def on_pushButton_Add_clicked(self): logging.debug('on_pushButton_Add_clicked') book = Audiobook.getInstance() self.mask = MaskWidget(self.mainWindow) self.mask.show() self.afw = AttachFromWidgetWithoutURL( False, self._translate("ReadingOrderWidget", 'Open file'), (ReadingOrderWidget.str_LastOpenedDirectory, book.getBookDir() + '/')[ReadingOrderWidget.str_LastOpenedDirectory is None], "Audio files (*.mp3);; Wav files (*.wav)") self.afw.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.afw.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.afw.geometry().height() / 2) result = self.afw.exec_() filenames = self.afw.getURLs() self.mask.close() if result == QDialog.Accepted and filenames: self.getAttachFromWidgetResult(False, filenames) else: return
def _validate(self): logging.debug("_validate") try: self._save(True) except Exception as ex: logging.debug('omit validation process') return manifestForTest = self.book.getManifestDict() logging.debug(manifestForTest) self.mask = MaskWidget(self) self.mask.show() with open( os.path.join(os.path.dirname(__file__), 'audiobooks.schema.json')) as f: schemaData = f.read() schema = json.loads(schemaData) self.validator = Validator( self._translate('MainWindow', 'Please wait...\n'), self._translate('MainWindow', 'Validation begins:'), schema, manifestForTest) self.validator.move( self.geometry().x() + self.geometry().width() / 2 - self.validator.geometry().width() / 2, self.geometry().y() + self.geometry().height() / 2 - self.validator.geometry().height() / 2) result = self.validator.exec_() self.mask.close()
def on_pushButton_Add_clicked(self): logging.debug('on_pushButton_Add_clicked') if self._isEmpty: self.ui.label_NoItem.setVisible(False) self.ui.listWidget.setEnabled(True) self._isEmpty = False book = Audiobook.getInstance() self.mask = MaskWidget(self.mainWindow) self.mask.show() self.afw = AttachFromWidgetWithoutURL(False, 'Open files', ( SupplementalListWidgetWithWidgets.str_LastOpenedDirectory, book.getBookDir() + '/' )[SupplementalListWidgetWithWidgets.str_LastOpenedDirectory is None], "Any file(*.*)") self.afw.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.afw.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.afw.geometry().height() / 2) result = self.afw.exec_() filenames = self.afw.getURLs() self.mask.close() if result == QDialog.Accepted and filenames: self.getAttachFromWidgetResult(False, filenames) else: return
def _button_Create_clicked(self): saveDir = self.ui.lineEdit_BookPath.text() bookTitle = self.ui.lineEdit_BookTitle.text() author = self.ui.lineEdit_Author.text() publisher = self.ui.lineEdit_Publisher.text() readBy = self.ui.lineEdit_ReadBy.text() if saveDir and bookTitle: logging.debug(saveDir + ":" + bookTitle) self.switch_window.emit({"saveDir": saveDir, "bookTitle": bookTitle, "author": {"type": "Person", "name": author}, "publisher": publisher, "readBy": {"type": "Person", "name": readBy}}) else: self.mask = MaskWidget(self) self.mask.show() self.alert = Alert(self._translate("CreateNewWizard", "You must select one directory and" " keyin the booktitle!"), self._translate("CreateNewWizard", "Warning!")) self.alert.move( self.geometry().x() + self.geometry().width() / 2 - self.alert.geometry().width() / 2, self.geometry().y() + self.geometry().height() / 2 - self.alert.geometry().height() / 2) result = self.alert.exec_() self.mask.close()
def _openAlertWindow(self, str_Msg, str_Title): self.mask = MaskWidget(self) self.mask.show() self.alert = Alert(str_Msg, str_Title) self.alert.move( self.geometry().x() + self.geometry().width() / 2 - self.alert.geometry().width() / 2, self.geometry().y() + self.geometry().height() / 2 - self.alert.geometry().height() / 2) result = self.alert.exec_() self.mask.close() return result
def _openAlertWithButtonsWindow(self, str_Msg, str_Title): self.mask = MaskWidget(self.mainWindow) self.mask.show() self.alertwithbuttons = AlertWithButtons(str_Msg, str_Title) self.alertwithbuttons.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.alertwithbuttons.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.alertwithbuttons.geometry().height() / 2) result = self.alertwithbuttons.exec_() self.mask.close() return result
def _getOpenFilename(self, bool_Flag, str_Title, str_Path, str_FileTypes): logging.debug('_getOpenFilename') self.mask = MaskWidget(self.mainWindow) self.mask.show() self.afw = AttachFromWidgetWithoutURL(bool_Flag, str_Title, str_Path, str_FileTypes) self.afw.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.afw.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.afw.geometry().height() / 2) result = self.afw.exec_() filename = self.afw.getURL() self.mask.close() return result, filename
class MainWindow(QMainWindow): switch_window = Signal() signal_exit = Signal() signal_close = Signal() signal_new = Signal() signal_open = Signal() MaxRecentFiles = 10 @dispatch(Audiobook) def __init__(self, book): super(MainWindow, self).__init__() self.book = book self.ui = Ui_MainWindow() self.ui.setupUi(self) self._translate = QCoreApplication.translate # MenuBar CSS self.ui.menubar.setStyleSheet( 'QMenuBar::item { color: #FFFFFF } QMenuBar::item::selected { background: #383838; color: #FFFFFF } ' 'QMenu::item { color: #FFFFFF } QMenu::item::selected { background: #383838; color: #FFFFFF }' ) # MainWindow Title self.setWindowTitle(self._translate("MainWindow", "Audiobook Editor")) # Set Window Icon # self.setWindowIcon(QIcon(':/pic/icon/audiobook-editor-logo.ico')) # Hide Minimize & Maximize buttons # self.setWindowFlags(self.windowFlags() & ~Qt.WindowMaximizeButtonHint) # WindowMinMaxButtonsHint) # Magic to make title bar disappear self.ui.dockWidget_Left.setTitleBarWidget(QWidget(None)) self.ui.dockWidget_CoverPreviewWidget.setTitleBarWidget(QWidget(None)) self.ui.dockWidget_SupplementalList.setTitleBarWidget(QWidget(None)) layout = QVBoxLayout(self.ui.tab_Metadata) layout.setContentsMargins(0, 0, 0, 0) layout.setAlignment(Qt.AlignTop | Qt.AlignLeft) self.metadataWidget = MetadataWidget(self, self.book.getManifestDict()) layout.addWidget(self.metadataWidget) layout = QVBoxLayout(self.ui.dockWidgetContents) layout.setContentsMargins(0, 0, 0, 0) self.coverPreviewWidget = CoverPreviewWidget(self.book.getCoverDict(), self.book.getBookDir(), self) layout.addWidget(self.coverPreviewWidget, 1) w = QWidget() layout = QHBoxLayout(w) layout.setContentsMargins(0, 0, 0, 0) self.readingOrderWidget = ReadingOrderWidget( self.book.getReadingOrderList(), self.book.getBookDir(), self) self.readingOrderWidget.signal_Duration_Changed.connect( self.metadataWidget.onDurationChanged) layout.addWidget(self.readingOrderWidget) self.setCentralWidget(w) self.readingOrderWidget.getDuration() layout = QVBoxLayout(self.ui.tab_TOC) layout.setContentsMargins(0, 0, 0, 0) self.tocWidget = TOCListWidget(self, self.book.getTOCList()) layout.addWidget(self.tocWidget) layout = QVBoxLayout(self.ui.dockWidgetContents_2) layout.setContentsMargins(0, 0, 0, 0) self.supplementalListWidget = SupplementalListWidgetWithWidgets( self.book.getSupplementalList(), self) layout.addWidget(self.supplementalListWidget, 1) self.supplementalListWidget.signal_Add_Resource_to_TOC.connect( self.tocWidget.add_Resource_to_TOC_triggered) self.readingOrderWidget.signal_Add_Resource_to_TOC.connect( self.tocWidget.add_Resource_to_TOC_triggered) self.ui.action_New.triggered.connect(self._new) self.ui.action_Open.triggered.connect(self._open) self.ui.action_Exit.triggered.connect(self._exit) self.ui.action_Save.triggered.connect(self._save) self.ui.action_Close.triggered.connect(self._close) self.ui.action_Validate.triggered.connect(self._validate) self.ui.action_Pack.triggered.connect(self._pack) logging.debug(self.supplementalListWidget.ui.listWidget.objectName()) logging.debug(self.supplementalListWidget.ui.listWidget.geometry()) logging.debug( self.supplementalListWidget.ui.listWidget.parent().objectName()) logging.debug( self.supplementalListWidget.ui.listWidget.parent().geometry()) logging.debug(self.supplementalListWidget.objectName()) logging.debug(self.supplementalListWidget.geometry()) logging.debug(self.supplementalListWidget.parent().objectName()) logging.debug(self.supplementalListWidget.parent().geometry()) logging.debug( self.supplementalListWidget.parent().parent().objectName()) logging.debug(self.supplementalListWidget.parent().parent().geometry()) self.settings = QSettings() self.recentFilesOrDirectoriesInSettings = '' self.ui.action_EN.triggered.connect( lambda: self.changeUILanguage('EN', self.ui.action_EN)) self.ui.action_TC.triggered.connect( lambda: self.changeUILanguage('TC', self.ui.action_TC)) app = QApplication.instance() for action in self.ui.menu_Language.actions(): if action.objectName() == 'action_' + app.ui_Language: action.setChecked(True) else: action.setChecked(False) self.updateSettings() @dispatch(dict) def __init__(self, dict_New): super(MainWindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self._translate = QCoreApplication.translate # MenuBar CSS self.ui.menubar.setStyleSheet( 'QMenuBar::item { color: #FFFFFF } QMenuBar::item::selected { background: #383838; color: #FFFFFF } ' 'QMenu::item { color: #FFFFFF } QMenu::item::selected { background: #383838; color: #FFFFFF }' ) # MainWindow Title self.setWindowTitle('Audiobook Editor') # Set Window Icon # self.setWindowIcon(QIcon(':/pic/icon/audiobook-editor-logo.ico')) # Hide Minimize & Maximize buttons # self.setWindowFlags(self.windowFlags() & ~Qt.WindowMaximizeButtonHint) # WindowMinMaxButtonsHint) # Magic to make title bar disappear self.ui.dockWidget_Left.setTitleBarWidget(QWidget(None)) self.ui.dockWidget_CoverPreviewWidget.setTitleBarWidget(QWidget(None)) self.ui.dockWidget_SupplementalList.setTitleBarWidget(QWidget(None)) # self.ui.action_Qt.triggered.connect(self.on_action_Qt_triggered) self.book = Audiobook.getInstance() layout = QVBoxLayout(self.ui.tab_Metadata) layout.setContentsMargins(0, 0, 0, 0) layout.setAlignment(Qt.AlignTop | Qt.AlignLeft) self.metadataWidget = MetadataWidget(self, dict_New) layout.addWidget(self.metadataWidget) # Magic to make title bar disappear self.ui.dockWidget_Left.setTitleBarWidget(QWidget(None)) self.ui.dockWidget_CoverPreviewWidget.setTitleBarWidget(QWidget(None)) self.ui.dockWidget_SupplementalList.setTitleBarWidget(QWidget(None)) layout = QVBoxLayout(self.ui.dockWidgetContents) layout.setContentsMargins(0, 0, 0, 0) self.coverPreviewWidget = CoverPreviewWidget(self) layout.addWidget(self.coverPreviewWidget) w = QWidget() layout = QHBoxLayout(w) layout.setContentsMargins(0, 0, 0, 0) self.readingOrderWidget = ReadingOrderWidget(self) layout.addWidget(self.readingOrderWidget) # layout.addStretch(0) self.setCentralWidget(w) layout = QVBoxLayout(self.ui.tab_TOC) self.tocWidget = TOCListWidget(self) layout.addWidget(self.tocWidget) self.readingOrderWidget.signal_Add_Resource_to_TOC.connect( self.tocWidget.add_Resource_to_TOC_triggered) self.readingOrderWidget.signal_Duration_Changed.connect( self.metadataWidget.onDurationChanged) layout = QVBoxLayout(self.ui.dockWidgetContents_2) layout.setContentsMargins(0, 0, 0, 0) self.supplementalListWidget = SupplementalListWidgetWithWidgets(self) layout.addWidget(self.supplementalListWidget) self.supplementalListWidget.signal_Add_Resource_to_TOC.connect( self.tocWidget.add_Resource_to_TOC_triggered) # self.supplementalListWidget.addItems(1) self.ui.action_New.triggered.connect(self._new) self.ui.action_Open.triggered.connect(self._open) self.ui.action_Exit.triggered.connect(self._exit) self.ui.action_Save.triggered.connect(self._save) self.ui.action_Close.triggered.connect(self._close) self.ui.action_Validate.triggered.connect(self._validate) self.ui.action_Pack.triggered.connect(self._pack) self.settings = QSettings() self.recentFilesOrDirectoriesInSettings = '' self.ui.action_EN.triggered.connect( lambda: self.changeUILanguage('EN', self.ui.action_EN)) self.ui.action_TC.triggered.connect( lambda: self.changeUILanguage('TC', self.ui.action_TC)) app = QApplication.instance() for action in self.ui.menu_Language.actions(): if action.objectName() == 'action_' + app.ui_Language: action.setChecked(True) else: action.setChecked(False) @Slot() def on_action_Qt_triggered(self): self.mask = MaskWidget(self) self.mask.show() QApplication.aboutQt() self.mask.close() @Slot() def on_action_Thanks_triggered(self): self._openAlertWindow( "backports.tempfile, beautifulsoup4, html5lib, jsonschema, multipledispatch, tinytag," " PySide2, Python, Qt5, regex, requests, urllib3", self._translate("MainWindow", "Special thanks")) @Slot() def on_action_Version_triggered(self): app = QApplication.instance() self._openAlertWindow( "<center>" + self._translate("MainWindow", "Audiobook Editor") + "</center>" + "<center>" + app.applicationVersion() + "</center>" + "<center>" + self._translate("MainWindow", "Hyweb Technology CO., LTD.") + "</center>", self._translate("MainWindow", "Version information")) @Slot() def on_action_Issues_triggered(self): QDesktopServices.openUrl( QUrl('https://github.com/hywebr00/audiobook-editor/issues')) @Slot() def on_action_License_triggered(self): QDesktopServices.openUrl(QUrl.fromLocalFile('about_licenses.html')) def _openAlertWindow(self, str_Msg, str_Title): self.mask = MaskWidget(self) self.mask.show() self.alert = Alert(str_Msg, str_Title) self.alert.move( self.geometry().x() + self.geometry().width() / 2 - self.alert.geometry().width() / 2, self.geometry().y() + self.geometry().height() / 2 - self.alert.geometry().height() / 2) result = self.alert.exec_() self.mask.close() return result def _exit(self): self.signal_exit.emit() def _pack(self): book = Audiobook.getInstance() try: self._save(flag_Pack=True) book.on_action_Pack_triggered(self) except Exception as ex: logging.debug("{0}".format(ex)) def _save(self, flag_Pack=False): logging.debug("_save()") # required fields should be checked first manifest = self.metadataWidget.save() if manifest == {}: if not flag_Pack: return else: raise Exception('Some necessary fields are empty!') toc = self.tocWidget.save() if toc == {}: if not flag_Pack: return else: raise Exception('Some necessary fields are empty!') cover = self.coverPreviewWidget.save() readingOrder = self.readingOrderWidget.save() supplementalList = self.supplementalListWidget.save() logging.debug(manifest) self.book.setManifestDict(manifest) logging.debug(readingOrder) self.book.setReadingOrderList(readingOrder) logging.debug(toc) self.book.setTOCList(toc) logging.debug(cover) self.book.setCoverDict(cover) logging.debug(supplementalList) self.book.setSupplementalList(supplementalList) self.updateSettings() self.book.on_action_Save_Audiobook_triggered() def refreshToc(self): tocList = [] return tocList def refreshManifest(self): return {} def _new(self): """ toc_refreshed = refreshToc() manifest_refreshed = refreshManifest() """ # self.book.on_action_Save_Audiobook_triggered() self.updateSettings() self.signal_new.emit() def updateSettings(self): self.settings = QSettings() self.recentFilesOrDirectoriesInSettings = self.settings.value( 'RecentlyOpenedFilesOrDirectories', '', type=str) logging.debug(self.recentFilesOrDirectoriesInSettings) self.recentFilesOrDirectoriesInSettings = self.recentFilesOrDirectoriesInSettings[: -1] self.recentFilesOrDirectoriesInSettings = self.recentFilesOrDirectoriesInSettings[ 1:] if len(self.recentFilesOrDirectoriesInSettings) > 0: self.recentFilesOrDirectoriesInSettings = [ json.loads(obj) for obj in self.recentFilesOrDirectoriesInSettings.split(';') ] else: self.recentFilesOrDirectoriesInSettings = [] bookPath = self.book.getBookDir() coverFile = self.book.getCoverDict() manifest = self.book.getManifestDict() logging.debug(bookPath) logging.debug(coverFile) logging.debug(manifest) dict_One = {} if self.book.is_LPF: dict_One = next( (one for one in self.recentFilesOrDirectoriesInSettings if one["bookPath"] == self.book.getLPFFilename()), None) logging.debug(dict_One) if dict_One is None: self.recentFilesOrDirectoriesInSettings.insert( 0, { "bookPath": self.book.getLPFFilename(), "coverFile": coverFile.get("url", ""), "bookTitle": manifest.get("name", ""), "lastOpenedDate": QDate.currentDate().toString( Qt.ISODate) }) else: self.recentFilesOrDirectoriesInSettings.remove(dict_One) dict_One["lastOpenedDate"] = QDate.currentDate().toString( Qt.ISODate) dict_One["coverFile"] = coverFile.get('url', "") dict_One["bookTitle"] = manifest.get("name", "") self.recentFilesOrDirectoriesInSettings.insert(0, dict_One) else: dict_One = next((one for one in self.recentFilesOrDirectoriesInSettings if one["bookPath"] == bookPath), None) logging.debug(dict_One) if dict_One is None: dict_Str = json.dumps( { "bookPath": bookPath, "coverFile": coverFile.get("url", ""), "bookTitle": manifest.get("name", ""), "lastOpenedDate": QDate.currentDate().toString( Qt.ISODate) }, ensure_ascii=False) self.recentFilesOrDirectoriesInSettings.insert( 0, { "bookPath": bookPath, "coverFile": coverFile.get("url", ""), "bookTitle": manifest.get("name", ""), "lastOpenedDate": QDate.currentDate().toString( Qt.ISODate) }) else: self.recentFilesOrDirectoriesInSettings.remove(dict_One) dict_One["lastOpenedDate"] = QDate.currentDate().toString( Qt.ISODate) dict_One["coverFile"] = coverFile.get("url", "") dict_One["bookTitle"] = manifest.get("name", "") self.recentFilesOrDirectoriesInSettings.insert(0, dict_One) logging.debug(self.recentFilesOrDirectoriesInSettings) self.settings.setValue( 'RecentlyOpenedFilesOrDirectories', '[' + ';'.join([ json.dumps(o) for o in self.recentFilesOrDirectoriesInSettings ]) + ']') def _open(self): self.updateSettings() self.signal_open.emit() def _close(self): self.book.on_action_Save_Audiobook_triggered() self.updateSettings() self.signal_close.emit() def _validate(self): logging.debug("_validate") try: self._save(True) except Exception as ex: logging.debug('omit validation process') return manifestForTest = self.book.getManifestDict() logging.debug(manifestForTest) self.mask = MaskWidget(self) self.mask.show() with open( os.path.join(os.path.dirname(__file__), 'audiobooks.schema.json')) as f: schemaData = f.read() schema = json.loads(schemaData) self.validator = Validator( self._translate('MainWindow', 'Please wait...\n'), self._translate('MainWindow', 'Validation begins:'), schema, manifestForTest) self.validator.move( self.geometry().x() + self.geometry().width() / 2 - self.validator.geometry().width() / 2, self.geometry().y() + self.geometry().height() / 2 - self.validator.geometry().height() / 2) result = self.validator.exec_() self.mask.close() def changeUILanguage(self, lang, action): logging.debug('changeUILanguage') translator = QTranslator() app = QApplication.instance() app.removeTranslator(app.ui_Translator) for act in self.ui.menu_Language.actions(): if act == action: action.setChecked(True) else: act.setChecked(False) # EN, TC translator.load( os.path.dirname(__file__) + '/Language/appLang_' + lang + '.qm') app.ui_Language = lang app.installTranslator(translator) app.ui_Translator = translator self.ui.retranslateUi(self) settings = QSettings(app.organizationName(), app.applicationName()) settings.setValue('Language', lang) def mousePressEvent(self, event): logging.debug('mousePressEvent') self.oldPos = event.globalPos() def mouseMoveEvent(self, event): if hasattr(self, "oldPos"): logging.debug('mouseMoveEvent with oldPos') delta = QPoint(event.globalPos() - self.oldPos) self.move(self.x() + delta.x(), self.y() + delta.y()) self.oldPos = event.globalPos() screenCount = QApplication.instance().desktop().screenCount() for screenNo in range(screenCount): if QApplication.instance().screens()[screenNo] == self.screen( ): logging.debug( 'mouseMoveEvent on screen {}'.format(screenNo)) break # logging.debug('mouseMoveEvent on screen {}'.format(self.screen())) def resizeEvent(self, event): width, height = event.size().width(), event.size().height() # logging.debug('resizeEvent with {}'.format(event.size())) # screenNo = QApplication.instance().screenAt(self.pos()) screenCount = QApplication.instance().desktop().screenCount() for screenNo in range(screenCount): if QApplication.instance().screens()[screenNo] == self.screen(): logging.debug('resizeEvent {} on screen {} with {}'.format( event.size(), screenNo, self.screen().size())) sWidth, sHeight = self.screen().size().width(), self.screen( ).size().height() break minWidth, minHeight = self.minimumWidth(), self.minimumHeight() if minWidth <= sWidth and minHeight <= sHeight: event.accept() else: event.accept() self.showMaximized()
def on_action_Qt_triggered(self): self.mask = MaskWidget(self) self.mask.show() QApplication.aboutQt() self.mask.close()
class MetadataWidget(QWidget): switch_window = Signal() # signal_exit = pyqtSignal() @dispatch(QMainWindow) def __init__(self, mainWindow): super(MetadataWidget, self).__init__() self.mainWindow = mainWindow self.ui = Ui_MetadataWidget() self.ui.setupUi(self) # self.ui.label_5.setMinimumWidth(self.ui.label_5.sizeHint().width()) # self.ui.label_5.setMaximumWidth(self.ui.label_5.sizeHint().width()) self.ui.label_5.adjustSize() self.ui.label_12.setGeometry(self.ui.label_5.geometry().x() + self.ui.label_5.geometry().width() + 7, self.ui.label_12.geometry().y(), self.ui.label_12.geometry().width(), self.ui.label_12.geometry().height()) self.ui.label_15.adjustSize() self.ui.label_17.setGeometry(self.ui.label_15.geometry().x() + self.ui.label_15.geometry().width() + 7, self.ui.label_17.geometry().y(), self.ui.label_17.geometry().width(), self.ui.label_17.geometry().height()) self.ui.label_11.adjustSize() self.ui.label_18.setGeometry(self.ui.label_11.geometry().x() + self.ui.label_11.geometry().width() + 7, self.ui.label_18.geometry().y(), self.ui.label_18.geometry().width(), self.ui.label_18.geometry().height()) # self.ui.label_15.setMinimumWidth(self.ui.label_15.sizeHint().width()) # self.ui.label_15.setMaximumWidth(self.ui.label_15.sizeHint().width()) # # self.ui.label_11.setMinimumWidth(self.ui.label_11.sizeHint().width()) # self.ui.label_11.setMaximumWidth(self.ui.label_11.sizeHint().width()) self._translate = QCoreApplication.translate _translate = self._translate self._duration = 0.0 currentDate = QDate.currentDate() self.ui.dateEdit_DatePublished.setDate(currentDate) self.ui.dateEdit_DateModified.setDate(currentDate) self.mask = None self.alert = None @dispatch(QMainWindow, dict) def __init__(self, mainWindow, manifestDict): super(MetadataWidget, self).__init__() self.mainWindow = mainWindow self.ui = Ui_MetadataWidget() self.ui.setupUi(self) self.ui.label_5.adjustSize() self.ui.label_12.setGeometry(self.ui.label_5.geometry().x() + self.ui.label_5.geometry().width() + 7, self.ui.label_12.geometry().y(), self.ui.label_12.geometry().width(), self.ui.label_12.geometry().height()) self.ui.label_15.adjustSize() self.ui.label_17.setGeometry(self.ui.label_15.geometry().x() + self.ui.label_15.geometry().width() + 7, self.ui.label_17.geometry().y(), self.ui.label_17.geometry().width(), self.ui.label_17.geometry().height()) self.ui.label_11.adjustSize() self.ui.label_18.setGeometry(self.ui.label_11.geometry().x() + self.ui.label_11.geometry().width() + 7, self.ui.label_18.geometry().y(), self.ui.label_18.geometry().width(), self.ui.label_18.geometry().height()) self._translate = QCoreApplication.translate self._duration = 0.0 currentDate = QDate.currentDate() if manifestDict.get('saveDir') is not None: # from CreateNewWizard self.ui.lineEdit_BookTitle.setText(manifestDict.get("bookTitle")) else: self.ui.lineEdit_BookTitle.setText(manifestDict.get("name")) # self.ui.lineEdit_BookTitle.setReadOnly(True) authorDict = manifestDict.get("author", {"type": "Person", "name": ""}) if isinstance(authorDict, dict): # New-styled author = authorDict.get("name", "") else: # Old-fashioned: str author = authorDict self.ui.lineEdit_Author.setText(author) readByDict = manifestDict.get("readBy", {"type": "Person", "name": ""}) if isinstance(readByDict, dict): # New-styled readBy = readByDict.get("name", "") else: # Old-fashioned readBy = readByDict self.ui.lineEdit_ReadBy.setText(readBy) self.ui.lineEdit_Publisher.setText(manifestDict.get("publisher", "")) self.ui.lineEdit_Website.setText(manifestDict.get("url", "")) self.ui.lineEdit_Website.setToolTip("<p style=\"color:#FFFFFF\">" + manifestDict.get("url", "") + "</p>") self.ui.comboBox_inLanguage.setCurrentText(manifestDict.get("inLanguage", "")) # print("datePublished =" + manifestDict.get("datePublished", currentDate.toString(Qt.ISODate))) try: self.ui.dateEdit_DatePublished.setDate(QDate.fromString(manifestDict.get("datePublished", currentDate.toString(Qt.ISODate)), Qt.ISODate)) except Exception as ex: self.ui.dateEdit_DatePublished.setDate(QDate.fromString(currentDate.toString(Qt.ISODate))) try: self.ui.dateEdit_DateModified.setDate(QDate.fromString(manifestDict.get("dateModified", currentDate.toString(Qt.ISODate)), Qt.ISODate)) except Exception as ex: self.ui.dateEdit_DateModified.setDate(QDate.fromString(currentDate.toString(Qt.ISODate))) self.mask = None self.alert = None ''' # Required self.ui.lineEdit_name.setText(manifestDict.get("name", "")) self.ui.lineEdit_conformsTo.setText(manifestDict.get("conformsTo", "")) self.ui.lineEdit_context.setText(str(manifestDict.get("@context", []))) self.ui.comboBox_abridged.setCurrentText(str(manifestDict.get("abridged", True))) self.ui.lineEdit_accessibilityFeature.setText(str(manifestDict.get("accessibilityFeature", []))) self.ui.lineEdit_accessibilityHazard.setText(manifestDict.get("accessibilityHazard", "")) self.ui.lineEdit_accessibilitySummary.setText(manifestDict.get("accessibilitySummary", "")) self.ui.comboBox_accessMode.setCurrentData(manifestDict.get("accessMode", [])) self.ui.lineEdit_accessModeSufficient.setText(str(manifestDict.get("accessModeSufficient", []))) self.ui.lineEdit_author.setText(manifestDict.get("author", "")) self.ui.lineEdit_cover.setText(manifestDict.get("cover", "")) self.ui.lineEdit_duration.setText(manifestDict.get("duration", "")) print( "dateModified = " + manifestDict.get("dateModified", "")) dt = QDateTime.fromString("2020-12-15 22:00:30") print( "dateModified = " + dt.toString("yyyy-MM-dd hh:mm:ss")) self.ui.dateTimeEdit_dateModified.setDateTime(QDateTime.fromString(manifestDict.get("dateModified", ""))) #date = QDate.fromString(manifestDict.get("datePublished", "1900-1-1"), "yyyy-MM-dd") self.ui.dateEdit_datePublished.setDate(QDate.fromString(manifestDict.get("datePublished", "1900-1-1"), "yyyy-MM-dd")) self.ui.lineEdit_id.setText(manifestDict.get("id", "")) self.ui.comboBox_inLanguage.setCurrentText(manifestDict.get("inLanguage", "ZH-TW")) self.ui.lineEdit_readBy.setText(manifestDict.get("readBy", "")) self.ui.comboBox_readingProgression.setCurrentText(manifestDict.get("readingProgression", "")) self.ui.lineEdit_type.setText(manifestDict.get("type", "CreativeWork")) self.ui.lineEdit_url.setText(manifestDict.get("url", "")) ''' def save(self): dict_pm = dict() # Required dict_pm["name"] = self.ui.lineEdit_BookTitle.text() dict_pm["conformsTo"] = "https://www.w3.org/TR/audiobooks/" dict_pm["@context"] = ["https://schema.org", "https://www.w3.org/ns/pub-context"] # Optional ''' dict_pm["abridged"] = self.ui.comboBox_abridged.currentText() dict_pm["accessibilityFeature"] = self.ui.lineEdit_accessibilityFeature.text() dict_pm["accessibilityHazard"] = self.ui.lineEdit_accessibilityHazard.text() dict_pm["accessibilitySummary"] = self.ui.lineEdit_accessibilitySummary.text() dict_pm["accessModeSufficient"] = self.ui.lineEdit_accessModeSufficient.text() dict_pm["accessMode"] = self.ui.comboBox_accessMode.currentText() ''' dict_pm["author"] = {"type": "Person", "name": self.ui.lineEdit_Author.text()} dict_pm["duration"] = "PT" + "{:.2f}".format(self._duration) + "S" dict_pm["dateModified"] = self.ui.dateEdit_DateModified.date().toString(Qt.ISODate) dict_pm["datePublished"] = self.ui.dateEdit_DatePublished.date().toString(Qt.ISODate) dict_pm["inLanguage"] = self.ui.comboBox_inLanguage.currentText() dict_pm["readBy"] = {"type": "Person", "name": self.ui.lineEdit_ReadBy.text()} dict_pm["publisher"] = self.ui.lineEdit_Publisher.text() # dict_pm["readingProgression"] = self.ui.comboBox_readingProgression.currentText() dict_pm["type"] = 'Audiobook' dict_pm["url"] = self.ui.lineEdit_Website.text() """ if dict_pm["name"] == "" or dict_pm["author"] == "" or dict_pm["readBy"] == "": self.alert = Alert(self._translate("MetadataWidget", "Some required fields are empty!")) self.alert.show() # QMessageBox.information(self, self._translate("MetadataWidget", "Hint!"), self._translate("MetadataWidget", "Some required fields are empty!"), QMessageBox.Ok) return {} """ bool_Alert_BookTitle = False bool_Alert_Author = False bool_Alert_ReadBy = False if dict_pm["name"] == "": self.ui.lineEdit_BookTitle.setEnabled(False) bool_Alert_BookTitle = True if dict_pm["author"]["name"] == "": self.ui.lineEdit_Author.setEnabled(False) bool_Alert_Author = True if dict_pm["readBy"]["name"] == "": self.ui.lineEdit_ReadBy.setEnabled(False) bool_Alert_ReadBy = True if bool_Alert_BookTitle or bool_Alert_Author or bool_Alert_ReadBy: self.mask = MaskWidget(self.mainWindow) self.mask.show() self.alert = Alert(self._translate("MetadataWidget", "Some required fields are empty!")) # self.alert.close_alert.connect(self.closeAlert) self.alert.move(self.mainWindow.geometry().x() + self.mainWindow.geometry().width()/2 - self.alert.geometry().width()/2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height()/2 - self.alert.geometry().height()/2) # self.alert.show() result = self.alert.exec_() self.closeAlert() return {} return dict_pm def onDurationChanged(self, duration): logging.debug("onDurationChanged : duration = " + "{:02d}".format(int(duration//3600)) + ":{:02d}".format(int(duration//60)) + ":{:02d}".format(int(duration%60))) self._duration = duration hr = int(duration//3600) mn = int(duration//60) - hr * 60 self.ui.label_Duration.setText("{:02d}".format(hr) + ":{:02d}".format(mn) + ":{:02d}".format(int(duration%60))) def closeAlert(self): self.mask.close() self.ui.lineEdit_BookTitle.setEnabled(True) self.ui.lineEdit_Author.setEnabled(True) self.ui.lineEdit_ReadBy.setEnabled(True) def changeEvent(self, event): """Handle LanguageChange event""" if event.type() == QEvent.LanguageChange: logging.debug("Language changed") self.ui.retranslateUi(self) self.ui.label_5.adjustSize() self.ui.label_12.setGeometry(self.ui.label_5.geometry().x() + self.ui.label_5.geometry().width() + 7, self.ui.label_12.geometry().y(), self.ui.label_12.geometry().width(), self.ui.label_12.geometry().height()) self.ui.label_15.adjustSize() self.ui.label_17.setGeometry(self.ui.label_15.geometry().x() + self.ui.label_15.geometry().width() + 7, self.ui.label_17.geometry().y(), self.ui.label_17.geometry().width(), self.ui.label_17.geometry().height()) self.ui.label_11.adjustSize() self.ui.label_18.setGeometry(self.ui.label_11.geometry().x() + self.ui.label_11.geometry().width() + 7, self.ui.label_18.geometry().y(), self.ui.label_18.geometry().width(), self.ui.label_18.geometry().height()) super().changeEvent(event)
class CreateNewWizard(QMainWindow): switch_window = Signal(dict) def __init__(self): super(CreateNewWizard, self).__init__() self.ui = Ui_CreateNewWizard() self.ui.setupUi(self) self.ui.label_3.adjustSize() self.ui.label_9.setGeometry(self.ui.label_3.geometry().x() + self.ui.label_3.geometry().width() + 7, self.ui.label_9.geometry().y(), self.ui.label_9.geometry().width(), self.ui.label_9.geometry().height()) self.ui.label_4.adjustSize() self.ui.label_10.setGeometry(self.ui.label_4.geometry().x() + self.ui.label_4.geometry().width() + 7, self.ui.label_10.geometry().y(), self.ui.label_10.geometry().width(), self.ui.label_10.geometry().height()) # REMOVE TITLE BAR self.setWindowFlag(Qt.FramelessWindowHint) self._translate = QCoreApplication.translate self.ui.pushButton_Browse.clicked.connect(self._button_Browse_clicked) self.ui.pushButton_Create.clicked.connect(self._button_Create_clicked) self.ui.pushButton_Cancel.clicked.connect(self._button_Cancel_clicked) def _button_Browse_clicked(self): saveDirectory = QFileDialog.getExistingDirectory(self, self._translate("NewBookWizard", "Select one directory"), "./") logging.debug(saveDirectory) if len(saveDirectory) > 0: self.ui.lineEdit_BookPath.setText(saveDirectory) self.ui.lineEdit_BookPath.setReadOnly(True) def _button_Create_clicked(self): saveDir = self.ui.lineEdit_BookPath.text() bookTitle = self.ui.lineEdit_BookTitle.text() author = self.ui.lineEdit_Author.text() publisher = self.ui.lineEdit_Publisher.text() readBy = self.ui.lineEdit_ReadBy.text() if saveDir and bookTitle: logging.debug(saveDir + ":" + bookTitle) self.switch_window.emit({"saveDir": saveDir, "bookTitle": bookTitle, "author": {"type": "Person", "name": author}, "publisher": publisher, "readBy": {"type": "Person", "name": readBy}}) else: self.mask = MaskWidget(self) self.mask.show() self.alert = Alert(self._translate("CreateNewWizard", "You must select one directory and" " keyin the booktitle!"), self._translate("CreateNewWizard", "Warning!")) self.alert.move( self.geometry().x() + self.geometry().width() / 2 - self.alert.geometry().width() / 2, self.geometry().y() + self.geometry().height() / 2 - self.alert.geometry().height() / 2) result = self.alert.exec_() self.mask.close() def _button_Cancel_clicked(self): self.switch_window.emit({}) self.close() def mousePressEvent(self, event): self.oldPos = event.globalPos() def mouseMoveEvent(self, event): if hasattr(self, "oldPos"): delta = QPoint(event.globalPos() - self.oldPos) self.move(self.x() + delta.x(), self.y() + delta.y()) self.oldPos = event.globalPos()
def on_traverseTreeAction_triggered(self, FORCE=False): logging.debug("on_traverseAction_triggered") self._TOCList = [] # Warning: This searchChildItem function may need to be FIXED immediately! # def serchChildItem(item=None, level=0): # TOCList = [] # if not item: # return # for m in range(item.childCount()): # child = item.child(m) # if child.timeEdit_Start.time().toString("hh:mm:ss.zzz") != "00:00:00.000" and \ # child.timeEdit_End.time().toString("hh:mm:ss.zzz") != "00:00:00.000": # startSeconds = child.timeEdit_Start.time().hour() * 3600 + \ # child.timeEdit_Start.time().minute() * 60 + \ # child.timeEdit_Start.time().second() + \ # float(child.timeEdit_Start.time().msec()) / 1000 # # endSeconds = child.timeEdit_End.time().hour() * 3600 + \ # child.timeEdit_End.time().minute() * 60 + \ # child.timeEdit_End.time().second() + \ # float(child.timeEdit_End.time().msec()) / 1000 # # TOCList.append({"level": level, # "href": child.comboBox.currentText() + # "#t=" + # str(startSeconds) + # "," + # str(endSeconds), # "title": child.lineEdit.text(), # "children": serchChildItem(child, level + 1)}) # else: # TOCList.append({"level": level, # "href": child.comboBox.currentText(), # "title": child.lineEdit.text(), # "children": serchChildItem(child, level + 1)}) # return TOCList lastHref = "" lastStartTime = QTime(0, 0) lastElementIndex = -1 for i in range(self.ui.listWidget.count()): widget = self.ui.listWidget.itemWidget(self.ui.listWidget.item(i)) level = 0 if not FORCE and widget.ui.lineEdit_Title.text() == "": widget.ui.lineEdit_Title.setEnabled(False) self.mask = MaskWidget(self.mainWindow) self.mask.show() self.alert = Alert(self._translate("TOCListWidget", "Some required fields are empty!")) self.alert.close_alert.connect(self.closeAlert) self.alert.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.alert.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.alert.geometry().height() / 2) # self.alert.show() self.alert.exec_() self.closeAlert() return {} if not FORCE and widget.ui.timeEdit_Start.time().toString("hh:mm:ss.zzz") != "00:00:00.000": startSeconds = widget.ui.timeEdit_Start.time().hour() * 3600 + \ widget.ui.timeEdit_Start.time().minute() * 60 + \ widget.ui.timeEdit_Start.time().second() + \ float(widget.ui.timeEdit_Start.time().msec()) / 1000 if lastHref != widget.ui.label_Href.text(): lastHref = widget.ui.label_Href.text() lastStartTime = widget.ui.timeEdit_Start.time() lastElementIndex = i self._TOCList.append({"level": level, "href": widget.ui.label_Href.text() + "#t=" + str(startSeconds), "title": widget.ui.lineEdit_Title.text(), "children": []}) # widget.setTitle(widget.ui.lineEdit_Title.text()) else: pos = self._TOCList[lastElementIndex]['href'].find('#t=') if pos == -1: self._TOCList[lastElementIndex] = {"level": self._TOCList[lastElementIndex]['level'], "href": self._TOCList[lastElementIndex]['href'] + "#t=0," + str(startSeconds), "title": self._TOCList[lastElementIndex]['title'], "children": []} lastElementIndex = i lastStartTime = widget.ui.timeEdit_Start.time() self._TOCList.append({"level": level, "href": widget.ui.label_Href.text() + "#t=" + str(startSeconds), "title": widget.ui.lineEdit_Title.text(), "children": []}) else: str_Time = self._TOCList[lastElementIndex].get('href')[pos + 3:] if str_Time.find(',') == -1: # Not found, only #t= float_Time = float(str_Time) if float_Time < startSeconds: # Check lastElement self._TOCList[lastElementIndex] = {"level": self._TOCList[lastElementIndex]['level'], "href": self._TOCList[lastElementIndex]['href'] + "," + str(startSeconds), "title": self._TOCList[lastElementIndex]['title'], "children": []} lastElementIndex = i lastStartTime = widget.ui.timeEdit_Start.time() self._TOCList.append({"level": level, "href": widget.ui.label_Href.text() + "#t=" + str(startSeconds), "title": widget.ui.lineEdit_Title.text(), "children": []}) else: # Don't care, just pass lastElementIndex = i lastStartTime = widget.ui.timeEdit_Start.time() self._TOCList.append({"level": level, "href": widget.ui.label_Href.text() + "#t=" + str(startSeconds), "title": widget.ui.lineEdit_Title.text(), "children": []}) continue else: lastElementIndex = i lastStartTime = widget.ui.timeEdit_Start.time() self._TOCList.append({"level": level, "href": widget.ui.label_Href.text() + "#t=" + str(startSeconds), "title": widget.ui.lineEdit_Title.text(), "children": []}) continue else: self._TOCList.append({"level": level, "href": widget.ui.label_Href.text(), "title": widget.ui.lineEdit_Title.text(), "children": []}) lastHref = widget.ui.label_Href.text() lastStartTime = QTime(0, 0) lastElementIndex = i logging.debug(json.dumps(self._TOCList, indent=4, ensure_ascii=False)) return self._TOCList
class TOCListWidget(QWidget): signal_duration_calculated = Signal(float) @dispatch(QMainWindow) def __init__(self, mainWindow): logging.debug('@dispatch()') super(TOCListWidget, self).__init__() self._initialize(mainWindow, 240, 82) @dispatch(QMainWindow, int, int) def __init__(self, mainWindow, width=240, height=82): super(TOCListWidget, self).__init__() self._initialize(mainWindow, width, height) @dispatch(QMainWindow, list) def __init__(self, mainWindow, tocList, width=240, height=82): super(TOCListWidget, self).__init__() self._initialize(mainWindow, width, height) self.on_generateListWidgetItemAction_triggered(tocList) def _initialize(self, mainWindow, width, height): self._itemWidth = width self._itemHeight = height self.mainWindow = mainWindow self.ui = Ui_TOCListWidget() self.ui.setupUi(self) self.ui.gridLayout.setContentsMargins(0, 0, 0, 0) self.ui.verticalLayout.setContentsMargins(0, 0, 0, 0) self.ui.listWidget.model().rowsMoved.connect(self.afterDrop) self.ui.listWidget.installEventFilter(self) self.ui.listWidget.setEnabled(True) self._isEmpty = True self._listWidgetItemSerialNo = 0 self._translate = QCoreApplication.translate self._TOCList = [] self.mask = None self.alert = None self._afterDrop = False self.roiList = [] self.startTime = "" # self.setAcceptDrops(True) def getItemSize(self): return QSize(self._itemWidth, self._itemHeight) def getSerialNo(self): return self._listWidgetItemSerialNo @dispatch(int) def addItems(self, number=1): if self._isEmpty: self.ui.widget_NoItem.setVisible(False) self.ui.listWidget.setEnabled(True) self._isEmpty = False for i in range(number): item = QListWidgetItem() # item.setText(str(self.ui.listWidget.count())) item.setSizeHint(self.getItemSize()) roi = TOCListWidgetItem(item, self.getSerialNo()) self.ui.listWidget.addItem(item) self.ui.listWidget.setItemWidget(item, roi) self._listWidgetItemSerialNo += 1 self.roiList.append(roi) @dispatch(str) def addItems(self, fullFilename): if self._isEmpty: self.ui.widget_NoItem.setVisible(False) self.ui.listWidget.setEnabled(True) self._isEmpty = False item = QListWidgetItem() # item.setText(str(self.ui.listWidget.count())) item.setSizeHint(self.getItemSize()) roi = TOCListWidgetItem(fullFilename, item, self.getSerialNo()) self.ui.listWidget.addItem(item) self.ui.listWidget.setItemWidget(item, roi) self._listWidgetItemSerialNo += 1 self.roiList.append(roi) def getSerialNo(self): return self._listWidgetItemSerialNo def removeItem(self, item): logging.debug("removeItem is called") itemWidget = self.ui.listWidget.itemWidget(item) if itemWidget is not None: self.roiList.remove(itemWidget) del itemWidget oldItem = self.ui.listWidget.takeItem(self.ui.listWidget.row(item)) del oldItem # oldItem = self.ui.listWidget.takeItem(self.ui.listWidget.row(item)) # del oldItem if self.ui.listWidget.count() == 0: self.ui.widget_NoItem.setVisible(True) self.ui.listWidget.setEnabled(True) self._isEmpty = True def resortItems(self): for i in range(self.listWidget.count()): self.listWidget.item(i).setText(str(i)) def on_generateListWidgetItemAction_triggered(self, data, root=None): logging.debug('TOCListWidget : on_generateListWidgetItemAction_triggered') self.ui.listWidget.clear() book = Audiobook.getInstance() def addChildItem(data, level, parentItem): children = data['children'] if children: return for i in range(len(children)): child = children[i] lvl = child['level'] if lvl == level: item = QListWidgetItem(parentItem) item.setSizeHint(self.getItemSize()) roi = TOCListWidgetItem(item, self.getSerialNo()) roi.ui.lineEdit_Title.setText(dict_TOC['title']) href = dict_TOC["href"] indexOfSharpSign = href.find('#t=') if indexOfSharpSign == -1: # Not in roi.ui.label_Href.setText(href) else: timeStamp = href[indexOfSharpSign + 3:] [startTime, endTime] = timeStamp.split(',') roi.ui.timeEdit_Start.setTime(QTime.fromString(startTime, "hh:mm:ss.zzz")) # roi.ui.lineEdit_End.setText(endTime) self._listWidgetItemSerialNo += 1 addChildItem(child, level + 1, item) if isinstance(data, list): logging.debug('isinstance(data, list)') if len(data) > 0: if self._isEmpty: logging.debug('self.ui.widget_NoItem.setVisible(False)') self.ui.widget_NoItem.setVisible(False) self.ui.listWidget.setEnabled(True) self._isEmpty = False for i in range(len(data)): dict_TOC = data[i] level = dict_TOC["level"] if level == 0: item = QListWidgetItem() # item.setText(str(self.ui.listWidget.count())) item.setSizeHint(self.getItemSize()) href = dict_TOC["href"] indexOfSharpSign = href.find('#t=') timeStamp = '' if indexOfSharpSign == -1: fullFilename = (book.getBookDir() + '/' + href, href)[href.startswith('http')] roi = TOCListWidgetItem(fullFilename, item, self.getSerialNo(), dict_TOC['title']) # roi.ui.lineEdit_Title.setText(dict_TOC['title']) else: timeStamp = href[indexOfSharpSign + 3:] fullFilename = (book.getBookDir() + '/' + href[0: indexOfSharpSign], href[0: indexOfSharpSign])[href.startswith('http')] roi = TOCListWidgetItem(fullFilename, item, self.getSerialNo(), dict_TOC['title']) # roi.ui.lineEdit_Title.setText(dict_TOC['title']) if timeStamp.find(',') == -1: startTime = timeStamp startTimeFloat = float(startTime) hh = int(startTimeFloat // 3600) startTimeFloat -= hh * 3600 mm = int(startTimeFloat // 60) startTimeFloat -= mm * 60 ss = int(startTimeFloat) startTimeFloat -= ss zzz = startTimeFloat startTimeString = "{:02d}".format(hh) + \ ":{:02d}".format(mm) + \ ":{:02d}".format(ss) + \ str("{:.3f}".format(zzz))[1: 5] roi.ui.timeEdit_Start.setTime(QTime.fromString(startTimeString, "hh:mm:ss.zzz")) else: # To-Do: start- & end-time both exist [startTime, endTime] = timeStamp.split(',') startTimeFloat = float(startTime) endTimeFloat = float(endTime) hh = int(startTimeFloat // 3600) startTimeFloat -= hh * 3600 mm = int(startTimeFloat // 60) startTimeFloat -= mm * 60 ss = int(startTimeFloat) startTimeFloat -= ss zzz = startTimeFloat startTimeString = "{:02d}".format(hh) + \ ":{:02d}".format(mm) + \ ":{:02d}".format(ss) + \ str("{:.3f}".format(zzz))[1: 5] hh = int(endTimeFloat // 3600) endTimeFloat -= hh * 3600 mm = int(endTimeFloat // 60) endTimeFloat -= mm * 60 ss = int(endTimeFloat) endTimeFloat -= ss zzz = endTimeFloat endTimeString = "{:02d}".format(hh) + \ ":{:02d}".format(mm) + \ ":{:02d}".format(ss) + \ str("{:.3f}".format(zzz))[1: 5] roi.ui.timeEdit_Start.setTime(QTime.fromString(startTimeString, "hh:mm:ss.zzz")) pass self.ui.listWidget.addItem(item) self.ui.listWidget.setItemWidget(item, roi) self._listWidgetItemSerialNo += 1 # if dict_TOC['children'] != []: addChildItem(dict_TOC, level + 1, item) self.roiList.append(roi) self.ui.listWidget.setEnabled(True) @Slot() # FORCE: No checking title and No adding timestamps def on_traverseTreeAction_triggered(self, FORCE=False): logging.debug("on_traverseAction_triggered") self._TOCList = [] # Warning: This searchChildItem function may need to be FIXED immediately! # def serchChildItem(item=None, level=0): # TOCList = [] # if not item: # return # for m in range(item.childCount()): # child = item.child(m) # if child.timeEdit_Start.time().toString("hh:mm:ss.zzz") != "00:00:00.000" and \ # child.timeEdit_End.time().toString("hh:mm:ss.zzz") != "00:00:00.000": # startSeconds = child.timeEdit_Start.time().hour() * 3600 + \ # child.timeEdit_Start.time().minute() * 60 + \ # child.timeEdit_Start.time().second() + \ # float(child.timeEdit_Start.time().msec()) / 1000 # # endSeconds = child.timeEdit_End.time().hour() * 3600 + \ # child.timeEdit_End.time().minute() * 60 + \ # child.timeEdit_End.time().second() + \ # float(child.timeEdit_End.time().msec()) / 1000 # # TOCList.append({"level": level, # "href": child.comboBox.currentText() + # "#t=" + # str(startSeconds) + # "," + # str(endSeconds), # "title": child.lineEdit.text(), # "children": serchChildItem(child, level + 1)}) # else: # TOCList.append({"level": level, # "href": child.comboBox.currentText(), # "title": child.lineEdit.text(), # "children": serchChildItem(child, level + 1)}) # return TOCList lastHref = "" lastStartTime = QTime(0, 0) lastElementIndex = -1 for i in range(self.ui.listWidget.count()): widget = self.ui.listWidget.itemWidget(self.ui.listWidget.item(i)) level = 0 if not FORCE and widget.ui.lineEdit_Title.text() == "": widget.ui.lineEdit_Title.setEnabled(False) self.mask = MaskWidget(self.mainWindow) self.mask.show() self.alert = Alert(self._translate("TOCListWidget", "Some required fields are empty!")) self.alert.close_alert.connect(self.closeAlert) self.alert.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.alert.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.alert.geometry().height() / 2) # self.alert.show() self.alert.exec_() self.closeAlert() return {} if not FORCE and widget.ui.timeEdit_Start.time().toString("hh:mm:ss.zzz") != "00:00:00.000": startSeconds = widget.ui.timeEdit_Start.time().hour() * 3600 + \ widget.ui.timeEdit_Start.time().minute() * 60 + \ widget.ui.timeEdit_Start.time().second() + \ float(widget.ui.timeEdit_Start.time().msec()) / 1000 if lastHref != widget.ui.label_Href.text(): lastHref = widget.ui.label_Href.text() lastStartTime = widget.ui.timeEdit_Start.time() lastElementIndex = i self._TOCList.append({"level": level, "href": widget.ui.label_Href.text() + "#t=" + str(startSeconds), "title": widget.ui.lineEdit_Title.text(), "children": []}) # widget.setTitle(widget.ui.lineEdit_Title.text()) else: pos = self._TOCList[lastElementIndex]['href'].find('#t=') if pos == -1: self._TOCList[lastElementIndex] = {"level": self._TOCList[lastElementIndex]['level'], "href": self._TOCList[lastElementIndex]['href'] + "#t=0," + str(startSeconds), "title": self._TOCList[lastElementIndex]['title'], "children": []} lastElementIndex = i lastStartTime = widget.ui.timeEdit_Start.time() self._TOCList.append({"level": level, "href": widget.ui.label_Href.text() + "#t=" + str(startSeconds), "title": widget.ui.lineEdit_Title.text(), "children": []}) else: str_Time = self._TOCList[lastElementIndex].get('href')[pos + 3:] if str_Time.find(',') == -1: # Not found, only #t= float_Time = float(str_Time) if float_Time < startSeconds: # Check lastElement self._TOCList[lastElementIndex] = {"level": self._TOCList[lastElementIndex]['level'], "href": self._TOCList[lastElementIndex]['href'] + "," + str(startSeconds), "title": self._TOCList[lastElementIndex]['title'], "children": []} lastElementIndex = i lastStartTime = widget.ui.timeEdit_Start.time() self._TOCList.append({"level": level, "href": widget.ui.label_Href.text() + "#t=" + str(startSeconds), "title": widget.ui.lineEdit_Title.text(), "children": []}) else: # Don't care, just pass lastElementIndex = i lastStartTime = widget.ui.timeEdit_Start.time() self._TOCList.append({"level": level, "href": widget.ui.label_Href.text() + "#t=" + str(startSeconds), "title": widget.ui.lineEdit_Title.text(), "children": []}) continue else: lastElementIndex = i lastStartTime = widget.ui.timeEdit_Start.time() self._TOCList.append({"level": level, "href": widget.ui.label_Href.text() + "#t=" + str(startSeconds), "title": widget.ui.lineEdit_Title.text(), "children": []}) continue else: self._TOCList.append({"level": level, "href": widget.ui.label_Href.text(), "title": widget.ui.lineEdit_Title.text(), "children": []}) lastHref = widget.ui.label_Href.text() lastStartTime = QTime(0, 0) lastElementIndex = i logging.debug(json.dumps(self._TOCList, indent=4, ensure_ascii=False)) return self._TOCList def save(self, FORCE=False): logging.debug("save in TOCWidget") return self.on_traverseTreeAction_triggered(FORCE) @Slot(str, str) def add_Resource_to_TOC_triggered(self, fullFilename, title): logging.debug('TOCListWidget : add_Resource_to_TOC_triggered') if self._isEmpty: self.ui.widget_NoItem.setVisible(False) self.ui.listWidget.setEnabled(True) self._isEmpty = False # self.ui.listWidget.setEnabled(True) item = QListWidgetItem() # item.setText(str(self.ui.listWidget.count())) item.setSizeHint(self.getItemSize()) roi = TOCListWidgetItem(fullFilename, item, self.getSerialNo(), title) roi.ui.lineEdit_Title.setText(title) self.ui.listWidget.addItem(item) self.ui.listWidget.setItemWidget(item, roi) self._listWidgetItemSerialNo += 1 self.roiList.append(roi) def closeAlert(self): self.mask.close() for i in range(self.ui.listWidget.count()): widget = self.ui.listWidget.itemWidget(self.ui.listWidget.item(i)) widget.ui.lineEdit_Title.setEnabled(True) def changeEvent(self, event): """Handle LanguageChange event""" if event.type() == QEvent.LanguageChange: logging.debug("Language changed") for i in range(self.ui.listWidget.count()): widget = self.ui.listWidget.itemWidget(self.ui.listWidget.item(i)) widget.setTitle(widget.getTitle()) self.ui.retranslateUi(self) super().changeEvent(event) def afterDrop(self, parent, start, end, destination, row): # super(ReadingOrderWidget, self).dropEvent(event) logging.debug('afterDrop') self._afterDrop = True def eventFilter(self, sender, event): if not (event.type() in [QEvent.Paint, QEvent.Leave, QEvent.Enter, QEvent.FocusAboutToChange, QEvent.Timer]): logging.debug(event.type()) if event.type() == QEvent.DragEnter: logging.debug("QEvent.DragEnter") logging.debug(sender) logging.debug(event.source()) logging.debug(event.mimeData().formats()) if isinstance(event.source(), TOCListWidgetItem): event.setDropAction(Qt.MoveAction) event.acceptProposedAction() return True elif isinstance(event.source(), ReadingOrderItem): # self.ui.listWidget.setDragDropMode(QAbstractItemView.InternalMove) event.setDropAction(Qt.CopyAction) event.acceptProposedAction() return True elif isinstance(event.source(), SupplementalListWidgetItem): event.setDropAction(Qt.CopyAction) event.acceptProposedAction() return True else: super().eventFilter(sender, event) return True elif event.type() == QEvent.Drop: logging.debug("QEvent.Drop") logging.debug(sender) logging.debug(event.source()) logging.debug(event.mimeData().formats()) if isinstance(event.source(), TOCListWidgetItem): event.acceptProposedAction() selection = self.ui.listWidget.selectedIndexes() from_index = selection[0].row() if selection else -1 to_index = self.ui.listWidget.indexAt(event.pos()).row() if (0 <= from_index < self.ui.listWidget.model().rowCount() and 0 <= to_index < self.ui.listWidget.model().rowCount() and from_index != to_index): if from_index < to_index: for i in range(from_index, to_index): result = self.ui.listWidget.model().moveRow(self.ui.listWidget.rootIndex(), i + 1, self.ui.listWidget.rootIndex(), i) logging.debug("from {} to {} : {}".format(from_index, to_index, ("Fail", "Succeed")[result])) else: result = self.ui.listWidget.model().moveRow(self.ui.listWidget.rootIndex(), from_index, self.ui.listWidget.rootIndex(), to_index) logging.debug("from {} to {} : {}".format(from_index, to_index, ("Fail", "Succeed")[result])) # event.accept() return True elif isinstance(event.source(), ReadingOrderItem): event.acceptProposedAction() # No matter what type of URL you got, encode them by QUrl::toEncode and display them with # QUrl::fromPercentEncoding when necessary. url = QUrl.fromPercentEncoding(event.mimeData().urls()[0].toEncoded()) url = (url[0].upper() + url[1:len(url)], url)[url.startswith('http')] self.add_Resource_to_TOC_triggered(url, event.mimeData().text()) return True elif isinstance(event.source(), SupplementalListWidgetItem): event.acceptProposedAction() url = QUrl.fromPercentEncoding(event.mimeData().urls()[0].toEncoded()) url = (url[0].upper() + url[1:len(url)], url)[url.startswith('http')] self.add_Resource_to_TOC_triggered(url, str()) return True else: super().eventFilter(sender, event) return True # if sender == self.ui.listWidget and event.type() == QEvent.ChildRemoved: # logging.debug("QEvent.ChildRemoved") # # if not self._afterDrop: # itemWidget = self.ui.listWidget.itemWidget(self.ui.listWidget.currentItem()) # if itemWidget is None: # logging.debug("itemWidget is None") # roiList = self.roiList.copy() # itemWOWidget = -1 # for i in range(self.ui.listWidget.count()): # item = self.ui.listWidget.item(i) # itemWidget = self.ui.listWidget.itemWidget(item) # if itemWidget is not None: # roiList.remove(itemWidget) # else: # itemWOWidget = i # if len(roiList) == 1: # super().eventFilter(sender, event) # oldROI = roiList[0] # roi = TOCListWidgetItem(oldROI.getFullFilename(), # self.ui.listWidget.item(itemWOWidget), # self.getSerialNo(), # oldROI.getTitle()) # isEnabled, startTime = oldROI.getTimeStamps() # roi.ui.timeEdit_Start.setEnabled(isEnabled) # if isEnabled: # roi.ui.timeEdit_Start.setTime(startTime) # self._listWidgetItemSerialNo += 1 # self.ui.listWidget.setItemWidget(self.ui.listWidget.item(itemWOWidget), roi) # self.roiList.remove(oldROI) # self.roiList.append(roi) # return True # else: # self._afterDrop = False # return super().eventFilter(sender, event) # def dragEnterEvent(self, event): # logging.debug(event.source()) # logging.debug(event.mimeData().formats()) # for mimeType in event.mimeData().formats(): # logging.debug(mimeType) # # if isinstance(event.source(), ReadingOrderItem): # event.mimeData().hasText() and event.mimeData().hasUrls(): # logging.debug(event.mimeData().text()) # for url in event.mimeData().urls(): # logging.debug(url.toString()) # # event.setDropAction(Qt.CopyAction) # event.acceptProposedAction() # elif isinstance(event.source(), SupplementalListWidgetItem): # event.setDropAction(Qt.CopyAction) # event.acceptProposedAction() # else: # super().dragEnterEvent(event) # # def dropEvent(self, event): # logging.debug(event.source()) # logging.debug(event.mimeData().text()) # for url in event.mimeData().urls(): # logging.debug(url.toString()) # if isinstance(event.source(), ReadingOrderItem): # event.mimeData().hasText() and event.mimeData().hasUrls(): # event.setDropAction(Qt.CopyAction) # event.accept() # self.add_Resource_to_TOC_triggered(event.mimeData().urls()[0].toString(), # event.mimeData().text()) # elif isinstance(event.source(), SupplementalListWidgetItem): # event.setDropAction(Qt.CopyAction) # event.accept() # self.add_Resource_to_TOC_triggered(event.mimeData().urls()[0].toString(), # str()) # else: # super().dropEvent(event) # def eventFilter(self, sender, event): # if sender == self.ui.listWidget and event.type() == QEvent.ChildRemoved: # logging.debug("eventFilter") # # if not self._afterDrop: # itemWidget = self.ui.listWidget.itemWidget(self.ui.listWidget.currentItem()) # if itemWidget is None: # logging.debug("itemWidget is None") # roiList = self.roiList.copy() # itemWOWidget = -1 # for i in range(self.ui.listWidget.count()): # item = self.ui.listWidget.item(i) # itemWidget = self.ui.listWidget.itemWidget(item) # if itemWidget is not None: # roiList.remove(itemWidget) # else: # itemWOWidget = i # if len(roiList) == 1: # super().eventFilter(sender, event) # oldROI = roiList[0] # roi = TOCListWidgetItem(oldROI.getFullFilename(), # self.ui.listWidget.item(itemWOWidget), # self.getSerialNo(), # oldROI.getTitle()) # isEnabled, startTime = oldROI.getTimeStamps() # roi.ui.timeEdit_Start.setEnabled(isEnabled) # if isEnabled: # roi.ui.timeEdit_Start.setTime(startTime) # self._listWidgetItemSerialNo += 1 # self.ui.listWidget.setItemWidget(self.ui.listWidget.item(itemWOWidget), roi) # self.roiList.remove(oldROI) # self.roiList.append(roi) # return True # else: # self._afterDrop = False # # return super().eventFilter(sender, event)
class ReadingOrderWidget(QWidget): signal_Duration_Changed = Signal(float) signal_Add_Resource_to_TOC = Signal(str, str) str_LastOpenedDirectory = None @dispatch(QMainWindow) def __init__(self, mainWindow): logging.debug('@dispatch()') self.__init__(mainWindow, 420, 78) @dispatch(QMainWindow, int, int) def __init__(self, mainWindow=None, width=420, height=78): logging.debug('@dispatch(int, int)') super(ReadingOrderWidget, self).__init__() self.mainWindow = mainWindow self.ui = Ui_ReadingOrderWidget() self.ui.setupUi(self) self.ui.gridLayout.setContentsMargins(0, 0, 0, 0) self.ui.verticalLayout.setContentsMargins(0, 0, 0, 0) self.ui.verticalLayout.setAlignment(self.ui.widget, Qt.AlignTop) self.ui.verticalLayout.removeWidget(self.ui.label_Book) self.ui.verticalLayout.removeWidget(self.ui.listWidget) self.ui.listWidget.setVisible(False) self.ui.verticalLayout.addSpacerItem( QSpacerItem(420, 10, QSizePolicy.Fixed, QSizePolicy.Expanding)) self.ui.verticalLayout.addWidget(self.ui.label_Book) self.ui.verticalLayout.setAlignment(self.ui.label_Book, Qt.AlignBottom) self._listWidgetItemSerialNo = 0 self._itemWidth = width self._itemHeight = height self.ui.listWidget.model().rowsMoved.connect(self.afterDrop) self.ui.listWidget.installEventFilter(self) self._isEmpty = True self._translate = QCoreApplication.translate self.ui.pushButton_Add.setToolTip( "<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" "font-weight: 400;font-size: 14px;line-height: 20px;border: 0px;\">" + self._translate("ReadingOrderWidget", "Add voice to Reading Order") + "</p>") self._seconds_Duration = 0.0 self.mask = None self.afw = None self.alert = None self.alertwithbuttons = None self._afterDrop = False self.roiList = [] @dispatch(list, str, QMainWindow) def __init__(self, rList, bookDir, mainWindow=None, width=420, height=78): super(ReadingOrderWidget, self).__init__() self.mainWindow = mainWindow self.ui = Ui_ReadingOrderWidget() self.ui.setupUi(self) self._translate = QCoreApplication.translate self.ui.gridLayout.setContentsMargins(0, 0, 0, 0) self.ui.verticalLayout.setContentsMargins(0, 0, 0, 0) self.ui.listWidget.model().rowsMoved.connect(self.afterDrop) self.ui.pushButton_Add.setToolTip( "<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" "font-weight: 400;font-size: 14px;line-height: 20px;border: 0px;\">" + self._translate("ReadingOrderWidget", "Add voice to Reading Order") + "</p>") # self.ui.listWidget.setAcceptDrops(True) self.ui.listWidget.setDragDropMode(QAbstractItemView.InternalMove) self.ui.listWidget.setDefaultDropAction(Qt.MoveAction) self.ui.listWidget.installEventFilter(self) if len(rList) > 0: for i in range(self.ui.verticalLayout.count()): item = self.ui.verticalLayout.itemAt(i) if isinstance(item, QSpacerItem): logging.debug("QSpacerItem is at pos " + str(i)) one = self.ui.verticalLayout.takeAt(i) del one break self.ui.verticalLayout.removeWidget(self.ui.widget_Board) self.ui.verticalLayout.removeWidget(self.ui.label_Book) self.ui.widget_Board.setVisible(False) self.ui.label_Book.setVisible(False) # self.ui.verticalLayout.removeItem(self.spacerItem) self.ui.verticalLayout.addWidget(self.ui.listWidget, 1) self.ui.listWidget.show() logging.debug(self.ui.verticalLayout.count()) self._isEmpty = False else: self.ui.verticalLayout.removeWidget(self.ui.listWidget) self.ui.listWidget.hide() self.ui.verticalLayout.setAlignment(self.ui.widget, Qt.AlignTop) self.ui.verticalLayout.removeWidget(self.ui.label_Book) self.ui.verticalLayout.addSpacerItem( QSpacerItem(420, 10, QSizePolicy.Fixed, QSizePolicy.Expanding)) self.ui.verticalLayout.addWidget(self.ui.label_Book) self.ui.verticalLayout.setAlignment(self.ui.label_Book, Qt.AlignBottom) self._isEmpty = True self._listWidgetItemSerialNo = 0 self._itemWidth = width self._itemHeight = height self._seconds_Duration = 0.0 self.roiList = [] book = Audiobook.getInstance() if len(rList) > 0: self._isEmpty = False for r in rList: logging.debug(r) if isinstance(r, dict): url = r.get("url", "") item = QListWidgetItem() # item.setText(str(self.ui.listWidget.count())) item.setSizeHint(self.getItemSize()) roi = ReadingOrderItem((book.getBookDir() + '/' + url, url)[url.startswith("http")], item, self.getSerialNo()) roi.ui.lineEdit.setText(r.get("name", "")) self.ui.listWidget.addItem(item) self.ui.listWidget.setItemWidget(item, roi) self._listWidgetItemSerialNo += 1 self.roiList.append(roi) elif isinstance(r, str): logging.debug("isinstance(r, str)") url = r if url.startswith("http"): # http(s) link pass else: item = QListWidgetItem() # item.setText(str(self.ui.listWidget.count())) item.setSizeHint(self.getItemSize()) roi = ReadingOrderItem((book.getBookDir() + '/' + url, url)[url.startswith("http")], item, self.getSerialNo()) self.ui.listWidget.addItem(item) self.ui.listWidget.setItemWidget(item, roi) self._listWidgetItemSerialNo += 1 self.roiList.append(roi) logging.warning("ROL has initialized") logging.debug(self._seconds_Duration) self.resortItems() self.getDuration() self.mask = None self.afw = None self.alert = None self.alertwithbuttons = None self._afterDrop = False def getItemSize(self): return QSize(self._itemWidth, self._itemHeight) def getSerialNo(self): return self._listWidgetItemSerialNo def _openAlertWithButtonsWindow(self, str_Msg, str_Title): self.mask = MaskWidget(self.mainWindow) self.mask.show() self.alertwithbuttons = AlertWithButtons(str_Msg, str_Title) self.alertwithbuttons.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.alertwithbuttons.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.alertwithbuttons.geometry().height() / 2) result = self.alertwithbuttons.exec_() self.mask.close() return result def _openAlertWindow(self, str_Msg, str_Title): self.mask = MaskWidget(self.mainWindow) self.mask.show() self.alert = Alert(str_Msg, str_Title) self.alert.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.alert.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.alert.geometry().height() / 2) result = self.alert.exec_() self.mask.close() return result def getAttachFromWidgetResult(self, bool_Result, list_Result): logging.debug(str(bool_Result) + ' ' + ";".join(list_Result)) book = Audiobook.getInstance() set_url = book.getCheckSet(self.mainWindow) filenames = [] if not bool_Result: filenames = list_Result book.checkAttachedFile(filenames, set_url, ReadingOrderWidget, self) @Slot() def on_pushButton_Add_clicked(self): logging.debug('on_pushButton_Add_clicked') book = Audiobook.getInstance() self.mask = MaskWidget(self.mainWindow) self.mask.show() self.afw = AttachFromWidgetWithoutURL( False, self._translate("ReadingOrderWidget", 'Open file'), (ReadingOrderWidget.str_LastOpenedDirectory, book.getBookDir() + '/')[ReadingOrderWidget.str_LastOpenedDirectory is None], "Audio files (*.mp3);; Wav files (*.wav)") self.afw.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.afw.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.afw.geometry().height() / 2) result = self.afw.exec_() filenames = self.afw.getURLs() self.mask.close() if result == QDialog.Accepted and filenames: self.getAttachFromWidgetResult(False, filenames) else: return def getDuration(self): duration = 0.0 for i in range(self.ui.listWidget.count()): duration += self.ui.listWidget.itemWidget( self.ui.listWidget.item(i)).getDuration() self._seconds_Duration = duration logging.debug('latest total duration = ' + str(duration) + ' seconds.') self.signal_Duration_Changed.emit(duration) return duration @Slot(str, int) def addItems(self, fullFilename='', number=1): logging.debug("addItems()") logging.debug('fullFilename = ' + fullFilename) if self.ui.listWidget.count() == 0: for i in range(self.ui.verticalLayout.count()): item = self.ui.verticalLayout.itemAt(i) if isinstance(item, QSpacerItem): logging.debug("QSpacerItem is at pos " + str(i)) one = self.ui.verticalLayout.takeAt(i) del one break self.ui.verticalLayout.removeWidget(self.ui.widget_Board) self.ui.verticalLayout.removeWidget(self.ui.label_Book) self.ui.widget_Board.setVisible(False) self.ui.label_Book.setVisible(False) # self.ui.verticalLayout.removeItem(self.spacerItem) self.ui.verticalLayout.addWidget(self.ui.listWidget, 1) self.ui.listWidget.show() self._isEmpty = False for i in range(number): item = QListWidgetItem() # item.setText(str(self.ui.listWidget.count())) item.setSizeHint(self.getItemSize()) if fullFilename == '': roi = ReadingOrderItem(item, self.getSerialNo()) else: roi = ReadingOrderItem(fullFilename, item, self.getSerialNo()) # roi = ReadingOrderItem(item, self.getSerialNo()) roi.ui.label_Index.setText("{:d}".format( self.ui.listWidget.count())) # roi.signal_resize_item.connect(self.on_signal_resize_item) self.ui.listWidget.addItem(item) self.ui.listWidget.setItemWidget(item, roi) self._listWidgetItemSerialNo += 1 self.roiList.append(roi) # print(self.ui.listWidget.geometry()) self.getDuration() @Slot() def removeSelectedItems(self): items = self.ui.listWidget.selectedItems() for item in items: oldItem = self.ui.listWidget.takeItem(self.ui.listWidget.row(item)) del oldItem if self.ui.listWidget.count() == 0: # self.spacerItem = QSpacerItem(420, 10, QSizePolicy.Fixed, QSizePolicy.Expanding) self.ui.verticalLayout.removeWidget(self.ui.listWidget) self.ui.listWidget.hide() self.ui.verticalLayout.addWidget(self.ui.widget_Board) self.ui.widget_Board.show() self.ui.verticalLayout.addSpacerItem( QSpacerItem(420, 10, QSizePolicy.Fixed, QSizePolicy.Expanding)) self.ui.verticalLayout.addWidget(self.ui.label_Book) self.ui.label_Book.show() self._isEmpty = True self.resortItems() self.getDuration() def removeItem(self, item): logging.debug("removeItem is called") itemWidget = self.ui.listWidget.itemWidget(item) if itemWidget is not None: self.roiList.remove(itemWidget) del itemWidget oldItem = self.ui.listWidget.takeItem(self.ui.listWidget.row(item)) # itemWidget = self.ui.listWidget.itemWidget(oldItem) del oldItem if self.ui.listWidget.count() == 0: self.ui.verticalLayout.removeWidget(self.ui.listWidget) self.ui.listWidget.hide() self.ui.verticalLayout.addWidget(self.ui.widget_Board) self.ui.widget_Board.show() self.ui.verticalLayout.addSpacerItem( QSpacerItem(420, 10, QSizePolicy.Fixed, QSizePolicy.Expanding)) self.ui.verticalLayout.addWidget(self.ui.label_Book) self.ui.label_Book.show() self._isEmpty = True self.resortItems() self.getDuration() def resortItems(self): for i in range(self.ui.listWidget.count()): # self.ui.listWidget.item(i).setText(str(i)) itemWidget = self.ui.listWidget.itemWidget( self.ui.listWidget.item(i)) itemWidget.ui.label_Index.setText("{:d}".format(i)) def save(self): readingOrderList = [] seconds_Duration = 0.0 for i in range(self.ui.listWidget.count()): # item = self.listWidget.item(i) item = self.ui.listWidget.itemWidget(self.ui.listWidget.item(i)) # url = item.ui.label_Href.text() url = item.getFullFilename() if url == "*.mp3": continue mime, encoding = mimetypes.guess_type(url, strict=False) extension = mimetypes.guess_extension(mime, strict=False) # kind = filetype.guess(url) book = Audiobook.getInstance() # kind = filetype.guess(bookDir + r'/' + url) if mime is None: logging.warning('Cannot guess file type!') readingOrderList.append({ "url": (url.replace(book.getBookDir() + "/", ""), url)[url.startswith('http')] }) else: logging.debug('File extension: %s' % extension) logging.debug('File MIME type: %s' % mime) if extension == ".mp3" and mime.startswith("audio"): seconds_Duration += item.getDuration() # _seconds logging.debug("url = " + url) readingOrderList.append({ "url": (url.replace(book.getBookDir() + "/", ""), url)[url.startswith('http')], "encodingFormat": mime, "name": item.ui.lineEdit.text(), "duration": "PT" + str(item.getDuration()) + "S" }) return readingOrderList def add_Resource_to_TOC(self, fullFilename, title): logging.debug('add_Resource_to_TOC') self.signal_Add_Resource_to_TOC.emit(fullFilename, title) def afterDrop(self, parent, start, end, destination, row): # super(ReadingOrderWidget, self).dropEvent(event) logging.debug('afterDrop') self._afterDrop = True self.resortItems() def changeEvent(self, event): """Handle LanguageChange event""" if event.type() == QEvent.LanguageChange: logging.debug("Language changed") self.ui.retranslateUi(self) self.ui.pushButton_Add.setToolTip( "<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" "font-weight: 400;font-size: 14px;line-height: 20px;border: 0px;\">" + self._translate("ReadingOrderWidget", "Add voice to Reading Order") + "</p>") super().changeEvent(event) def eventFilter(self, sender, event): if not (event.type() in [ QEvent.Paint, QEvent.Leave, QEvent.Enter, QEvent.FocusAboutToChange, QEvent.Timer ]): logging.debug(event.type()) if event.type() == QEvent.DragEnter: logging.debug("QEvent.DragEnter") logging.debug(sender) logging.debug(event.source()) logging.debug(event.mimeData().formats()) # It's a ReadingOrderItem, not a SupplementalListWidgetItem # if sender is self.ui.listWidget: if isinstance(event.source(), ReadingOrderItem): # self.ui.listWidget.setDragDropMode(QAbstractItemView.InternalMove) event.setDropAction(Qt.MoveAction) event.acceptProposedAction() return True # if event.mimeData().hasText() and event.mimeData().hasUrls(): # logging.debug(event.mimeData().text()) # for url in event.mimeData().urls(): # logging.debug(url.toString()) # # event.acceptProposedAction() # self.dragEnterEvent(event) # return True # It's a SupplementalListWidgetItem, not a ReadingOrderItem elif isinstance( event.source(), SupplementalListWidgetItem ): # not event.mimeData().hasText() and event.mimeData().hasUrls(): # event.setDropAction(Qt.CopyAction) # event.acceptProposedAction() event.ignore() return True else: super().eventFilter(sender, event) return True elif event.type() == QEvent.Drop: logging.debug("QEvent.Drop") logging.debug(sender) logging.debug(event.source()) logging.debug(event.mimeData().formats()) # It's a ReadingOrderItem, not a SupplementalListWidgetItem if isinstance(event.source(), ReadingOrderItem): event.acceptProposedAction() # target = self.ui.listWidget.itemAt(event.pos()) # sources = self.ui.listWidget.selectedItems() # logging.debug(event.pos()) # logging.debug(target) # logging.debug(sources) selection = self.ui.listWidget.selectedIndexes() from_index = selection[0].row() if selection else -1 to_index = self.ui.listWidget.indexAt(event.pos()).row() if (0 <= from_index < self.ui.listWidget.model().rowCount() and 0 <= to_index < self.ui.listWidget.model().rowCount() and from_index != to_index): if from_index < to_index: for i in range(from_index, to_index): result = self.ui.listWidget.model().moveRow( self.ui.listWidget.rootIndex(), i + 1, self.ui.listWidget.rootIndex(), i) logging.debug("from {} to {} : {}".format( from_index, to_index, ("Fail", "Succeed")[result])) else: result = self.ui.listWidget.model().moveRow( self.ui.listWidget.rootIndex(), from_index, self.ui.listWidget.rootIndex(), to_index) logging.debug("from {} to {} : {}".format( from_index, to_index, ("Fail", "Succeed")[result])) event.accept() return True # It's a SupplementalListWidgetItem, not a ReadingOrderItem elif isinstance(event.source(), SupplementalListWidgetItem): event.ignore() return True else: super().eventFilter(sender, event) return True # elif event.type() == QEvent.ChildRemoved: # sender == self.ui.listWidget and # logging.debug("QEvent.ChildRemoved") # # self.ui.listWidget.takeItem(self.ui.listWidget.currentRow()) # logging.debug("SN={}".format(self.getSerialNo())) # logging.debug("listCount={}".format(self.ui.listWidget.count())) # logging.debug("currentRow={}".format(self.ui.listWidget.currentRow())) # # if not self._afterDrop: # itemWidget = self.ui.listWidget.itemWidget(self.ui.listWidget.currentItem()) # if itemWidget is None: # logging.debug("itemWidget is None") # roiList = self.roiList.copy() # itemWOWidget = -1 # for i in range(self.ui.listWidget.count()): # item = self.ui.listWidget.item(i) # itemWidget = self.ui.listWidget.itemWidget(item) # if itemWidget is not None: # roiList.remove(itemWidget) # else: # itemWOWidget = i # if len(roiList) == 1: # super().eventFilter(sender, event) # oldROI = roiList[0] # roi = ReadingOrderItem(oldROI.getFullFilename(), # self.ui.listWidget.item(itemWOWidget), # self.getSerialNo()) # roi.ui.lineEdit.setText(oldROI.ui.lineEdit.text()) # self._listWidgetItemSerialNo += 1 # self.ui.listWidget.setItemWidget(self.ui.listWidget.item(itemWOWidget), roi) # self.roiList.remove(oldROI) # self.roiList.append(roi) # self.resortItems() # return True # else: # self._afterDrop = False # return False return super().eventFilter(sender, event)
class SupplementalListWidgetWithWidgets(QWidget): signal_Add_Resource_to_TOC = Signal(str, str) signal_Duration_Changed = Signal(float) str_LastOpenedDirectory = None @dispatch(QMainWindow) def __init__(self, mainWindow=None, width=240, height=46): super(SupplementalListWidgetWithWidgets, self).__init__() self.mainWindow = mainWindow self._itemWidth = width self._itemHeight = height self.ui = Ui_SupplementalListWidgetWithWidgets() self.ui.setupUi(self) self.ui.listWidget.setEnabled(False) self.ui.gridLayout.setContentsMargins(0, 0, 0, 0) self.ui.verticalLayout.setContentsMargins(0, 0, 0, 0) self._isEmpty = True self._listWidgetItemSerialNo = 0 self._translate = QCoreApplication.translate self.ui.pushButton_Add.setToolTip( "<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" "font-weight: 400;font-size: 14px;line-height: 20px;border: 0px;\">" + self._translate("SupplementalListWidgetWithWidgets", "Add more files") + "</p>") self.mask = None # self.maskCount = 0 self.afw = None self.alert = None self.alertwithbuttons = None @dispatch(list, QMainWindow) def __init__(self, sList, mainWindow=None, width=240, height=46): super(SupplementalListWidgetWithWidgets, self).__init__() self.mainWindow = mainWindow self.ui = Ui_SupplementalListWidgetWithWidgets() self.ui.setupUi(self) self.ui.gridLayout.setContentsMargins(0, 0, 0, 0) self.ui.verticalLayout.setContentsMargins(0, 0, 0, 0) if len(sList) > 0: self.ui.label_NoItem.setVisible(False) self.ui.listWidget.setEnabled(True) self._isEmpty = False else: self.ui.label_NoItem.setVisible(True) self.ui.listWidget.setEnabled(False) self._isEmpty = True self._listWidgetItemSerialNo = 0 # Unique index for # self.ui.listWidget.setDragDropMode(QAbstractItemView.InternalMove) # self.ui.listWidget.setContextMenuPolicy(Qt.CustomContextMenu) # self.ui.listWidget.customContextMenuRequested[QPoint].connect(self.on_SupplementalListWidgetContextMenu_triggered) self._itemWidth = width self._itemHeight = height self._translate = QCoreApplication.translate self.ui.pushButton_Add.setToolTip( "<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" "font-weight: 400;font-size: 14px;line-height: 20px;border: 0px;\">" + self._translate("SupplementalListWidgetWithWidgets", "Add more files") + "</p>") self.mask = None self.afw = None self.alert = None self.alertwithbuttons = None book = Audiobook.getInstance() for s in sList: url = s.get("url", "") if url != "": item = QListWidgetItem() # item.setText(str(self.ui.listWidget.count())) item.setSizeHint(self.getItemSize()) roi = SupplementalListWidgetItem( (book.getBookDir() + '/' + url, url)[url.startswith('http')], item, self.getSerialNo()) self.ui.listWidget.addItem(item) self.ui.listWidget.setItemWidget(item, roi) self._listWidgetItemSerialNo += 1 def _openAlertWithButtonsWindow(self, str_Msg, str_Title): self.mask = MaskWidget(self.mainWindow) self.mask.show() self.alertwithbuttons = AlertWithButtons(str_Msg, str_Title) self.alertwithbuttons.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.alertwithbuttons.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.alertwithbuttons.geometry().height() / 2) result = self.alertwithbuttons.exec_() self.mask.close() return result def _openAlertWindow(self, str_Msg, str_Title): self.mask = MaskWidget(self.mainWindow) self.mask.show() self.alert = Alert(str_Msg, str_Title) self.alert.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.alert.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.alert.geometry().height() / 2) result = self.alert.exec_() self.mask.close() return result def getAttachFromWidgetResult(self, bool_Result, list_Result): logging.debug(str(bool_Result) + ' ' + ";".join(list_Result)) book = Audiobook.getInstance() set_url = book.getCheckSet(self.mainWindow) filenames = [] if not bool_Result: filenames = list_Result book.checkAttachedFile(filenames, set_url, SupplementalListWidgetWithWidgets, self) @Slot() def on_pushButton_Add_clicked(self): logging.debug('on_pushButton_Add_clicked') if self._isEmpty: self.ui.label_NoItem.setVisible(False) self.ui.listWidget.setEnabled(True) self._isEmpty = False book = Audiobook.getInstance() self.mask = MaskWidget(self.mainWindow) self.mask.show() self.afw = AttachFromWidgetWithoutURL(False, 'Open files', ( SupplementalListWidgetWithWidgets.str_LastOpenedDirectory, book.getBookDir() + '/' )[SupplementalListWidgetWithWidgets.str_LastOpenedDirectory is None], "Any file(*.*)") self.afw.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.afw.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.afw.geometry().height() / 2) result = self.afw.exec_() filenames = self.afw.getURLs() self.mask.close() if result == QDialog.Accepted and filenames: self.getAttachFromWidgetResult(False, filenames) else: return @Slot(bool) def on_removeItemAction_triggered(self, checked): logging.debug("removeItem is clicked") self.removeSelectedItems() self.resortItems() def removeItem(self, item): logging.debug("removeItem is called") oldItem = self.ui.listWidget.takeItem(self.ui.listWidget.row(item)) del oldItem if self.ui.listWidget.count() == 0: self.ui.label_NoItem.setVisible(True) self.ui.listWidget.setEnabled(False) self._isEmpty = True def getItemSize(self): return QSize(self._itemWidth, self._itemHeight) def getSerialNo(self): return self._listWidgetItemSerialNo def addItems(self, fullFilename='', number=1): logging.debug('addItems() is called') for i in range(number): item = QListWidgetItem() # item.setText(str(self.ui.listWidget.count())) item.setSizeHint(self.getItemSize()) if fullFilename == '': roi = SupplementalListWidgetItem(item, self.getSerialNo()) else: roi = SupplementalListWidgetItem(fullFilename, item, self.getSerialNo()) self.ui.listWidget.addItem(item) self.ui.listWidget.setItemWidget(item, roi) self._listWidgetItemSerialNo += 1 def removeSelectedItems(self): items = self.listWidget.selectedItems() for item in items: oldItem = self.listWidget.takeItem(self.listWidget.row(item)) QFile.remove(item.getFullFilename()) del oldItem def resortItems(self): for i in range(self.listWidget.count()): self.listWidget.item(i).setText(str(i)) def save(self): supplementalList = [] for i in range(self.ui.listWidget.count()): item = self.ui.listWidget.itemWidget(self.ui.listWidget.item(i)) url = item.getFullFilename() # item.ui.lineEdit.text() if url == "": continue book = Audiobook.getInstance() # kind = filetype.guess(book.getBookDir() + '/' + url) mime, encoding = mimetypes.guess_type(url, strict=False) if mime is None: logging.warning('Cannot guess file type!') supplementalList.append({ "url": (url.replace(book.getBookDir() + "/", ""), url)[url.startswith('http')] }) else: extension = mimetypes.guess_extension(mime) logging.debug('File extension: %s' % extension) logging.debug('File MIME type: %s' % mime) supplementalList.append({ "url": url.replace(book.getBookDir() + "/", ""), "encodingFormat": mime }) return supplementalList def add_Resource_to_TOC(self, fullFilename, _): logging.debug('add_Resource_to_TOC') self.signal_Add_Resource_to_TOC.emit(fullFilename, "") # def mouseDoubleClickEvent(self, event): # logging.debug(event.localPos()) def changeEvent(self, event): """Handle LanguageChange event""" if event.type() == QEvent.LanguageChange: logging.debug("Language changed") self.ui.retranslateUi(self) self.ui.pushButton_Add.setToolTip( "<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" "font-weight: 400;font-size: 14px;line-height: 20px;border: 0px;\">" + self._translate("SupplementalListWidgetWithWidgets", "Add more files") + "</p>") super().changeEvent(event)
class CoverPreviewWidget(QWidget): switch_window = Signal() @dispatch(dict, str, QMainWindow) def __init__(self, dict_Cover, dir_Book, mainWindow=None): super(CoverPreviewWidget, self).__init__() self.mainWindow = mainWindow self.ui = Ui_CoverPreviewWidget() self.ui.setupUi(self) self.href = "" url = dict_Cover.get("url", "") if url == "" or url.endswith( ".html"): # For now, just "bypass" .html cover # self.href = dir_Book + "/" + url self.href = "" self.ui.pushButton_AddCover.setVisible(True) self.ui.pushButton_Remove.setVisible(False) self.ui.pushButton_Replace.setVisible(False) else: # try: self.href = dir_Book + "/" + url if not os.path.exists(self.href): self.ui.label_Cover.setPixmap( QPixmap(":/SVG/svg/icon/cover-preview-empty.svg")) self.href = '' self.ui.pushButton_Remove.setVisible(False) self.ui.pushButton_Replace.setVisible(False) self.ui.pushButton_AddCover.setVisible(True) else: pixmap = QPixmap(self.href) w, h = pixmap.size().width(), pixmap.size().height() img = pixmap.scaledToWidth(120, Qt.SmoothTransformation) \ if w > h else pixmap.scaledToHeight(120, Qt.SmoothTransformation) self.coverFilename = self.href self.ui.label_Cover.setFixedSize(img.size()) self.ui.label_Cover.setPixmap(img) # pixmap = pixmap.scaled(self.ui.label_Cover.size(), Qt.KeepAspectRatio) # self.ui.label_Cover.setPixmap(pixmap) self.ui.pushButton_Remove.setVisible(True) self.ui.pushButton_Replace.setVisible(True) self.ui.pushButton_AddCover.setVisible(False) # except Exception as ex: # logging.critical(ex) # self.href = '' # self.ui.pushButton_AddCover.setVisible(True) # self.ui.pushButton_Remove.setVisible(False) # self.ui.pushButton_Replace.setVisible(False) self.ui.pushButton_AddCover.clicked.connect( lambda: self.on_pushButton_clicked(self.ui.pushButton_AddCover)) self.ui.pushButton_Replace.clicked.connect( lambda: self.on_pushButton_clicked(self.ui.pushButton_Replace)) self.ui.pushButton_Remove.clicked.connect( lambda: self.on_pushButton_clicked(self.ui.pushButton_Remove)) self._translate = QCoreApplication.translate # self.ui.pushButton_AddCover.setToolTip("<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" # "font-weight: 400;font-size: 14px;line-height: 20px;" # "border: 0px #404040;\">" + # self._translate("CoverPreviewWidget", "Add a picture as cover") + # "</p>") # self.ui.pushButton_Replace.setToolTip("<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" # "font-weight: 400;font-size: 14px;line-height: 20px;" # "border: 0px #404040;\">" + # self._translate("CoverPreviewWidget", "Replace this cover") + # "</p>") # self.ui.pushButton_Remove.setToolTip("<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" # "font-weight: 400;font-size: 14px;line-height: 20px;" # "border: 0px #404040;\">" + # self._translate("CoverPreviewWidget", "Remove this cover") + # "</p>") self.mask = None self.alert = None self.afw = None @dispatch(QMainWindow) def __init__(self, mainWindow=None): super(CoverPreviewWidget, self).__init__() self.mainWindow = mainWindow self.ui = Ui_CoverPreviewWidget() self.ui.setupUi(self) self.href = "" self.ui.pushButton_AddCover.setVisible(True) self.ui.pushButton_Remove.setVisible(False) self.ui.pushButton_Replace.setVisible(False) self.ui.pushButton_AddCover.clicked.connect( lambda: self.on_pushButton_clicked(self.ui.pushButton_AddCover)) self.ui.pushButton_Replace.clicked.connect( lambda: self.on_pushButton_clicked(self.ui.pushButton_Replace)) self.ui.pushButton_Remove.clicked.connect( lambda: self.on_pushButton_clicked(self.ui.pushButton_Remove)) self._translate = QCoreApplication.translate # self.ui.pushButton_AddCover.setToolTip("<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" # "font-weight: 400;font-size: 14px;line-height: 20px;" # "border: 0px #404040;\">" + # self._translate("CoverPreviewWidget", "Add a picture as cover") + # "</p>") # self.ui.pushButton_Replace.setToolTip("<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" # "font-weight: 400;font-size: 14px;line-height: 20px;" # "border: 0px #404040;\">" + # self._translate("CoverPreviewWidget", "Replace this cover") + # "</p>") # self.ui.pushButton_Remove.setToolTip("<p style=\"color:#FFFFFF;font-family: Noto Sans;font-style: normal;" # "font-weight: 400;font-size: 14px;line-height: 20px;" # "border: 0px #404040;\">" + # self._translate("CoverPreviewWidget", "Remove this cover") + # "</p>") self.mask = None self.alert = None self.afw = None def _openAlertWithButtonsWindow(self, str_Msg, str_Title): self.mask = MaskWidget(self.mainWindow) self.mask.show() self.alertwithbuttons = AlertWithButtons(str_Msg, str_Title) self.alertwithbuttons.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.alertwithbuttons.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.alertwithbuttons.geometry().height() / 2) result = self.alertwithbuttons.exec_() self.mask.close() return result def _openAlertWindow(self, str_Msg, str_Title): self.mask = MaskWidget(self.mainWindow) self.mask.show() self.alert = Alert(str_Msg, str_Title) self.alert.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.alert.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.alert.geometry().height() / 2) result = self.alert.exec_() self.mask.close() return result def getAttachFromWidgetResult(self, bool_Result, str_Result): logging.debug(str(bool_Result) + ' ' + str_Result) book = Audiobook.getInstance() option, list_compulsory = book.determineOption() url_compulsory = [ book.getBookDir() + '/' + f for f in list_compulsory if not f.startswith('http') ] url_orderingOrder = [ book.getBookDir() + '/' + f["url"] for f in self.mainWindow.readingOrderWidget.save() if not f["url"].startswith('http') ] url_toc = [ book.getBookDir() + '/' + f["href"] for f in self.mainWindow.tocWidget.save(True) if not f["href"].startswith('http') ] url_cover = [ book.getBookDir() + '/' + f["url"] for f in [self.save()] if not f["url"].startswith('http') ] url_supplemental = [ book.getBookDir() + '/' + f["url"] for f in self.mainWindow.supplementalListWidget.save() if not f["url"].startswith('http') ] set_url = set(url_compulsory + url_orderingOrder + url_toc + url_cover + url_supplemental) filename = "" if not bool_Result: filename = str_Result file_folder, file_name = os.path.split(filename) if filename in set_url: logging.debug( "You have chosen one file already in this audiobook") self._openAlertWindow( self._translate( "CoverPreviewWidget", 'You have chosen one file already in this audiobook!'), self._translate("CoverPreviewWidget", "Warning")) filename = "" return elif book.getBookDir() + '/' + file_name in set_url: logging.debug( "One file with the same name is already in this audiobook") result = self._openAlertWithButtonsWindow( self._translate( "CoverPreviewWidget", 'One file with the same name is already ' 'in this audiobook! ' 'Do you want to overwrite ' 'that original file?'), self._translate("CoverPreviewWidget", "Warning")) if result: try: shutil.copyfile(filename, book.getBookDir() + '/' + file_name) filename = book.getBookDir() + '/' + file_name except Exception as ex: logging.critical(ex) self._openAlertWindow( self._translate( "CoverPreviewWidget", 'Sorry, we couldn\'t overwrite the original file!' ), self._translate("CoverPreviewWidget", "Warning")) return else: filename = "" return elif file_folder == book.getBookDir(): pass else: pass src = QFile(filename) # info = QFileInfo(filename) if file_folder != book.getBookDir(): try: shutil.copyfile(filename, book.getBookDir() + '/' + file_name) filename = book.getBookDir() + '/' + file_name except Exception as ex: logging.critical(ex) self._openAlertWindow( self._translate( "CoverPreviewWidget", 'Sorry, we couldn\'t copy this file ' 'to the audiobook folder!'), self._translate("CoverPreviewWidget", "Warning")) return else: filename = book.getBookDir() + '/' + file_name img = QPixmap(filename) w, h = img.size().width(), img.size().height() img = img.scaledToWidth(120, Qt.SmoothTransformation) \ if w > h else img.scaledToHeight(120, Qt.SmoothTransformation) # self.ui.label_Cover.setFixedSize(img.size()) # self.ui.label_Cover.setPixmap(img) if self.href: bool_Removed = QFile.remove(self.href) if not bool_Removed: self._openAlertWindow( self._translate( "CoverPreviewWidget", 'We couldn\'t remove the old cover for you!'), self._translate("CoverPreviewWidget", "Warning")) # return self.ui.label_Cover.setFixedSize(img.size()) self.ui.label_Cover.setPixmap(img) self.href = filename self.ui.pushButton_Remove.setVisible(True) self.ui.pushButton_Replace.setVisible(True) self.ui.pushButton_AddCover.setVisible(False) def _getOpenFilename(self, bool_Flag, str_Title, str_Path, str_FileTypes): logging.debug('_getOpenFilename') self.mask = MaskWidget(self.mainWindow) self.mask.show() self.afw = AttachFromWidgetWithoutURL(bool_Flag, str_Title, str_Path, str_FileTypes) self.afw.move( self.mainWindow.geometry().x() + self.mainWindow.geometry().width() / 2 - self.afw.geometry().width() / 2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height() / 2 - self.afw.geometry().height() / 2) result = self.afw.exec_() filename = self.afw.getURL() self.mask.close() return result, filename def on_pushButton_clicked(self, sender): book = Audiobook.getInstance() if sender.objectName() == "pushButton_AddCover": logging.debug('button_AddCover is clicked') filename, _ = QFileDialog.getOpenFileName( self, self._translate("CoverPreviewWidget", 'Open file'), book.getBookDir() + '/', "JPG Files (*.jpg);;PNG Files (*.png)") # result, filename = self._getOpenFilename(False, # 'Open file', # book.getBookDir() + '/', # "JPG Files (*.jpg);;PNG Files (*.png)") if filename: self.getAttachFromWidgetResult(False, filename) else: return elif sender.objectName() == 'pushButton_Remove': logging.debug('button_Remove is clicked') bool_removed = QFile.remove(self.href) self.ui.label_Cover.setFixedSize(120, 120) self.ui.label_Cover.setPixmap( QPixmap(":/SVG/svg/icon/cover-preview-empty.svg")) self.href = '' self.ui.pushButton_Remove.setVisible(False) self.ui.pushButton_Replace.setVisible(False) self.ui.pushButton_AddCover.setVisible(True) elif sender.objectName() == 'pushButton_Replace': logging.debug('button_Replace is clicked') filename, _ = QFileDialog.getOpenFileName( self, self._translate("CoverPreviewWidget", 'Open file'), book.getBookDir() + '/', "JPG Files (*.jpg);;PNG Files (*.png)") if filename: self.getAttachFromWidgetResult(False, filename) else: return def save(self): book = Audiobook.getInstance() # kind = filetype.guess(self.href) mime, _ = mimetypes.guess_type(self.href) extension = mimetypes.guess_extension(self.href) if mime is not None: dict_cover = { "url": self.href.replace(book.getBookDir() + "/", ""), "encodingFormat": mime, "name": "Cover", "rel": "cover" } else: dict_cover = { "url": "", "encodingFormat": "", "name": "Cover", "rel": "cover" } return dict_cover def changeEvent(self, event): """Handle LanguageChange event""" if event.type() == QEvent.LanguageChange: logging.debug("Language changed") self.ui.retranslateUi(self) super().changeEvent(event)
def save(self): dict_pm = dict() # Required dict_pm["name"] = self.ui.lineEdit_BookTitle.text() dict_pm["conformsTo"] = "https://www.w3.org/TR/audiobooks/" dict_pm["@context"] = ["https://schema.org", "https://www.w3.org/ns/pub-context"] # Optional ''' dict_pm["abridged"] = self.ui.comboBox_abridged.currentText() dict_pm["accessibilityFeature"] = self.ui.lineEdit_accessibilityFeature.text() dict_pm["accessibilityHazard"] = self.ui.lineEdit_accessibilityHazard.text() dict_pm["accessibilitySummary"] = self.ui.lineEdit_accessibilitySummary.text() dict_pm["accessModeSufficient"] = self.ui.lineEdit_accessModeSufficient.text() dict_pm["accessMode"] = self.ui.comboBox_accessMode.currentText() ''' dict_pm["author"] = {"type": "Person", "name": self.ui.lineEdit_Author.text()} dict_pm["duration"] = "PT" + "{:.2f}".format(self._duration) + "S" dict_pm["dateModified"] = self.ui.dateEdit_DateModified.date().toString(Qt.ISODate) dict_pm["datePublished"] = self.ui.dateEdit_DatePublished.date().toString(Qt.ISODate) dict_pm["inLanguage"] = self.ui.comboBox_inLanguage.currentText() dict_pm["readBy"] = {"type": "Person", "name": self.ui.lineEdit_ReadBy.text()} dict_pm["publisher"] = self.ui.lineEdit_Publisher.text() # dict_pm["readingProgression"] = self.ui.comboBox_readingProgression.currentText() dict_pm["type"] = 'Audiobook' dict_pm["url"] = self.ui.lineEdit_Website.text() """ if dict_pm["name"] == "" or dict_pm["author"] == "" or dict_pm["readBy"] == "": self.alert = Alert(self._translate("MetadataWidget", "Some required fields are empty!")) self.alert.show() # QMessageBox.information(self, self._translate("MetadataWidget", "Hint!"), self._translate("MetadataWidget", "Some required fields are empty!"), QMessageBox.Ok) return {} """ bool_Alert_BookTitle = False bool_Alert_Author = False bool_Alert_ReadBy = False if dict_pm["name"] == "": self.ui.lineEdit_BookTitle.setEnabled(False) bool_Alert_BookTitle = True if dict_pm["author"]["name"] == "": self.ui.lineEdit_Author.setEnabled(False) bool_Alert_Author = True if dict_pm["readBy"]["name"] == "": self.ui.lineEdit_ReadBy.setEnabled(False) bool_Alert_ReadBy = True if bool_Alert_BookTitle or bool_Alert_Author or bool_Alert_ReadBy: self.mask = MaskWidget(self.mainWindow) self.mask.show() self.alert = Alert(self._translate("MetadataWidget", "Some required fields are empty!")) # self.alert.close_alert.connect(self.closeAlert) self.alert.move(self.mainWindow.geometry().x() + self.mainWindow.geometry().width()/2 - self.alert.geometry().width()/2, self.mainWindow.geometry().y() + self.mainWindow.geometry().height()/2 - self.alert.geometry().height()/2) # self.alert.show() result = self.alert.exec_() self.closeAlert() return {} return dict_pm
class Audiobook(QObject): _PEP_OPTION_1 = """<!DOCTYPE html> <html> <head> <title>{}</title> <meta charset="utf-8"> {} <link rel=\"publication\" href=\"#{}\"> <script type=\"application/ld+json\" id=\"{}\"> {} </script> </head> <body> <nav role=\"doc-toc\"> <h1>{}</h1> {} </nav> </body> </html>""" _PEP_OPTION_2 = """<!DOCTYPE html> <html> <head> <title>{}</title> <meta charset="utf-8"> {} <link rel=\"publication\" href=\"{}\"> </head> <body> <h1>{}</h1> </body> </html>""" _TOC_OPTION_2 = """<!DOCTYPE html> <html> <head> <title>Table of Contents</title> <meta charset="utf-8"> </head> <body> <nav role=\"doc-toc\"> <h1>{}</h1> {} </nav> </body> </html>""" _PEP_OPTION_3 = """<!DOCTYPE html> <html> <head> <title>{}</title> <meta charset="utf-8"> {} <link rel=\"publication\" href=\"{}\"> </head> <body> <nav role=\"doc-toc\"> <h1>{}</h1> {} </nav> </body> </html>""" _TOC_OPTION_4 = """<!DOCTYPE html> <html> <head> <title>Table of Contents</title> <meta charset="utf-8"> </head> <body> <nav role=\"doc-toc\"> <h1>{}</h1> {} </nav> </body> </html>""" # Singleton Pattern _instance = None signal_not_W3C_compatible = Signal() @staticmethod @dispatch(dict) def getInstance(dict_New): if Audiobook._instance is None: Audiobook(dict_New) return Audiobook._instance @staticmethod @dispatch(str) def getInstance(item_Open): if Audiobook._instance is None: Audiobook(item_Open) return Audiobook._instance @staticmethod @dispatch() def getInstance(): if Audiobook._instance is None: Audiobook({}) return Audiobook._instance ''' {"saveDir": saveDir, "bookTitle": bookTitle, "author": author, "publisher": publisher, "readBy": readBy} ''' @dispatch(dict) def __init__(self, dict_New): super().__init__(None) # logging.basicConfig(level=logging.critical, format=LOGGING_FORMAT, datefmt=LOGGING_DATE_FORMAT) if Audiobook._instance is not None: raise Exception('Only one instance of Book should exist!') return Audiobook._instance = self self._translate = QCoreApplication.translate self._id = id(self) self._optionNo = 1 self._loaded = False self._dirty = False self._PEP_File = '' self._CSS_File_List = [] self._TOC = '' self._TOC_File = '' self._TOC_List = [] self._MANIFEST = {} self._MANIFEST_File = '' # self._MANIFEST_List = [] self._Reading_Order_List = [] self._MANIFEST_ID = 'manifest' self.is_LPF = False # 21 keys self.CONST_MANIFEST_KEY_LIST = [ '@context', 'conformsTo', 'type', 'id', 'url', 'name', 'author', 'readBy', 'abridged', 'accessMode', 'accessModeSufficient', 'accessibilityFeature', 'accessibilityHazard', 'accessibilitySummary', 'dateModified', 'datePublished', 'inLanguage', 'readingProgression', 'duration', 'readingOrder', 'resources' ] self._BOOK_DIR = dict_New.get("saveDir", "") self._Booktitle = dict_New.get("bookTitle", "") self._MANIFEST["name"] = dict_New.get("bookTitle", "") self._MANIFEST["author"] = dict_New.get("author", "") self._MANIFEST["publisher"] = dict_New.get("publisher", "") self._MANIFEST["readBy"] = dict_New.get("readBy", "") self.settings = QSettings() self.recentPackToLPFDirectoryInSettings = self.settings.value( 'RecentPackToLPFDirectory', QStandardPaths.DocumentsLocation, type=str) @dispatch(str) def __init__(self, item_Open): super().__init__(None) logging.debug('@dispatch(str)') if Audiobook._instance is not None: raise Exception('Only one instance of Book should exist!') return Audiobook._instance = self self._translate = QCoreApplication.translate self._id = id(self) self._optionNo = 1 self._loaded = False self._dirty = False self._PEP_File = '' self._CSS_File_List = [] self._TOC = '' self._TOC_File = '' self._TOC_List = [] self._MANIFEST = {} self._MANIFEST_File = '' # self._Reading_Order_List = [] self._Booktitle = '' self._MANIFEST_ID = 'manifest' # 21 keys self.CONST_MANIFEST_KEY_LIST = [ '@context', 'conformsTo', 'type', 'id', 'url', 'name', 'author', 'readBy', 'abridged', 'accessMode', 'accessModeSufficient', 'accessibilityFeature', 'accessibilityHazard', 'accessibilitySummary', 'dateModified', 'datePublished', 'inLanguage', 'readingProgression', 'duration', 'readingOrder', 'resources' ] self.signal_not_W3C_compatible.connect(self.notW3CCompatible) if os.path.isdir(item_Open): logging.debug(item_Open + " is a directory") self._is_LPF = False self._LPF_File = '' self._BOOK_DIR = item_Open self.openFromDirectory(item_Open) elif os.path.isfile(item_Open): logging.debug(item_Open + " is a normal file") filename, file_extension = os.path.splitext(item_Open) if file_extension == '.lpf': self._is_LPF = True self._LPF_File = item_Open self.openFromLPF( item_Open, QStandardPaths.writableLocation( QStandardPaths.CacheLocation) + '/' + QUuid.createUuid().toString()) self.openFromDirectory(self.getBookDir()) self.settings = QSettings() self.recentPackToLPFDirectoryInSettings = self.settings.value( 'RecentPackToLPFDirectory', QStandardPaths.DocumentsLocation, type=str) @property def dirty(self): return self._dirty @dirty.setter def dirty(self, value): self._dirty = value @dirty.deleter def dirty(self): del self._dirty @property def loaded(self): return self._loaded @loaded.setter def loaded(self, value): self._loaded = value @loaded.deleter def loaded(self): del self._loaded @property def is_LPF(self): return self._is_LPF @is_LPF.setter def is_LPF(self, value): self._is_LPF = value @is_LPF.deleter def is_LPF(self): del self._is_LPF def __del__(self): logging.debug("Audiobook: del is called!") pass def resetToOption_1(self): self._optionNo = 1 self._loaded = False self._dirty = False self._PEP_File = '' self._CSS_File_List = [] self._TOC = '' self._TOC_File = '' self._TOC_List = [] self._MANIFEST = {} self._MANIFEST_File = '' # self._Reading_Order_List = [] self._Booktitle = '' self._MANIFEST_ID = 'manifest' # 21 keys self.CONST_MANIFEST_KEY_LIST = [ '@context', 'conformsTo', 'type', 'id', 'url', 'name', 'author', 'readBy', 'abridged', 'accessMode', 'accessModeSufficient', 'accessibilityFeature', 'accessibilityHazard', 'accessibilitySummary', 'dateModified', 'datePublished', 'inLanguage', 'readingProgression', 'duration', 'readingOrder', 'resources' ] @Slot() def notW3CCompatible(self): logging.critical("This is not a W3C Compatible Audiobook!") logging.debug("We're going to NEW a book!") self.resetToOption_1() def getLPFFilename(self): return self._LPF_File def getBookDir(self): return self._BOOK_DIR def getID(self): return self._id def setManifestDict(self, m): self._MANIFEST = m def getManifestDict(self): return self._MANIFEST def getTOCFile(self): return self._TOC_File def setTOCList(self, t): self._TOC_List = t def getTOCList(self): return self._TOC_List def setCoverDict(self, dict_Cover): if self._MANIFEST.get("resources", None) is None: self._MANIFEST["resources"] = [dict_Cover] return for i in len(self._MANIFEST["resources"]): if self._MANIFEST["resources"][i].get("rel", None) is not None and \ self._MANIFEST["resources"][i].get("rel") == "cover": self._MANIFEST["resources"].remove(i) break self._MANIFEST["resources"].append(dict_Cover) def getCoverDict(self): if 'resources' in self.getManifestDict(): for item in self.getManifestDict()["resources"]: if item.get("rel") is not None and item.get("rel") == "cover": return item return {} def setSupplementalList(self, sList): if self._MANIFEST.get("resources", None) is None: self._MANIFEST["resources"] = sList return for s in sList: self._MANIFEST["resources"].append(s) def getSupplementalList(self): slist = [] if 'resources' in self.getManifestDict(): for item in self.getManifestDict()["resources"]: if item.get("rel") is not None and item.get("rel") == "cover": continue elif item.get("name") is not None and item.get( "name") == "Primary Entry Page": continue elif item.get("rel") is not None and item.get("rel") == "contents" and \ item.get("url") is not None and item.get("url") == "index.html": continue elif item.get("name") is not None and item.get( "name") == "Table of Contents": continue else: slist.append(item) return slist def setReadingOrderList(self, rList): self._MANIFEST["readingOrder"] = rList def getReadingOrderList(self): if 'readingOrder' in self.getManifestDict(): return self.getManifestDict()['readingOrder'] return [] def on_action_Pack_triggered(self, mainWindow): logging.debug("Save as a LPF file!") fdlg = QFileDialog() fdlg.setFileMode(QFileDialog.AnyFile) self.recentPackToLPFDirectoryInSettings = self.settings.value( 'RecentPackToLPFDirectory', QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation), type=str) saveLPFName, _ = fdlg.getSaveFileName( None, self._translate('Book', 'Save As Audiobook(lpf)'), self.recentPackToLPFDirectoryInSettings, self._translate('Book', 'Audiobooks (*.lpf)')) if saveLPFName: logging.debug(saveLPFName) if os.path.split( saveLPFName)[0] != self.recentPackToLPFDirectoryInSettings: self.settings.setValue('RecentPackToLPFDirectory', os.path.split(saveLPFName)[0]) self.saveAsLPF(saveLPFName, mainWindow) return def _openAlertWindow(self, str_Msg, str_Title, bgWindow): self.mask = MaskWidget(bgWindow) self.mask.show() self.alert = Alert(str_Msg, str_Title) self.alert.move( bgWindow.geometry().x() + bgWindow.geometry().width() / 2 - self.alert.geometry().width() / 2, bgWindow.geometry().y() + bgWindow.geometry().height() / 2 - self.alert.geometry().height() / 2) result = self.alert.exec_() self.mask.close() return result def saveAsLPF(self, lpf_file, bgWindow=None): filesToBePacked = set() def addFilesToBePacked(data): # data = self.getTOCList() for i in range(len(data)): tocDict = data[i] level = tocDict["level"] href = tocDict["href"] if not href.startswith('http'): pos = href.find('#t=') if pos == -1: filesToBePacked.add(href) else: filesToBePacked.add(href[0:pos]) addFilesToBePacked(data[i]['children']) data = self.getTOCList() addFilesToBePacked(data) for i in range(len(self._MANIFEST["readingOrder"])): if self._MANIFEST["readingOrder"][i].get("url", None) is not None and \ not self._MANIFEST["readingOrder"][i].get("url", None).startswith('http'): file = self._MANIFEST["readingOrder"][i].get("url", None) filesToBePacked.add(file) for i in range(len(self._MANIFEST["resources"])): if self._MANIFEST["resources"][i].get("url", None) is not None and \ not self._MANIFEST["resources"][i].get("url", None).startswith('http'): file = self._MANIFEST["resources"][i].get("url", None) filesToBePacked.add(file) for css in self._CSS_File_List: if not css.startswith('http'): file = css filesToBePacked.add(file) if not self._MANIFEST_File == '': filesToBePacked.add(self._MANIFEST_File) filesToBePacked = set(filesToBePacked) save_zip_file = zipfile.ZipFile(lpf_file, mode='w') for f in filesToBePacked: logging.debug( os.path.join(self.getBookDir(), f).replace('\\', '/')) if os.path.join(self.getBookDir(), f).replace('\\', '/') == lpf_file: continue mime, _ = mimetypes.guess_type( os.path.join(self.getBookDir(), f).replace('\\', '/')) if mime is None or (not mime.startswith("audio") and not mime.startswith("video")): save_zip_file.write(os.path.join(self.getBookDir(), f).replace('\\', '/'), f, compress_type=zipfile.ZIP_DEFLATED) else: save_zip_file.write(os.path.join(self.getBookDir(), f).replace('\\', '/'), f, compress_type=zipfile.ZIP_STORED) save_zip_file.close() if bgWindow is not None: self._openAlertWindow( self._translate("Book", "Packing is finished!"), self._translate("Book", "Information"), bgWindow) def getLPFInfo(self, lpf_file): zf = zipfile.ZipFile(lpf_file) for info in zf.infolist(): logging.debug(info.filename) logging.debug('\tComment:\t', info.comment) logging.debug('\tModified:\t', datetime.datetime(*info.date_time)) logging.debug('\tSystem:\t\t', info.create_system, '(0 = Windows, 3 = Unix)') logging.debug('\tZIP version:\t', info.create_version) logging.debug('\tCompressed:\t', info.compress_size, 'bytes') logging.debug('\tUncompressed:\t', info.file_size, 'bytes') @Slot() def on_action_Save_Audiobook_triggered(self): logging.debug("on_action_Save_Audiobook_triggered") self.saveIntoDirectory(self.getBookDir()) if self.is_LPF: logging.debug("Save as a LPF file!") self.saveAsLPF(self._LPF_File) def checkResources(self): errorList = [] for resource in self._MANIFEST['resources']: if 'url' in resource.keys(): logging.debug(resource['url']) url = resource['url'] m = re.search(r'^https?://', url) if m: # http(s):// res = Helper.checkURLAvailability(url) logging.debug(res) if not res: errorList.append(url) else: # local logging.debug('not a URL') directory = self._BOOK_DIR if os.path.exists(os.path.join(directory, url)): logging.debug('{} exists'.format( os.path.join(directory, url))) else: logging.debug('{} doesn\'t exist'.format( os.path.join(directory, url))) errorList.append(os.path.join(directory, url)) return errorList def saveIntoDirectory(self, directory=None): def getTOCBlock(data, level=0): fullTOCString = '\t' * (3 + level * 2) + "<ol>\n" for i in range(len(data)): dict_TOC = data[i] lvl = dict_TOC["level"] href = dict_TOC["href"] title = dict_TOC["title"] fullTOCString += '\t' * (3 + level * 2 + 1) + \ '<li><a href=\"' + \ href + '\">' + \ title + \ '</a></li>\n' if len(dict_TOC["children"]) > 0: fullTOCString += getTOCBlock(dict_TOC["children"], level + 1) else: pass fullTOCString += '\t' * (3 + level * 2) + "</ol>" return fullTOCString logging.debug('saveIntoDirectory') errorList = self.checkResources() if not errorList: logging.debug('Resources check passed') else: logging.debug('Not-found list : {}'.format(errorList)) if directory is None: directory = self._BOOK_DIR if self._optionNo == 1: # self._PEP_File and not self._TOC_File and not self._MANIFEST_File logging.debug("self._optionNo = 1") fullManifestContent = "" data = self.getTOCList() tocString = getTOCBlock(data, 0) metadata = self.getManifestDict() """ <meta name=\"stylesheet\" src=\"{}\"> """ fullCSSContent = "" for css in self._CSS_File_List: fullCSSContent += '<meta name=\"stylesheet\" src=\"{}\">'.format( css) if self._MANIFEST.get("resources", "") == "": raise Exception("Packing Error: It seems not saved!") self._MANIFEST["resources"].append({ "url": "index.html", "encodingFormat": "text/html", "name": "Primary Entry Page", "rel": "contents" }) fullHTMLContent = self._PEP_OPTION_1.format( self._Booktitle if self._Booktitle else self._MANIFEST['name'], fullCSSContent, self._MANIFEST_ID, self._MANIFEST_ID, json.dumps(self._MANIFEST, indent=4, ensure_ascii=False), self._Booktitle if self._Booktitle else self._MANIFEST['name'], tocString) logging.debug(fullHTMLContent) with open(directory + "/" + "index.html", "w", encoding="utf-8") as f: # contents to be grabbed f.write(fullHTMLContent) elif self._optionNo == 2: logging.debug("self._optionNo = 2") # fullManifestContent = "" fullCSSContent = "" for css in self._CSS_File_List: fullCSSContent += '<meta name=\"stylesheet\" src=\"{}\">'.format( css) fullPEPContent = self._PEP_OPTION_2.format(self._Booktitle, fullCSSContent, self._MANIFEST_File, self._Booktitle) with open(directory + "/" + "index.html", "w", encoding="utf-8") as f: f.write(fullPEPContent) data = self.getTOCList() tocString = getTOCBlock(data, 0) fullTOCContent = self._TOC_OPTION_2.format(self._Booktitle, tocString) with open(directory + "/" + self._TOC_File, "w", encoding="utf-8") as f: f.write(fullTOCContent) self._MANIFEST["resources"].append({ "type": "LinkedResource", "encodingFormat": "text/html", "name": "Table of Contents", "rel": "contents", "url": self.getTOCFile() or "toc.html" }) self._MANIFEST["resources"].append({ "url": "index.html", "encodingFormat": "text/html", "name": "Primary Entry Page", "rel": "contents" }) with open(directory + "/" + self._MANIFEST_File, "w", encoding="utf-8") as f: json.dump(self._MANIFEST, f, indent=4, ensure_ascii=False) elif self._optionNo == 3: logging.debug("self._optionNo = 3") data = self.getTOCList() tocString = getTOCBlock(data, 0) # fullPEPContent = "" fullCSSContent = "" for css in self._CSS_File_List: fullCSSContent += '<meta name=\"stylesheet\" src=\"{}\">'.format( css) fullPEPContent = self._PEP_OPTION_3.format(self._Booktitle, fullCSSContent, self._MANIFEST_File, self._Booktitle, tocString) with open(directory + "/" + "index.html", "w", encoding="utf-8") as f: f.write(fullPEPContent) self._MANIFEST["resources"].append({ "url": "index.html", "encodingFormat": "text/html", "name": "Primary Entry Page", "rel": "contents" }) with open(directory + "/" + self._MANIFEST_File, "w", encoding="utf-8") as f: json.dump(self._MANIFEST, f, indent=4, ensure_ascii=False) elif self._optionNo == 4: logging.debug("self._optionNo = 4") data = self.getTOCList() tocString = getTOCBlock(data, 0) fullTOCContent = self._TOC_OPTION_4.format(self._Booktitle, tocString) with open(directory + "/" + self._TOC_File, "w", encoding="utf-8") as f: f.write(fullTOCContent) self._MANIFEST["resources"].append({ "type": "LinkedResource", "encodingFormat": "text/html", "name": "Table of Contents", "rel": "contents", "url": self.getTOCFile() or "toc.html" }) with open(directory + "/" + self._MANIFEST_File, "w", encoding="utf-8") as f: json.dump(self._MANIFEST, f, indent=4, ensure_ascii=False) elif self._optionNo == 5: logging.debug("self._optionNo = 5") with open(directory + "/" + self._MANIFEST_File, "w", encoding="utf-8") as f: json.dump(self._MANIFEST, f, indent=4, ensure_ascii=False) else: logging.debug("self._optionNo = ???") if self.dirty: self.dirty = False def checkResources(self): errorList = [] for resource in self._MANIFEST.get('resources', []): if 'url' in resource.keys(): logging.debug(resource['url']) url = resource['url'] m = re.search(r'^https?://', url) if m: # http(s):// res = self.helper.checkURLAvailability(url) logging.debug(res) if not res: errorList.append(url) else: # local logging.debug('not a URL') directory = self._BOOK_DIR if os.path.exists(directory + "/" + url): logging.debug('{} exists'.format(directory + "/" + url)) else: logging.debug('{} doesn\'t exist'.format(directory + "/" + url)) errorList.append(directory + "/" + url) return errorList def determineOption(self): s = set() if self._PEP_File and not self._TOC_File and not self._MANIFEST_File: self._optionNo = 1 s.add(self._PEP_File) elif self._PEP_File and self._TOC_File and self._MANIFEST_File: self._optionNo = 2 s.add(self._PEP_File) s.add(self._TOC_File) s.add(self._MANIFEST_File) elif self._PEP_File and not self._TOC_File and self._MANIFEST_File: self._optionNo = 3 s.add(self._PEP_File) s.add(self._MANIFEST_File) elif not self._PEP_File and self._TOC_File and self._MANIFEST_File: self._optionNo = 4 s.add(self._TOC_File) s.add(self._MANIFEST_File) elif not self._PEP_File and not self._TOC_File and self._MANIFEST_File: self._optionNo = 5 return self._optionNo, s def setOptionAndFilenames(self, opt_No, pep_file, toc_file, manifest_file): logging.debug("New an audiobook") self._optionNo = opt_No self._PEP_File = pep_file self._TOC_File = toc_file self._MANIFEST_File = manifest_file def openFromLPF(self, lpf_file, unzip_destination): """ self.__is_LPF = False self.__LPF_File = "" self.__BOOK_DIR = "" """ logging.debug('openFromLPF with destination = ' + unzip_destination) try: unzip_files = zipfile.ZipFile(lpf_file, mode='r', compression=zipfile.ZIP_DEFLATED) logging.debug(unzip_files) for f in unzip_files.namelist(): logging.debug(f) unzip_files.extractall(unzip_destination) unzip_files.close() except Exception as ex: logging.error('Sorry, we failed to unzip this lpf file!') return False self._is_LPF = True self._LPF_File = lpf_file lpf_directory, lpf_filename = os.path.split(lpf_file) logging.debug(lpf_filename[:-4]) self._BOOK_DIR = unzip_destination # + '/' + lpf_filename[:-4] logging.debug("self._BOOK_DIR = " + self._BOOK_DIR) return True def openFromDirectory(self, directory): def parseTOC(): tocFlag = False resources = self._MANIFEST.get("resources", None) for resource in resources: logging.debug(resource.keys()) if "rel" in resource.keys() and resource["rel"] == "contents": logging.debug(resource['url']) tocSoup = BeautifulSoup(open(os.path.join( directory, resource["url"]), encoding="utf-8"), features='html5lib') toc = tocSoup.select_one( '[role="doc-toc"]') # eg. <nav role="doc-toc"> if toc: logging.debug("TOC ===\n" + "*" * 70 + "\n" + toc.get_text()) self._TOC = toc self._TOC_File = resource["url"] logging.debug(self._TOC_File) ol = toc.find('ol', recursive=False) ul = toc.find('ul', recursive=False) if ol: self._TOC_List = dictify(ol, 0) elif ul: self._TOC_List = dictify(ul, 0, tagName="ul") logging.debug( "dictify ===\n" + "*" * 70 + "\n" + json.dumps( self._TOC_List, indent=4, ensure_ascii=False)) tocFlag = True break return tocFlag def dictify(ol, level, tagName="ol"): childrenList = [] lvl = level for li in ol.find_all("li", recursive=False): result = {} a_tag = li.find('a', recursive=False) result['level'] = lvl result['href'] = a_tag['href'] result['title'] = a_tag.get_text() next_ol = li.find(tagName, recursive=False) if next_ol: result['children'] = dictify(next_ol, lvl + 1) else: result['children'] = [] childrenList.append(result) return childrenList logging.debug('openFromDirectory') if os.path.exists(directory + "/index.html"): logging.debug("\"index.html\" exists: PEP is available!\n" + "*" * 70) self._PEP_File = "index.html" soup = BeautifulSoup(open(directory + r"/index.html", encoding="utf-8"), features='html5lib') # html5lib html.parser link = soup.select_one('link[rel=\"publication\"]') if link: href = link.attrs["href"] matchInternalID = re.match('^#(\w+)', href) if matchInternalID: logging.debug(matchInternalID.group(1)) manifest = soup.select_one('script[id=\"' + matchInternalID.group(1) + '\"]') if manifest: # <script type="application/ld+json" id="XXX"> if manifest.attrs["type"] == "application/ld+json": self._MANIFEST = json.loads(manifest.string) # self.__MANIFEST_File = "index.html" logging.debug('MANIFEST ===\n ' + '*' * 70 + '\n' + json.dumps(self._MANIFEST, indent=4, ensure_ascii=False)) else: matchExternalJson = re.match('(\w+\.json$)', href) if matchExternalJson: logging.debug(matchExternalJson.group(1)) if not os.path.exists(os.path.join(directory, href)): json_files = glob.glob( os.path.join(directory, '*.json')) href = json_files[0].replace(directory + "/", "") self._MANIFEST_File = href fo = open(os.path.join(directory, href), mode='r', encoding="utf-8") logging.debug("fo.name = " + fo.name) manifestStr = fo.read() # print("manifestStr = ", "*" * 70, "\n", manifestStr) fo.close() self._MANIFEST = json.loads(manifestStr) logging.debug("MANIFEST ===\n" + "*" * 70 + "\n" + json.dumps(self._MANIFEST, indent=4)) csses = soup.select('head > link[rel=\"stylesheet\"]') if csses: for css in csses: href = css.attrs["href"] # css.attrs["href"] = "css/mycss.css" self._CSS_File_List.append(href) logging.debug(self._CSS_File_List) csses = soup.select('head > [name=\"stylesheet\"]') if csses: for css in csses: src = css.attrs["src"] self._CSS_File_List.append(src) logging.debug(self._CSS_File_List) booktitle = soup.select_one('head > title') if booktitle and booktitle.text: logging.debug(booktitle.text) self._Booktitle = booktitle.text # self._MANIFEST['name'] = booktitle.text else: self._Booktitle = self._MANIFEST['name'] toc = soup.select_one( '[role="doc-toc"]' ) # <XXX role="doc-toc"> XXX is usually 'nav' if toc: logging.debug('toc.name =' + toc.name) self._TOC = toc ol = toc.find('ol', recursive=False) ul = toc.find('ul', recursive=False) if ol: self._TOC_List = dictify(ol, 0) elif ul: self._TOC_List = dictify(ul, 0, tagName='ul') logging.debug( 'dictify ===\n' + '*' * 70 + '\n' + json.dumps(self._TOC_List, indent=4, ensure_ascii=False)) else: logging.debug("Try to find 'nav' in other .html files.") tocFlag = False """ The algorithm here needs to be fixed. Old style: parse other html files directly Correct(New) style: """ tocFlag = parseTOC() # resources = self._MANIFEST.get("resources", None) # if resources: # for resource in resources: # logging.debug(resource.keys()) # # if "rel" in resource.keys() and resource["rel"] == "contents": # tocFlag = False # logging.debug(resource['url']) # tocSoup = BeautifulSoup(open(directory + "/" + resource["url"], encoding="utf-8"), # features='html5lib') # toc = tocSoup.select_one('[role="doc-toc"]') # eg. <nav role="doc-toc"> # if toc: # logging.debug("TOC ===\n" + "*" * 70 + "\n" + toc.get_text()) # self._TOC = toc # self._TOC_File = resource["url"] # logging.debug(self._TOC_File) # # ol = toc.find('ol', recursive=False) # ul = toc.find('ul', recursive=False) # if ol: # self._TOC_List = dictify(ol, 0) # elif ul: # self._TOC_List = dictify(ul, 0, tagName="ul") # logging.debug("dictify ===\n" + "*" * 70 + "\n" + json.dumps(self._TOC_List, # indent=4, # ensure_ascii=False)) # # tocFlag = True # break elif os.path.exists(directory + "/publication.json"): """ The algorithm here needs to be fixed. Old style: parse other html files to find toc first Correct(New) style: no index.html, then publication.json must appear """ logging.debug( "\"publication.json\" exists: PEP isn't available!\n" + "*" * 70) with open(directory + "/publication.json", encoding="utf-8") as manifest_file: self._MANIFEST = json.load(manifest_file) self._MANIFEST_File = "publication.json" logging.debug( "MANIFEST ===\n" + "*" * 70 + "\n" + json.dumps(self._MANIFEST, indent=4, ensure_ascii=False)) tocFlag = parseTOC() # resources = self._MANIFEST.get("resources", None) # if resources: # for resource in resources: # logging.debug(resource.keys()) # # if "rel" in resource.keys() and resource["rel"] == "contents": # logging.debug(resource['url']) # tocSoup = BeautifulSoup(open(directory + "/" + resource["url"], encoding="utf-8"), # features='html5lib') # toc = tocSoup.select_one('[role="doc-toc"]') # eg. <nav role="doc-toc"> # if toc: # logging.debug("TOC ===\n" + "*" * 70 + "\n" + toc.get_text()) # self._TOC = toc # self._TOC_File = resource["url"] # logging.debug(self._TOC_File) # # ol = toc.find('ol', recursive=False) # ul = toc.find('ul', recursive=False) # if ol: # self._TOC_List = dictify(ol, 0) # elif ul: # self._TOC_List = dictify(ul, 0, tagName="ul") # logging.debug("dictify ===\n" + "*" * 70 + "\n" + json.dumps(self._TOC_List, # indent=4, # ensure_ascii=False)) # # tocFlag = True # break else: # Not following the W3C rules # Search any .html file in Book directory seems like a good try # htmls = glob.glob(directory + "/*.html") logging.critical("Fatal error!") self.signal_not_W3C_compatible.emit() return if self._TOC or self._MANIFEST: self._loaded = True self.determineOption() logging.debug(self._optionNo) # For Test self._dirty = True def getCheckSet(self, mainWindow): option, list_compulsory = self.determineOption() url_compulsory = [ self.getBookDir() + '/' + f for f in list_compulsory if not f.startswith('http') ] url_orderingOrder = [ self.getBookDir() + '/' + f["url"] for f in mainWindow.readingOrderWidget.save() if not f["url"].startswith('http') ] url_toc = [ self.getBookDir() + '/' + f["href"] for f in mainWindow.tocWidget.save(True) if not f["href"].startswith('http') ] url_cover = [ self.getBookDir() + '/' + f["url"] for f in [mainWindow.coverPreviewWidget.save()] if not f["url"].startswith('http') ] url_supplemental = [ self.getBookDir() + '/' + f["url"] for f in mainWindow.supplementalListWidget.save() if not f["url"].startswith('http') ] set_url = set(url_compulsory + url_orderingOrder + url_toc + url_cover + url_supplemental) return set_url def checkAttachedFile(self, filenames, checkSet, cls, instance): for filename in filenames: file_folder, file_name = os.path.split(filename) if filename in checkSet: logging.debug( "You have chosen one file already in this audiobook") continue elif self.getBookDir() + '/' + file_name in checkSet: logging.debug( "One file with the same name is already in this audiobook") try: shutil.copyfile(filename, self.getBookDir() + '/' + file_name) filename = self.getBookDir() + '/' + file_name except Exception as ex: logging.critical(ex) continue elif file_folder == self.getBookDir(): pass else: pass cls.str_LastOpenedDirectory = file_folder src = QFile(filename) # info = QFileInfo(filename) if file_folder != self.getBookDir(): try: shutil.copyfile(filename, self.getBookDir() + '/' + file_name) filename = self.getBookDir() + '/' + file_name except Exception as ex: logging.critical(ex) continue else: filename = self.getBookDir() + '/' + file_name mime, _ = mimetypes.guess_type(filename, strict=False) if mime is None: logging.warning('Cannot guess file type!') else: logging.debug('File MIME type: %s' % mime) instance.addItems(filename, 1)