Beispiel #1
0
    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
Beispiel #2
0
    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()
Beispiel #3
0
    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
Beispiel #4
0
    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()
Beispiel #5
0
    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
Beispiel #6
0
    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
Beispiel #7
0
    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
Beispiel #8
0
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()
Beispiel #9
0
    def on_action_Qt_triggered(self):
        self.mask = MaskWidget(self)
        self.mask.show()

        QApplication.aboutQt()
        self.mask.close()
Beispiel #10
0
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)
Beispiel #11
0
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()
Beispiel #12
0
    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
Beispiel #13
0
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)
Beispiel #14
0
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)
Beispiel #15
0
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)
Beispiel #16
0
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)
Beispiel #17
0
    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
Beispiel #18
0
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)