# -*- coding: utf-8 -*- # # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2013-2014 Pawel Jastrzebski <pawelj@iosphe.re> # # Permission to use, copy, modify, and/or distribute this software for # any purpose with or without fee is hereby granted, provided that the # above copyright notice and this permission notice appear in all # copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE # AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL # DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA # OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. __version__ = '4.0.2' __license__ = 'ISC' __copyright__ = '2012-2014, Ciro Mattia Gonano <ciromattia@gmail.com>, Pawel Jastrzebski <pawelj@iosphe.re>' __docformat__ = 'restructuredtext en' import os import sys from urllib.parse import unquote from urllib.request import urlopen, urlretrieve from socket import gethostbyname_ex, gethostname from traceback import format_tb from time import sleep from shutil import move from http.server import BaseHTTPRequestHandler, HTTPServer from socketserver import ThreadingMixIn from subprocess import STDOUT, PIPE from PyQt5 import QtGui, QtCore, QtWidgets from xml.dom.minidom import parse from html.parser import HTMLParser from psutil import virtual_memory, Popen, Process from .shared import md5Checksum from . import comic2ebook from . import dualmetafix from . import KCC_rc_web if sys.platform.startswith('darwin'): from . import KCC_ui_osx as KCC_ui elif sys.platform.startswith('linux'): from . import KCC_ui_linux as KCC_ui else: from . import KCC_ui class Icons: def __init__(self): self.deviceKindle = QtGui.QIcon() self.deviceKindle.addPixmap(QtGui.QPixmap(":/Devices/icons/Kindle.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.deviceKobo = QtGui.QIcon() self.deviceKobo.addPixmap(QtGui.QPixmap(":/Devices/icons/Kobo.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.deviceOther = QtGui.QIcon() self.deviceOther.addPixmap(QtGui.QPixmap(":/Devices/icons/Other.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.MOBIFormat = QtGui.QIcon() self.MOBIFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/MOBI.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.CBZFormat = QtGui.QIcon() self.CBZFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/CBZ.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.EPUBFormat = QtGui.QIcon() self.EPUBFormat.addPixmap(QtGui.QPixmap(":/Formats/icons/EPUB.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.info = QtGui.QIcon() self.info.addPixmap(QtGui.QPixmap(":/Status/icons/info.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.warning = QtGui.QIcon() self.warning.addPixmap(QtGui.QPixmap(":/Status/icons/warning.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.error = QtGui.QIcon() self.error.addPixmap(QtGui.QPixmap(":/Status/icons/error.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.programIcon = QtGui.QIcon() self.programIcon.addPixmap(QtGui.QPixmap(":/Icon/icons/comic2ebook.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) class HTMLStripper(HTMLParser): def __init__(self): HTMLParser.__init__(self) self.reset() self.fed = [] def handle_data(self, d): self.fed.append(d) def get_data(self): return ''.join(self.fed) class WebServerHandler(BaseHTTPRequestHandler): # noinspection PyAttributeOutsideInit, PyArgumentList def do_GET(self): if self.path == '/': self.path = '/index.html' try: sendReply = False mimetype = None if self.path.endswith('.mobi'): mimetype = 'application/x-mobipocket-ebook' sendReply = True if self.path.endswith('.epub'): mimetype = 'application/epub+zip' sendReply = True if self.path.endswith('.cbz'): mimetype = 'application/x-cbz' sendReply = True if self.path == '/index.html': self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write(bytes('<!DOCTYPE html>\n' '<html lang="en">\n' '<head><meta charset="utf-8">\n' '<link href="' + GUI.webContent.favicon + '" rel="icon" type="image/x-icon" />\n' '<title>Kindle Comic Converter</title>\n' '</head>\n' '<body>\n' '<div style="text-align: center; font-size:25px">\n' '<p style="font-size:50px">- <img style="vertical-align: middle" ' 'alt="KCC Logo" src="' + GUI.webContent.logo + '" /> -</p>\n', 'UTF-8')) if len(GUI.completedWork) > 0 and not GUI.conversionAlive: for key in sorted(GUI.completedWork.keys()): self.wfile.write(bytes('<p><a href="' + key + '">' + key.split('.')[0] + '</a></p>\n', 'UTF-8')) else: self.wfile.write(bytes('<p style="font-weight: bold">No downloads are available.<br/>' 'Convert some files and refresh this page.</p>\n', 'UTF-8')) self.wfile.write(bytes('</div>\n' '</body>\n' '</html>\n', 'UTF-8')) elif sendReply: outputFile = GUI.completedWork[unquote(self.path[1:])] fp = open(outputFile, 'rb') self.send_response(200) self.send_header('Content-type', mimetype) self.send_header('Content-Length', os.path.getsize(outputFile)) self.end_headers() while True: chunk = fp.read(8192) if not chunk: fp.close() break self.wfile.write(chunk) return except (IOError, LookupError): self.send_error(404, 'File Not Found: %s' % self.path) class WebServerThreaded(ThreadingMixIn, HTTPServer): """Handle requests in a separate thread.""" class WebServerThread(QtCore.QThread): def __init__(self): QtCore.QThread.__init__(self) self.server = None self.running = False def __del__(self): self.wait() def run(self): try: # Sweet cross-platform one-liner to get LAN ip address lIP = [ip for ip in gethostbyname_ex(gethostname())[2] if not ip.startswith("127.")][:1][0] except Exception: # Sadly it can fail on some Linux configurations lIP = None try: self.server = WebServerThreaded(('', 4242), WebServerHandler) self.running = True if lIP: MW.addMessage.emit('<b><a href="http://' + str(lIP) + ':4242/">Content server</a></b> started.', 'info', False) else: MW.addMessage.emit('<b>Content server</b> started on port 4242.', 'info', False) while self.running: self.server.handle_request() except Exception: MW.addMessage.emit('<b>Content server</b> failed to start!', 'error', False) def stop(self): self.running = False class VersionThread(QtCore.QThread): def __init__(self): QtCore.QThread.__init__(self) self.newVersion = '' self.md5 = '' def __del__(self): self.wait() def run(self): try: sleep(1) XML = urlopen('http://kcc.iosphe.re/Version.php') XML = parse(XML) except Exception: return latestVersion = XML.childNodes[0].getElementsByTagName('latest')[0].childNodes[0].toxml() if tuple(map(int, (latestVersion.split(".")))) > tuple(map(int, (__version__.split(".")))): if sys.platform.startswith('win'): self.newVersion = latestVersion self.md5 = XML.childNodes[0].getElementsByTagName('WindowsMD5')[0].childNodes[0].toxml() MW.showDialog.emit('<b>New version released!</b> <a href="https://github.com/ciromattia/kcc/releases/">' 'See changelog.</a><<br/><br/>Installed version: ' + __version__ + '<br/>Current version: ' + latestVersion + '<br/><br/>Would you like to start automatic update?', 'question') else: MW.addMessage.emit('<a href="http://kcc.iosphe.re/">' '<b>New version is available!</b></a> ' '(<a href="https://github.com/ciromattia/kcc/releases/">' 'Changelog</a>)', 'warning', False) def getNewVersion(self, dialogAnswer): if dialogAnswer == QtWidgets.QMessageBox.Yes: try: MW.modeConvert.emit(-1) MW.progressBarTick.emit('Downloading update') path = urlretrieve('http://kcc.iosphe.re/Windows/KindleComicConverter_win_' + self.newVersion + '.exe', reporthook=self.getNewVersionTick) if self.md5 != md5Checksum(path[0]): raise Exception move(path[0], path[0] + '.exe') MW.hideProgressBar.emit() MW.modeConvert.emit(1) Popen(path[0] + '.exe /SP- /silent /noicons') MW.forceShutdown.emit() except Exception: MW.addMessage.emit('Failed to download update!', 'warning', False) MW.hideProgressBar.emit() MW.modeConvert.emit(1) def getNewVersionTick(self, size, blockSize, totalSize): if size == 0: MW.progressBarTick.emit(str(int(totalSize / blockSize))) MW.progressBarTick.emit('tick') class ProgressThread(QtCore.QThread): def __init__(self): QtCore.QThread.__init__(self) self.running = False self.content = None self.progress = 0 def __del__(self): self.wait() def run(self): self.running = True while self.running: sleep(1) if self.content and GUI.conversionAlive: MW.addMessage.emit(self.content + self.progress * '.', 'info', True) self.progress += 1 if self.progress == 4: self.progress = 0 def stop(self): self.running = False class WorkerSignals(QtCore.QObject): result = QtCore.pyqtSignal(list) class KindleGenThread(QtCore.QRunnable): def __init__(self, batch): super(KindleGenThread, self).__init__() self.signals = WorkerSignals() self.work = batch def run(self): kindlegenErrorCode = 0 kindlegenError = '' try: if os.path.getsize(self.work) < 629145600: output = Popen('kindlegen -dont_append_source -locale en "' + self.work + '"', stdout=PIPE, stderr=STDOUT, shell=True) for line in output.stdout: line = line.decode('utf-8') # ERROR: Generic error if "Error(" in line: kindlegenErrorCode = 1 kindlegenError = line # ERROR: EPUB too big if ":E23026:" in line: kindlegenErrorCode = 23026 if kindlegenErrorCode > 0: break else: # ERROR: EPUB too big kindlegenErrorCode = 23026 self.signals.result.emit([kindlegenErrorCode, kindlegenError, self.work]) except Exception as err: # ERROR: KCC unknown generic error kindlegenErrorCode = 1 kindlegenError = format(err) self.signals.result.emit([kindlegenErrorCode, kindlegenError, self.work]) class DualMetaFixThread(QtCore.QRunnable): def __init__(self, batch): super(DualMetaFixThread, self).__init__() self.signals = WorkerSignals() self.work = batch def run(self): item = self.work os.remove(item) mobiPath = item.replace('.epub', '.mobi') move(mobiPath, mobiPath + '_toclean') try: mobiedit = dualmetafix.DualMobiMetaFix(mobiPath + '_toclean') open(mobiPath, 'wb').write(mobiedit.getresult()) self.signals.result.emit([True]) except Exception as err: self.signals.result.emit([False, format(err)]) class WorkerThread(QtCore.QThread): #noinspection PyArgumentList def __init__(self): QtCore.QThread.__init__(self) self.pool = QtCore.QThreadPool() self.conversionAlive = False self.errors = False self.kindlegenErrorCode = [0] self.workerOutput = [] # Let's make sure that we don't fill the memory availableMemory = virtual_memory().total/1000000000 if availableMemory <= 2: self.threadNumber = 1 elif 2 < availableMemory <= 4: self.threadNumber = 2 else: self.threadNumber = 4 # Let's make sure that we don't use too many threads if self.threadNumber > QtCore.QThread.idealThreadCount(): self.threadNumber = QtCore.QThread.idealThreadCount() self.progressBarTick = MW.progressBarTick self.addMessage = MW.addMessage def __del__(self): self.wait() def sync(self): self.conversionAlive = GUI.conversionAlive def clean(self): GUI.progress.content = '' GUI.progress.stop() GUI.needClean = True MW.hideProgressBar.emit() MW.addMessage.emit('<b>Conversion interrupted.</b>', 'error', False) MW.addTrayMessage.emit('Conversion interrupted.', 'Critical') MW.modeConvert.emit(1) def addResult(self, output): MW.progressBarTick.emit('tick') self.workerOutput.append(output) def run(self): MW.modeConvert.emit(0) profile = GUI.profiles[str(GUI.DeviceBox.currentText())]['Label'] argv = ["--profile=" + profile] currentJobs = [] if GUI.MangaBox.isChecked(): argv.append("--manga-style") if GUI.RotateBox.isChecked(): argv.append("--rotate") if GUI.QualityBox.checkState() == 1: argv.append("--quality=1") elif GUI.QualityBox.checkState() == 2: argv.append("--quality=2") if str(GUI.FormatBox.currentText()) == 'CBZ': argv.append("--cbz-output") if GUI.currentMode == 1: if profile in ['KFHD', 'KFHD8', 'KFHDX', 'KFHDX8']: argv.append("--upscale") if GUI.currentMode > 1: if GUI.ProcessingBox.isChecked(): argv.append("--noprocessing") if GUI.NoRotateBox.isChecked(): argv.append("--nosplitrotate") if GUI.UpscaleBox.checkState() == 1: argv.append("--stretch") elif GUI.UpscaleBox.checkState() == 2: argv.append("--upscale") if GUI.BorderBox.checkState() == 1: argv.append("--whiteborders") elif GUI.BorderBox.checkState() == 2: argv.append("--blackborders") if GUI.NoDitheringBox.isChecked(): argv.append("--forcepng") if GUI.WebtoonBox.isChecked(): argv.append("--webtoon") if float(GUI.GammaValue) > 0.09: # noinspection PyTypeChecker argv.append("--gamma=" + GUI.GammaValue) if str(GUI.FormatBox.currentText()) == 'MOBI': argv.append("--batchsplit") if GUI.currentMode > 2: argv.append("--customwidth=" + str(GUI.customWidth.text())) argv.append("--customheight=" + str(GUI.customHeight.text())) if GUI.ColorBox.isChecked(): argv.append("--forcecolor") for i in range(GUI.JobList.count()): # Make sure that we don't consider any system message as job to do if GUI.JobList.item(i).icon().isNull(): currentJobs.append(str(GUI.JobList.item(i).text())) GUI.JobList.clear() for job in currentJobs: sleep(0.5) if not self.conversionAlive: self.clean() return self.errors = False MW.addMessage.emit('<b>Source:</b> ' + job, 'info', False) if str(GUI.FormatBox.currentText()) == 'CBZ': MW.addMessage.emit('Creating CBZ files', 'info', False) GUI.progress.content = 'Creating CBZ files' else: MW.addMessage.emit('Creating EPUB files', 'info', False) GUI.progress.content = 'Creating EPUB files' jobargv = list(argv) jobargv.append(job) try: outputPath = comic2ebook.main(jobargv, self) MW.hideProgressBar.emit() except UserWarning as warn: if not self.conversionAlive: self.clean() return else: GUI.progress.content = '' self.errors = True MW.addMessage.emit(str(warn), 'warning', False) MW.addMessage.emit('Failed to create output file!', 'error', False) MW.addTrayMessage.emit('Failed to create output file!', 'Critical') except Exception as err: GUI.progress.content = '' self.errors = True _, _, traceback = sys.exc_info() MW.showDialog.emit("Error during conversion %s:\n\n%s\n\nTraceback:\n%s" % (jobargv[-1], str(err), "".join(format_tb(traceback))), 'error') MW.addMessage.emit('Failed to create EPUB!', 'error', False) MW.addTrayMessage.emit('Failed to create EPUB!', 'Critical') if not self.conversionAlive: for item in outputPath: if os.path.exists(item): os.remove(item) self.clean() return if not self.errors: GUI.progress.content = '' if str(GUI.FormatBox.currentText()) == 'CBZ': MW.addMessage.emit('Creating CBZ files... <b>Done!</b>', 'info', True) else: MW.addMessage.emit('Creating EPUB files... <b>Done!</b>', 'info', True) if str(GUI.FormatBox.currentText()) == 'MOBI': MW.progressBarTick.emit('Creating MOBI files') MW.progressBarTick.emit(str(len(outputPath)*2+1)) MW.progressBarTick.emit('tick') MW.addMessage.emit('Creating MOBI files', 'info', False) GUI.progress.content = 'Creating MOBI files' self.workerOutput = [] # Number of KindleGen threads depends on the size of RAM self.pool.setMaxThreadCount(self.threadNumber) for item in outputPath: worker = KindleGenThread(item) worker.signals.result.connect(self.addResult) self.pool.start(worker) self.pool.waitForDone() sleep(0.5) self.kindlegenErrorCode = [0] for errors in self.workerOutput: if errors[0] != 0: self.kindlegenErrorCode = errors break if not self.conversionAlive: for item in outputPath: if os.path.exists(item): os.remove(item) sleep(1) if os.path.exists(item.replace('.epub', '.mobi')): os.remove(item.replace('.epub', '.mobi')) self.clean() return if self.kindlegenErrorCode[0] == 0: GUI.progress.content = '' MW.addMessage.emit('Creating MOBI files... <b>Done!</b>', 'info', True) MW.addMessage.emit('Processing MOBI files', 'info', False) GUI.progress.content = 'Processing MOBI files' self.workerOutput = [] # DualMetaFix is very fast and there is not reason to use multithreading. self.pool.setMaxThreadCount(1) for item in outputPath: worker = DualMetaFixThread(item) worker.signals.result.connect(self.addResult) self.pool.start(worker) self.pool.waitForDone() sleep(0.5) for success in self.workerOutput: if not success[0]: self.errors = True break if not self.errors: for item in outputPath: GUI.progress.content = '' mobiPath = item.replace('.epub', '.mobi') os.remove(mobiPath + '_toclean') if GUI.targetDirectory and GUI.targetDirectory != os.path.split(mobiPath)[0]: try: move(mobiPath, GUI.targetDirectory) mobiPath = os.path.join(GUI.targetDirectory, os.path.basename(mobiPath)) except Exception: pass GUI.completedWork[os.path.basename(mobiPath)] = mobiPath MW.addMessage.emit('Processing MOBI files... <b>Done!</b>', 'info', True) else: GUI.progress.content = '' for item in outputPath: mobiPath = item.replace('.epub', '.mobi') if os.path.exists(mobiPath): os.remove(mobiPath) if os.path.exists(mobiPath + '_toclean'): os.remove(mobiPath + '_toclean') MW.addMessage.emit('Failed to process MOBI file!', 'error', False) MW.addTrayMessage.emit('Failed to process MOBI file!', 'Critical') else: GUI.progress.content = '' epubSize = (os.path.getsize(self.kindlegenErrorCode[2]))//1024//1024 for item in outputPath: if os.path.exists(item): os.remove(item) sleep(1) if os.path.exists(item.replace('.epub', '.mobi')): os.remove(item.replace('.epub', '.mobi')) MW.addMessage.emit('KindleGen failed to create MOBI!', 'error', False) MW.addTrayMessage.emit('KindleGen failed to create MOBI!', 'Critical') if self.kindlegenErrorCode[0] == 1 and self.kindlegenErrorCode[1] != '': MW.showDialog.emit("KindleGen error:\n\n" + self.kindlegenErrorCode[1], 'error') if self.kindlegenErrorCode[0] == 23026: MW.addMessage.emit('Created EPUB file was too big.', 'error', False) MW.addMessage.emit('EPUB file: ' + str(epubSize) + 'MB. Supported size: ~350MB.', 'error', False) else: for item in outputPath: if GUI.targetDirectory and GUI.targetDirectory != os.path.split(item)[0]: try: move(item, GUI.targetDirectory) item = os.path.join(GUI.targetDirectory, os.path.basename(item)) except Exception: pass GUI.completedWork[os.path.basename(item)] = item GUI.progress.content = '' GUI.progress.stop() MW.hideProgressBar.emit() GUI.needClean = True MW.addMessage.emit('<b>All jobs completed.</b>', 'info', False) MW.addTrayMessage.emit('All jobs completed.', 'Information') MW.modeConvert.emit(1) class SystemTrayIcon(QtWidgets.QSystemTrayIcon): def __init__(self): if self.isSystemTrayAvailable(): QtWidgets.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW) self.activated.connect(self.catchClicks) def catchClicks(self): MW.showNormal() MW.raise_() MW.activateWindow() def addTrayMessage(self, message, icon): icon = eval('QtWidgets.QSystemTrayIcon.' + icon) if self.supportsMessages() and not MW.isActiveWindow(): self.showMessage('Kindle Comic Converter', message, icon) class KCCGUI(KCC_ui.Ui_KCC): def selectDir(self): if self.needClean: self.needClean = False GUI.JobList.clear() dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select directory', self.lastPath) if dname != '': if sys.platform.startswith('win'): dname = dname.replace('/', '\\') self.lastPath = os.path.abspath(os.path.join(dname, os.pardir)) GUI.JobList.addItem(dname) def selectFile(self): if self.needClean: self.needClean = False GUI.JobList.clear() if self.UnRAR: if self.sevenza: fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, 'Comic (*.cbz *.cbr *.cb7 *.zip *.rar *.7z *.pdf)') else: fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, 'Comic (*.cbz *.cbr *.zip *.rar *.pdf)') else: if self.sevenza: fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, 'Comic (*.cbz *.cb7 *.zip *.7z *.pdf)') else: fnames = QtWidgets.QFileDialog.getOpenFileNames(MW, 'Select file', self.lastPath, 'Comic (*.cbz *.zip *.pdf)') for fname in fnames[0]: if fname != '': if sys.platform.startswith('win'): fname = fname.replace('/', '\\') self.lastPath = os.path.abspath(os.path.join(fname, os.pardir)) GUI.JobList.addItem(fname) def clearJobs(self): GUI.JobList.clear() def modeBasic(self): self.currentMode = 1 if sys.platform.startswith('darwin'): MW.setMaximumSize(QtCore.QSize(420, 291)) MW.setMinimumSize(QtCore.QSize(420, 291)) MW.resize(420, 291) else: MW.setMaximumSize(QtCore.QSize(420, 287)) MW.setMinimumSize(QtCore.QSize(420, 287)) MW.resize(420, 287) GUI.BasicModeButton.setEnabled(True) GUI.AdvModeButton.setEnabled(True) GUI.BasicModeButton.setStyleSheet('font-weight:Bold;') GUI.AdvModeButton.setStyleSheet('font-weight:Normal;') GUI.FormatBox.setEnabled(False) GUI.OptionsBasic.setEnabled(True) GUI.OptionsBasic.setVisible(True) GUI.MangaBox.setChecked(False) GUI.RotateBox.setChecked(False) GUI.QualityBox.setChecked(False) GUI.OptionsAdvanced.setEnabled(False) GUI.OptionsAdvanced.setVisible(False) GUI.ProcessingBox.setChecked(False) GUI.UpscaleBox.setChecked(False) GUI.NoRotateBox.setChecked(False) GUI.BorderBox.setChecked(False) GUI.WebtoonBox.setChecked(False) GUI.NoDitheringBox.setChecked(False) GUI.OptionsAdvancedGamma.setEnabled(False) GUI.OptionsAdvancedGamma.setVisible(False) GUI.OptionsExpert.setEnabled(False) GUI.OptionsExpert.setVisible(False) GUI.ColorBox.setChecked(False) def modeAdvanced(self): self.currentMode = 2 MW.setMaximumSize(QtCore.QSize(420, 365)) MW.setMinimumSize(QtCore.QSize(420, 365)) MW.resize(420, 365) GUI.BasicModeButton.setEnabled(True) GUI.AdvModeButton.setEnabled(True) GUI.BasicModeButton.setStyleSheet('font-weight:Normal;') GUI.AdvModeButton.setStyleSheet('font-weight:Bold;') GUI.FormatBox.setEnabled(True) GUI.OptionsBasic.setEnabled(True) GUI.OptionsBasic.setVisible(True) GUI.MangaBox.setChecked(False) GUI.RotateBox.setChecked(False) GUI.QualityBox.setChecked(False) GUI.OptionsAdvanced.setEnabled(True) GUI.OptionsAdvanced.setVisible(True) GUI.ProcessingBox.setChecked(False) GUI.UpscaleBox.setChecked(False) GUI.NoRotateBox.setChecked(False) GUI.BorderBox.setChecked(False) GUI.WebtoonBox.setChecked(False) GUI.NoDitheringBox.setChecked(False) GUI.OptionsAdvancedGamma.setEnabled(True) GUI.OptionsAdvancedGamma.setVisible(True) GUI.OptionsExpert.setEnabled(True) GUI.OptionsExpert.setVisible(True) GUI.ColorBox.setChecked(False) def modeExpert(self): self.currentMode = 3 MW.setMaximumSize(QtCore.QSize(420, 397)) MW.setMinimumSize(QtCore.QSize(420, 397)) MW.resize(420, 397) GUI.BasicModeButton.setEnabled(False) GUI.AdvModeButton.setEnabled(False) GUI.BasicModeButton.setStyleSheet('font-weight:Normal;') GUI.AdvModeButton.setStyleSheet('font-weight:Normal;') GUI.FormatBox.setEnabled(True) GUI.OptionsBasic.setEnabled(True) GUI.OptionsBasic.setVisible(True) GUI.MangaBox.setChecked(False) GUI.RotateBox.setChecked(False) GUI.QualityBox.setChecked(False) GUI.OptionsAdvanced.setEnabled(True) GUI.OptionsAdvanced.setVisible(True) GUI.ProcessingBox.setChecked(False) GUI.UpscaleBox.setChecked(False) GUI.NoRotateBox.setChecked(False) GUI.BorderBox.setChecked(False) GUI.WebtoonBox.setChecked(False) GUI.NoDitheringBox.setChecked(False) GUI.OptionsAdvancedGamma.setEnabled(True) GUI.OptionsAdvancedGamma.setVisible(True) GUI.OptionsExpert.setEnabled(True) GUI.OptionsExpert.setVisible(True) GUI.ColorBox.setChecked(False) def modeConvert(self, enable): if enable < 1: status = False else: status = True if self.currentMode != 3: GUI.BasicModeButton.setEnabled(status) GUI.AdvModeButton.setEnabled(status) if self.currentMode != 1: GUI.FormatBox.setEnabled(status) GUI.DirectoryButton.setEnabled(status) GUI.ClearButton.setEnabled(status) GUI.FileButton.setEnabled(status) GUI.DeviceBox.setEnabled(status) GUI.OptionsBasic.setEnabled(status) GUI.OptionsAdvanced.setEnabled(status) GUI.OptionsAdvancedGamma.setEnabled(status) GUI.OptionsExpert.setEnabled(status) GUI.ConvertButton.setEnabled(True) if enable == 1: self.conversionAlive = False self.worker.sync() icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(":/Other/icons/convert.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) GUI.ConvertButton.setIcon(icon) GUI.ConvertButton.setText('Convert') GUI.Form.setAcceptDrops(True) elif enable == 0: self.conversionAlive = True self.worker.sync() icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(":/Other/icons/clear.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) GUI.ConvertButton.setIcon(icon) GUI.ConvertButton.setText('Abort') GUI.Form.setAcceptDrops(False) elif enable == -1: self.conversionAlive = True self.worker.sync() GUI.ConvertButton.setEnabled(False) GUI.Form.setAcceptDrops(False) def toggleWebtoonBox(self, value): if value: GUI.NoRotateBox.setEnabled(False) GUI.NoRotateBox.setChecked(True) GUI.QualityBox.setEnabled(False) GUI.QualityBox.setChecked(False) GUI.MangaBox.setEnabled(False) GUI.MangaBox.setChecked(False) else: if not GUI.ProcessingBox.isChecked(): GUI.NoRotateBox.setEnabled(True) GUI.QualityBox.setEnabled(True) GUI.MangaBox.setEnabled(True) def toggleNoSplitRotate(self, value): if value: GUI.RotateBox.setEnabled(False) GUI.RotateBox.setChecked(False) else: if not GUI.ProcessingBox.isChecked(): GUI.RotateBox.setEnabled(True) def toggleProcessingBox(self, value): if value: GUI.RotateBox.setEnabled(False) GUI.RotateBox.setChecked(False) GUI.QualityBox.setEnabled(False) GUI.QualityBox.setChecked(False) GUI.UpscaleBox.setEnabled(False) GUI.UpscaleBox.setChecked(False) GUI.NoRotateBox.setEnabled(False) GUI.NoRotateBox.setChecked(False) GUI.BorderBox.setEnabled(False) GUI.BorderBox.setChecked(False) GUI.WebtoonBox.setEnabled(False) GUI.WebtoonBox.setChecked(False) GUI.NoDitheringBox.setEnabled(False) GUI.NoDitheringBox.setChecked(False) GUI.ColorBox.setEnabled(False) GUI.ColorBox.setChecked(False) GUI.GammaSlider.setEnabled(False) GUI.GammaLabel.setEnabled(False) else: GUI.RotateBox.setEnabled(True) GUI.UpscaleBox.setEnabled(True) GUI.NoRotateBox.setEnabled(True) GUI.BorderBox.setEnabled(True) GUI.WebtoonBox.setEnabled(True) GUI.NoDitheringBox.setEnabled(True) GUI.ColorBox.setEnabled(True) GUI.GammaSlider.setEnabled(True) GUI.GammaLabel.setEnabled(True) if GUI.profiles[str(GUI.DeviceBox.currentText())]['Quality']: GUI.QualityBox.setEnabled(True) def toggleQualityBox(self, value): if value == 2 and 'Kobo' in str(GUI.DeviceBox.currentText()): self.addMessage('Kobo devices can\'t use ultra quality mode!', 'warning') GUI.QualityBox.setCheckState(0) def changeGamma(self, value): value = float(value) value = '%.2f' % (value/100) if float(value) <= 0.09: GUI.GammaLabel.setText('Gamma: Auto') else: GUI.GammaLabel.setText('Gamma: ' + str(value)) self.GammaValue = value def changeDevice(self): if self.currentMode == 1: self.modeBasic() elif self.currentMode == 2: self.modeAdvanced() elif self.currentMode == 3: self.modeExpert() profile = GUI.profiles[str(GUI.DeviceBox.currentText())] if profile['ForceExpert']: self.modeExpert() GUI.BasicModeButton.setEnabled(False) GUI.AdvModeButton.setEnabled(False) else: GUI.BasicModeButton.setEnabled(True) GUI.AdvModeButton.setEnabled(True) if self.currentMode == 3: self.modeBasic() self.changeFormat() GUI.GammaSlider.setValue(0) self.changeGamma(0) if profile['DefaultUpscale']: GUI.UpscaleBox.setChecked(True) if str(GUI.DeviceBox.currentText()) == 'Other': self.addMessage('<a href="https://github.com/ciromattia/kcc/wiki/NonKindle-devices">' 'List of supported Non-Kindle devices.</a>', 'info') def changeFormat(self, outputFormat=None): profile = GUI.profiles[str(GUI.DeviceBox.currentText())] if outputFormat is not None: GUI.FormatBox.setCurrentIndex(outputFormat) else: if GUI.FormatBox.count() == 3: GUI.FormatBox.setCurrentIndex(profile['DefaultFormat']) else: if profile['DefaultFormat'] != 0: tmpFormat = profile['DefaultFormat'] - 1 else: tmpFormat = 0 GUI.FormatBox.setCurrentIndex(tmpFormat) if GUI.WebtoonBox.isChecked(): GUI.MangaBox.setEnabled(False) GUI.QualityBox.setEnabled(False) GUI.MangaBox.setChecked(False) GUI.QualityBox.setChecked(False) else: GUI.QualityBox.setEnabled(profile['Quality']) if not profile['Quality']: GUI.QualityBox.setChecked(False) if GUI.ProcessingBox.isChecked(): GUI.QualityBox.setEnabled(False) GUI.QualityBox.setChecked(False) def stripTags(self, html): s = HTMLStripper() s.feed(html) return s.get_data() def addMessage(self, message, icon, replace=False): if icon != '': icon = eval('self.icons.' + icon) item = QtWidgets.QListWidgetItem(icon, ' ' + self.stripTags(message)) else: item = QtWidgets.QListWidgetItem(' ' + self.stripTags(message)) if replace: GUI.JobList.takeItem(GUI.JobList.count()-1) # Due to lack of HTML support in QListWidgetItem we overlay text field with QLabel # We still fill original text field with transparent content to trigger creation of horizontal scrollbar item.setForeground(QtGui.QColor('transparent')) label = QtWidgets.QLabel(message) label.setStyleSheet('background-image:url('');background-color:rgba(0,0,0,0);') label.setOpenExternalLinks(True) font = QtGui.QFont() font.setPointSize(self.listFontSize) label.setFont(font) GUI.JobList.addItem(item) GUI.JobList.setItemWidget(item, label) GUI.JobList.scrollToBottom() def showDialog(self, message, kind): if kind == 'error': QtWidgets.QMessageBox.critical(MW, 'KCC - Error', message, QtWidgets.QMessageBox.Ok) elif kind == 'question': dialogResponse = QtWidgets.QMessageBox.question(MW, 'KCC - Question', message, QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) MW.dialogAnswer.emit(dialogResponse) def updateProgressbar(self, command): if command == 'tick': GUI.ProgressBar.setValue(GUI.ProgressBar.value() + 1) elif command.isdigit(): GUI.ProgressBar.setMaximum(int(command) - 1) GUI.BasicModeButton.hide() GUI.AdvModeButton.hide() GUI.ProgressBar.reset() GUI.ProgressBar.show() else: GUI.ProgressBar.setFormat(command) def convertStart(self): if self.conversionAlive: GUI.ConvertButton.setEnabled(False) self.addMessage('Process will be interrupted. Please wait.', 'warning') self.conversionAlive = False self.worker.sync() else: # noinspection PyArgumentList if QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier: dname = QtWidgets.QFileDialog.getExistingDirectory(MW, 'Select output directory', self.lastPath) if dname != '': if sys.platform.startswith('win'): dname = dname.replace('/', '\\') GUI.targetDirectory = dname else: GUI.targetDirectory = '' else: GUI.targetDirectory = '' self.progress.start() if self.needClean: self.needClean = False GUI.JobList.clear() if GUI.JobList.count() == 0: self.addMessage('No files selected! Please choose files to convert.', 'error') self.needClean = True return if self.currentMode > 2 and (str(GUI.customWidth.text()) == '' or str(GUI.customHeight.text()) == ''): GUI.JobList.clear() self.addMessage('Target resolution is not set!', 'error') self.needClean = True return self.worker.start() def hideProgressBar(self): GUI.ProgressBar.hide() GUI.BasicModeButton.show() GUI.AdvModeButton.show() def saveSettings(self, event): if self.conversionAlive: GUI.ConvertButton.setEnabled(False) self.addMessage('Process will be interrupted. Please wait.', 'warning') self.conversionAlive = False self.worker.sync() event.ignore() if not GUI.ConvertButton.isEnabled(): event.ignore() self.contentServer.stop() self.settings.setValue('settingsVersion', __version__) self.settings.setValue('lastPath', self.lastPath) self.settings.setValue('lastDevice', GUI.DeviceBox.currentIndex()) self.settings.setValue('currentFormat', GUI.FormatBox.currentIndex()) self.settings.setValue('currentMode', self.currentMode) self.settings.setValue('startNumber', self.startNumber + 1) self.settings.setValue('options', {'MangaBox': GUI.MangaBox.checkState(), 'RotateBox': GUI.RotateBox.checkState(), 'QualityBox': GUI.QualityBox.checkState(), 'ProcessingBox': GUI.ProcessingBox.checkState(), 'UpscaleBox': GUI.UpscaleBox.checkState(), 'NoRotateBox': GUI.NoRotateBox.checkState(), 'BorderBox': GUI.BorderBox.checkState(), 'WebtoonBox': GUI.WebtoonBox.checkState(), 'NoDitheringBox': GUI.NoDitheringBox.checkState(), 'ColorBox': GUI.ColorBox.checkState(), 'customWidth': GUI.customWidth.text(), 'customHeight': GUI.customHeight.text(), 'GammaSlider': float(self.GammaValue)*100}) self.settings.sync() self.tray.hide() APP.shutdown() def handleMessage(self, message): MW.raise_() MW.activateWindow() if type(message) is bytes: message = message.decode('UTF-8') if not self.conversionAlive and message != 'ARISE': if self.needClean: self.needClean = False GUI.JobList.clear() if self.UnRAR: if self.sevenza: formats = ['.cbz', '.cbr', '.cb7', '.zip', '.rar', '.7z', '.pdf'] else: formats = ['.cbz', '.cbr', '.zip', '.rar', '.pdf'] else: if self.sevenza: formats = ['.cbz', '.cb7', '.zip', '.7z', '.pdf'] else: formats = ['.cbz', '.zip', '.pdf'] if os.path.isdir(message): GUI.JobList.addItem(message) elif os.path.isfile(message): extension = os.path.splitext(message) if extension[1].lower() in formats: GUI.JobList.addItem(message) else: self.addMessage('This file type is unsupported!', 'error') def dragAndDrop(self, e): e.accept() def dragAndDropAccepted(self, e): for message in e.mimeData().urls(): message = unquote(message.toString().replace('file:///', '')) if sys.platform.startswith('win'): message = message.replace('/', '\\') else: message = '/' + message if message[-1] == '/': message = message[:-1] self.handleMessage(message) def forceShutdown(self): self.saveSettings(None) sys.exit(0) # noinspection PyArgumentList def __init__(self, KCCAplication, KCCWindow): global APP, MW, GUI APP = KCCAplication MW = KCCWindow GUI = self self.setupUi(MW) # User settings will be reverted to default ones if were created in one of the following versions # Empty string cover all versions before this system was implemented purgeSettingsVersions = [''] self.icons = Icons() self.webContent = KCC_rc_web.WebContent() self.settings = QtCore.QSettings('KindleComicConverter', 'KindleComicConverter') self.settingsVersion = self.settings.value('settingsVersion', '', type=str) if self.settingsVersion in purgeSettingsVersions: QtCore.QSettings.clear(self.settings) self.settingsVersion = self.settings.value('settingsVersion', '', type=str) self.lastPath = self.settings.value('lastPath', '', type=str) self.lastDevice = self.settings.value('lastDevice', 0, type=int) self.currentMode = self.settings.value('currentMode', 1, type=int) self.currentFormat = self.settings.value('currentFormat', 0, type=int) self.startNumber = self.settings.value('startNumber', 0, type=int) self.options = self.settings.value('options', {'GammaSlider': 0}) self.worker = WorkerThread() self.versionCheck = VersionThread() self.contentServer = WebServerThread() self.progress = ProgressThread() self.tray = SystemTrayIcon() self.conversionAlive = False self.needClean = True self.GammaValue = 1.0 self.completedWork = {} self.targetDirectory = '' if sys.platform.startswith('darwin'): self.listFontSize = 11 self.statusBarFontSize = 10 self.statusBarStyle = 'QLabel{padding-top:2px;padding-bottom:3px;}' self.ProgressBar.setStyleSheet('QProgressBar{padding-top:5px;text-align:center;}') elif sys.platform.startswith('linux'): self.listFontSize = 8 self.statusBarFontSize = 8 self.statusBarStyle = 'QLabel{padding-top:5px;padding-bottom:3px;}' self.statusBar.setStyleSheet('QStatusBar::item{border:0px;border-top:2px solid #C2C7CB;}') else: self.listFontSize = 9 self.statusBarFontSize = 8 self.statusBarStyle = 'QLabel{padding-top:3px;padding-bottom:3px}' self.statusBar.setStyleSheet('QStatusBar::item{border:0px;border-top:2px solid #C2C7CB;}') # Decrease priority to increase system responsiveness during conversion from psutil import BELOW_NORMAL_PRIORITY_CLASS self.p = Process(os.getpid()) self.p.nice(BELOW_NORMAL_PRIORITY_CLASS) self.p.ionice(1) self.profiles = { "Kindle Paperwhite": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'KHD'}, "Kindle": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'K345'}, "Kindle DX/DXG": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'KDX'}, "Kindle Fire": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'KF'}, "K. Fire HD 7\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'KFHD'}, "K. Fire HD 8.9\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'KFHD8'}, "K. Fire HDX 7\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'KFHDX'}, "K. Fire HDX 8.9\"": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': True, 'Label': 'KFHDX8'}, "Kobo Mini/Touch": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, 'DefaultUpscale': False, 'Label': 'KoMT'}, "Kobo Glow": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, 'DefaultUpscale': False, 'Label': 'KoG'}, "Kobo Aura": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, 'DefaultUpscale': False, 'Label': 'KoA'}, "Kobo Aura HD": {'Quality': True, 'ForceExpert': False, 'DefaultFormat': 2, 'DefaultUpscale': False, 'Label': 'KoAHD'}, "Other": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 1, 'DefaultUpscale': False, 'Label': 'OTHER'}, "Kindle for Android": {'Quality': False, 'ForceExpert': True, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'KFA'}, "Kindle 1": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'K1'}, "Kindle 2": {'Quality': False, 'ForceExpert': False, 'DefaultFormat': 0, 'DefaultUpscale': False, 'Label': 'K2'} } profilesGUI = [ "Kindle Paperwhite", "Kindle", "Kindle DX/DXG", "Separator", "Kindle Fire", "K. Fire HD 7\"", "K. Fire HD 8.9\"", "K. Fire HDX 7\"", "K. Fire HDX 8.9\"", "Separator", "Kobo Mini/Touch", "Kobo Glow", "Kobo Aura", "Kobo Aura HD", "Separator", "Other", "Separator", "Kindle for Android", "Kindle 1", "Kindle 2", ] statusBarLabel = QtWidgets.QLabel('<b><a href="http://kcc.iosphe.re/">HOMEPAGE</a> - <a href="https://github.' 'com/ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DO' 'NATE</a> - <a href="https://github.com/ciromattia/kcc/wiki">WIKI</a> - <a hr' 'ef="http://www.mobileread.com/forums/showthread.php?t=207461">FORUM</a></b>') statusBarLabel.setAlignment(QtCore.Qt.AlignCenter) statusBarLabel.setStyleSheet(self.statusBarStyle) statusBarLabel.setOpenExternalLinks(True) statusBarLabelFont = QtGui.QFont() statusBarLabelFont.setPointSize(self.statusBarFontSize) statusBarLabel.setFont(statusBarLabelFont) GUI.statusBar.addPermanentWidget(statusBarLabel, 1) self.addMessage('<b>Welcome!</b>', 'info') self.addMessage('<b>Remember:</b> All options have additional informations in tooltips.', 'info') if self.startNumber < 5: self.addMessage('Since you are new user of <b>KCC</b> please see few ' '<a href="https://github.com/ciromattia/kcc/wiki/Important-tips">important tips</a>.', 'info') kindleGenExitCode = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True) if kindleGenExitCode.wait() == 0: self.KindleGen = True formats = ['MOBI', 'EPUB', 'CBZ'] versionCheck = Popen('kindlegen -locale en', stdout=PIPE, stderr=STDOUT, shell=True) for line in versionCheck.stdout: line = line.decode("utf-8") if 'Amazon kindlegen' in line: versionCheck = line.split('V')[1].split(' ')[0] if tuple(map(int, (versionCheck.split(".")))) < tuple(map(int, ('2.9'.split(".")))): self.addMessage('Your <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=' '1000765211">kindlegen</a> is outdated! Creating MOBI might fail.' ' Please update <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=' '1000765211">kindlegen</a> from Amazon\'s website.', 'warning') break else: self.KindleGen = False formats = ['EPUB', 'CBZ'] if sys.platform.startswith('win'): self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">' 'kindlegen</a> in KCC directory! MOBI creation will be disabled.', 'warning') else: self.addMessage('Cannot find <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000765211">' 'kindlegen</a> in PATH! MOBI creation will be disabled.', 'warning') rarExitCode = Popen('unrar', stdout=PIPE, stderr=STDOUT, shell=True) rarExitCode = rarExitCode.wait() if rarExitCode == 0 or rarExitCode == 7: self.UnRAR = True else: self.UnRAR = False self.addMessage('Cannot find <a href="http://www.rarlab.com/rar_add.htm">UnRAR</a>!' ' Processing of CBR/RAR files will be disabled.', 'warning') sevenzaExitCode = Popen('7za', stdout=PIPE, stderr=STDOUT, shell=True) sevenzaExitCode = sevenzaExitCode.wait() if sevenzaExitCode == 0 or sevenzaExitCode == 7: self.sevenza = True else: self.sevenza = False self.addMessage('Cannot find <a href="http://www.7-zip.org/download.html">7za</a>!' ' Processing of CB7/7Z files will be disabled.', 'warning') APP.messageFromOtherInstance.connect(self.handleMessage) GUI.BasicModeButton.clicked.connect(self.modeBasic) GUI.AdvModeButton.clicked.connect(self.modeAdvanced) GUI.DirectoryButton.clicked.connect(self.selectDir) GUI.ClearButton.clicked.connect(self.clearJobs) GUI.FileButton.clicked.connect(self.selectFile) GUI.ConvertButton.clicked.connect(self.convertStart) GUI.GammaSlider.valueChanged.connect(self.changeGamma) GUI.NoRotateBox.stateChanged.connect(self.toggleNoSplitRotate) GUI.WebtoonBox.stateChanged.connect(self.toggleWebtoonBox) GUI.ProcessingBox.stateChanged.connect(self.toggleProcessingBox) GUI.QualityBox.stateChanged.connect(self.toggleQualityBox) GUI.DeviceBox.activated.connect(self.changeDevice) GUI.FormatBox.activated.connect(self.changeFormat) MW.progressBarTick.connect(self.updateProgressbar) MW.modeConvert.connect(self.modeConvert) MW.addMessage.connect(self.addMessage) MW.showDialog.connect(self.showDialog) MW.hideProgressBar.connect(self.hideProgressBar) MW.forceShutdown.connect(self.forceShutdown) MW.dialogAnswer.connect(self.versionCheck.getNewVersion) MW.closeEvent = self.saveSettings MW.addTrayMessage.connect(self.tray.addTrayMessage) GUI.Form.setAcceptDrops(True) GUI.Form.dragEnterEvent = self.dragAndDrop GUI.Form.dropEvent = self.dragAndDropAccepted for profile in profilesGUI: if profile == "Other": GUI.DeviceBox.addItem(self.icons.deviceOther, profile) elif profile == "Separator": GUI.DeviceBox.insertSeparator(GUI.DeviceBox.count()+1) elif 'Ko' in profile: GUI.DeviceBox.addItem(self.icons.deviceKobo, profile) else: GUI.DeviceBox.addItem(self.icons.deviceKindle, profile) for f in formats: GUI.FormatBox.addItem(eval('self.icons.' + f + 'Format'), f) if self.lastDevice > GUI.DeviceBox.count(): self.lastDevice = 0 if profilesGUI[self.lastDevice] == "Separator": self.lastDevice = 0 if self.currentFormat > GUI.FormatBox.count(): self.currentFormat = 0 GUI.DeviceBox.setCurrentIndex(self.lastDevice) self.changeDevice() if self.currentFormat != self.profiles[str(GUI.DeviceBox.currentText())]['DefaultFormat']: self.changeFormat(self.currentFormat) for option in self.options: if str(option) == "customWidth": GUI.customWidth.setText(str(self.options[option])) elif str(option) == "customHeight": GUI.customHeight.setText(str(self.options[option])) elif str(option) == "GammaSlider": if GUI.GammaSlider.isEnabled(): GUI.GammaSlider.setValue(int(self.options[option])) self.changeGamma(int(self.options[option])) else: if eval('GUI.' + str(option)).isEnabled(): eval('GUI.' + str(option)).setCheckState(self.options[option]) self.hideProgressBar() self.worker.sync() self.versionCheck.start() self.contentServer.start() self.tray.show() MW.setWindowTitle("Kindle Comic Converter " + __version__) MW.show() MW.raise_()