Beispiel #1
0
class QtAbout(QDialog):
    def __init__(self):
        super().__init__()

        self.layout = QVBoxLayout()

        # Description
        title_label = QLabel(
            "<b>napari: a multi-dimensional image viewer for python</b>")
        title_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
        self.layout.addWidget(title_label)

        # Add information
        self.infoTextBox = QTextEdit()
        self.infoTextBox.setTextInteractionFlags(Qt.TextSelectableByMouse)
        self.infoTextBox.setLineWrapMode(QTextEdit.NoWrap)
        # Add text copy button
        self.infoCopyButton = QtCopyToClipboardButton(self.infoTextBox)
        self.info_layout = QHBoxLayout()
        self.info_layout.addWidget(self.infoTextBox, 1)
        self.info_layout.addWidget(self.infoCopyButton, 0, Qt.AlignTop)
        self.info_layout.setAlignment(Qt.AlignTop)
        self.layout.addLayout(self.info_layout)

        self.infoTextBox.setText(sys_info(as_html=True))
        self.infoTextBox.setMinimumSize(
            self.infoTextBox.document().size().width() + 19,
            self.infoTextBox.document().size().height() + 10,
        )

        self.layout.addWidget(QLabel('<b>citation information:</b>'))
        self.citationTextBox = QTextEdit(citation_text)
        self.citationTextBox.setFixedHeight(64)
        self.citationCopyButton = QtCopyToClipboardButton(self.citationTextBox)
        self.citation_layout = QHBoxLayout()
        self.citation_layout.addWidget(self.citationTextBox, 1)
        self.citation_layout.addWidget(self.citationCopyButton, 0, Qt.AlignTop)
        self.layout.addLayout(self.citation_layout)

        self.setLayout(self.layout)

    @staticmethod
    def showAbout(qt_viewer):
        d = QtAbout()
        d.setObjectName('QtAbout')
        d.setStyleSheet(qt_viewer.styleSheet())
        d.setWindowTitle('About')
        d.setWindowModality(Qt.ApplicationModal)
        d.exec_()
Beispiel #2
0
class Console(FramelessWindow):
    message = Signal(dict)

    def __init__(self, parent=None):
        super(Console, self).__init__(parent)
        self.json_decode_warning = None

        self.widget = QWidget(self)
        self.console_layout = QVBoxLayout(self.widget)
        self.widget.setLayout(self.console_layout)

        self.output_label = QLabel(self.widget)
        self.output_label.setText("Output")
        self.output_edit = QTextEdit(self.widget)
        self.output_edit.setReadOnly(True)
        self.highlighter = Highlighter(self.output_edit.document())
        self.output_edit.setStyleSheet("background-color: rgb(0, 0, 0);")
        self.output_edit.setTextColor(QColor(0, 255, 0))
        self.output_edit.setFont(QFont(self.output_edit.currentFont().family(), 10))

        self.input_label = QLabel(self.widget)
        self.input_label.setText("Command")
        self.input_edit = QLineEdit(self.widget)
        self.input_edit.setStyleSheet("background-color: rgb(0, 0, 0); color: rgb(0, 255, 0)")
        self.input_edit.setFont(QFont(self.output_edit.currentFont().family(), 10))

        self.send_button = QPushButton(self.widget)
        self.send_button.setObjectName("send_button")
        self.send_button.setText("Send command")

        self.console_layout.addWidget(self.output_label)
        self.console_layout.addWidget(self.output_edit)
        self.console_layout.addWidget(self.input_label)
        self.console_layout.addWidget(self.input_edit)
        self.console_layout.addWidget(self.send_button)

        self.addContentWidget(self.widget)
        QMetaObject.connectSlotsByName(self)

    def on_send_button_clicked(self):
        mess = self.input_edit.text()
        try:
            mess = json.loads(mess)
            self.message.emit(mess)
            self.input_edit.clear()
            self.output_edit.append("$: {}".format(mess))

        except json.JSONDecodeError as e:
            self.json_decode_warning = FramelessCriticalMessageBox(self)
            self.json_decode_warning.setText("Failed to convert string to JSON: {}".format(e))
            self.json_decode_warning.setStandardButtons(QDialogButtonBox.Ok)
            self.json_decode_warning.button(QDialogButtonBox.Ok).clicked.connect(self.json_decode_warning.close)
            self.json_decode_warning.show()

    def write(self, resp: dict):
        if resp.get("event_type"):
            resp = "[{}]: {}".format(str(resp.get("event_type")).upper(), resp)
        else:
            resp = str(resp)
        self.output_edit.append(resp)
Beispiel #3
0
            def show_tb(parent):
                tbdialog = QDialog(parent=parent.parent())
                tbdialog.setModal(True)
                # this is about the minimum width to not get rewrap
                # and the minimum height to not have scrollbar
                tbdialog.resize(650, 270)
                tbdialog.setLayout(QVBoxLayout())

                text = QTextEdit()
                theme = get_theme(
                    get_settings().appearance.theme, as_dict=False
                )
                _highlight = Pylighter(  # noqa: F841
                    text.document(), "python", theme.syntax_style
                )
                text.setText(notification.as_text())
                text.setReadOnly(True)
                btn = QPushButton(trans._('Enter Debugger'))

                def _enter_debug_mode():
                    btn.setText(
                        trans._(
                            'Now Debugging. Please quit debugger in console to continue'
                        )
                    )
                    _debug_tb(notification.exception.__traceback__)
                    btn.setText(trans._('Enter Debugger'))

                btn.clicked.connect(_enter_debug_mode)
                tbdialog.layout().addWidget(text)
                tbdialog.layout().addWidget(btn, 0, Qt.AlignRight)
                tbdialog.show()
class MainWindow(QMainWindow):
    def __init__(self):
        self.logger = logging.getLogger(APPLICATION_NAME + '.MainWindow')
        self.logger.info("MainWindow.__init__")

        super(MainWindow, self).__init__()

        self.init_ui()

        self.read_settings()

    def init_ui(self):
        # Define standard icon.
        standard_icon = self.style().standardIcon

        # Central widget.
        self.main_widget = SpectrumWidget()
        self.main_widget.setFocus()
        self.setCentralWidget(self.main_widget)

        # Project action
        new_project_action = QAction(QIcon(':/oi/svg/document.svg'),
                                     'New project', self)
        new_project_action.setShortcut('Ctrl+N')
        new_project_action.setStatusTip('New project')
        new_project_action.triggered.connect(self.new_project)

        open_project_action = QAction(QIcon(':/oi/svg/envelope-open.svg'),
                                      'Open project', self)
        open_project_action.setShortcut('Ctrl+O')
        open_project_action.setStatusTip('Open project')
        open_project_action.triggered.connect(self.open_project)

        close_project_action = QAction(QIcon(':/oi/svg/envelope-closed.svg'),
                                       'Close project', self)
        close_project_action.setShortcut('Ctrl+C')
        close_project_action.setStatusTip('Close project')
        close_project_action.triggered.connect(self.close_project)

        save_project_action = QAction(QIcon(':/oi/svg/hard-drive.svg'),
                                      'Save project', self)
        save_project_action.setShortcut('Ctrl+S')
        save_project_action.setStatusTip('Save project')
        save_project_action.triggered.connect(self.save_project)

        saveas_project_action = QAction(QIcon(':/oi/svg/hard-drive.svg'),
                                        'Save project as ...', self)
        # saveas_project_action.setShortcut('Ctrl+S')
        saveas_project_action.setStatusTip('Save project as ...')
        saveas_project_action.triggered.connect(self.saveas_project)

        # Spectrum action
        import_spectrum_action = QAction(QIcon(':/oi/svg/account-login.svg'),
                                         'Import spectrum', self)
        import_spectrum_action.setShortcut('Ctrl+I')
        import_spectrum_action.setStatusTip('Import spectrum')
        import_spectrum_action.triggered.connect(self.import_spectrum)

        export_spectrum_action = QAction(QIcon(':/oi/svg/account-logout.svg'),
                                         'Export spectrum', self)
        # export_spectrum_action.setShortcut('Ctrl+I')
        export_spectrum_action.setStatusTip('Export spectrum')
        export_spectrum_action.triggered.connect(self.export_spectrum)

        # Exit action
        exit_action = QAction(QIcon(':/oi/svg/x.svg'), 'Exit', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.setStatusTip('Exit application')
        exit_action.triggered.connect(self.close)

        # Status bar.
        self.statusBar()

        # Menu bar.
        menubar = self.menuBar()
        file_menu = menubar.addMenu('&File')
        file_menu.addAction(new_project_action)
        file_menu.addAction(open_project_action)
        file_menu.addAction(save_project_action)
        file_menu.addAction(saveas_project_action)
        file_menu.addAction(close_project_action)
        file_menu.addSeparator()
        file_menu.addAction(exit_action)

        view_menu = menubar.addMenu('&View')

        spectrum_menu = menubar.addMenu('&Spectrum')
        spectrum_menu.addAction(import_spectrum_action)

        analysis_menu = menubar.addMenu('&Analysis')

        # Toolbar
        file_toolbar = self.addToolBar('File')
        file_toolbar.addAction(new_project_action)
        file_toolbar.addAction(open_project_action)
        file_toolbar.addAction(save_project_action)
        file_toolbar.addAction(saveas_project_action)
        file_toolbar.addAction(close_project_action)
        file_toolbar.addAction(exit_action)
        view_menu.addAction(file_toolbar.toggleViewAction())

        spectrum_toolbar = self.addToolBar('Spectrum')
        spectrum_toolbar.addAction(import_spectrum_action)
        view_menu.addAction(spectrum_toolbar.toggleViewAction())

        analysis_toolbar = self.addToolBar('Analysis')
        view_menu.addAction(analysis_toolbar.toggleViewAction())

        view_menu.addSeparator()

        # Dock widget.
        self.graphic_settings_dock = QDockWidget("Graphic settings", self)
        self.graphic_settings_dock.setObjectName("graphic_settings_dock")
        self.graphic_settings_dock.setAllowedAreas(Qt.AllDockWidgetAreas)
        label_test = QLabel("Test label")
        self.graphic_settings_dock.setWidget(label_test)
        view_menu.addAction(self.graphic_settings_dock.toggleViewAction())
        self.addDockWidget(Qt.AllDockWidgetAreas, self.graphic_settings_dock)
        print(self.graphic_settings_dock.objectName())

        # Final options.
        self.setWindowTitle('X-ray spectrum analyzer')
        self.show()

    def closeEvent(self, event):
        self.save_settings()
        super(MainWindow, self).closeEvent(event)

    def save_settings(self):
        settings = QSettings("openMicroanalysis", "xrayspectrumanalyzergui")
        # print(settings.fileName())

        settings.beginGroup("MainWindow")
        settings.setValue("geometry", self.saveGeometry())
        settings.setValue("window_state", self.saveState())
        settings.endGroup()

        settings.beginGroup("graphic_settings_dock")
        settings.setValue("visible", self.graphic_settings_dock.isVisible())
        settings.endGroup()

        # settings.beginGroup("zero_loss_peak_dock")
        # settings.setValue("visible", self.zero_loss_peak_dock.isVisible())
        # settings.endGroup()

    def read_settings(self):
        settings = QSettings("openMicroanalysis", "xrayspectrumanalyzergui")
        # print(settings.fileName())
        # settings.clear()

        settings.beginGroup("MainWindow")
        geometry_value = settings.value("geometry")
        if geometry_value is None:
            self.setGeometry(300, 300, 600, 400)
        else:
            self.restoreGeometry(geometry_value)
        window_state_value = settings.value("window_state")
        if window_state_value is not None:
            self.restoreState(window_state_value)
        settings.endGroup()

        settings.beginGroup("graphic_settings_dock")
        visible_value = settings.value("visible")
        if visible_value is not None:
            if visible_value == "true":
                self.graphic_settings_dock.setVisible(True)
            elif visible_value == "false":
                self.graphic_settings_dock.setVisible(False)
        settings.endGroup()

        # settings.beginGroup("zero_loss_peak_dock")
        # visible_value = settings.value("visible")
        # if visible_value is not None:
        #     if visible_value == "true":
        #         self.zero_loss_peak_dock.setVisible(True)
        #     elif visible_value == "false":
        #         self.zero_loss_peak_dock.setVisible(False)
        # settings.endGroup()

    def import_spectrum(self):
        self.statusBar().showMessage("Import spectrum", 2000)

        path = os.path.dirname(__file__)
        formats = ["*.msa", "*.txt"]
        file_filters = "Spectrum file ({:s})".format(" ".join(formats))
        file_names = QFileDialog.getOpenFileName(self,
                                                 "Import an x-ray spectrum",
                                                 path, file_filters)

    def export_spectrum(self):
        self.statusBar().showMessage("Export spectrum", 2000)

    def new_project(self):
        self.statusBar().showMessage("New project", 2000)

    def open_project(self):
        self.statusBar().showMessage("Open project", 2000)

    def close_project(self):
        self.statusBar().showMessage("Close project", 2000)

    def save_project(self):
        self.statusBar().showMessage("Save project", 2000)

    def saveas_project(self):
        self.statusBar().showMessage("Save project as ...", 2000)

    def create_gui(self):
        self.logger.info("MainWindow.create_gui")

        self._create_main_window()
        self._create_actions()
        self._create_menus()
        self._create_toolbars()
        self._create_tooltip()
        self._create_spectra_display()
        self._create_data_display()
        self._create_operations_display()
        self._create_layout()
        self._create_statusbar()

        self._read_settings()

        self.show()

    def _create_main_window(self):
        self.setGeometry(300, 300, 500, 500)
        self.setWindowTitle('Spectrum Analyzer')
        # self.setWindowIcon(QIcon('../../../images/cog.svg'))
        self.setWindowIcon(self.style().standardIcon(QStyle.SP_DesktopIcon))
        self._center_main_window()

    def _center_main_window(self):
        self.logger.info("MainWindow._center_main_window")

        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def _create_menus(self):
        self.logger.info("MainWindow._create_menus")

        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenu.addAction(self.newAct)
        self.fileMenu.addAction(self.openAct)
        self.fileMenu.addAction(self.saveAct)
        self.fileMenu.addAction(self.saveAsAct)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.exitAct)

        self.menuBar().addSeparator()

        self.helpMenu = self.menuBar().addMenu("&Help")
        self.helpMenu.addAction(self.aboutAct)
        self.helpMenu.addAction(self.aboutQtAct)

    def _create_layout(self):
        mainLayout = QHBoxLayout()
        mainLayout.addWidget(self.dataGroupBox)
        mainLayout.addWidget(self.plotGroupBox)
        mainLayout.addWidget(self.operationsGroupBox)

        self.mainGroupBox = QGroupBox("Main layout")
        self.mainGroupBox.setLayout(mainLayout)
        self.setCentralWidget(self.mainGroupBox)

    def _create_spectra_display(self):
        self.plotGroupBox = QGroupBox("Plot layout")

        self.figure1 = Figure(facecolor=(1, 1, 1), edgecolor=(0, 0, 0))
        self.canvas1 = FigureCanvas(self.figure1)
        self.canvas1.setParent(self.plotGroupBox)
        self.canvas1.setFocusPolicy(Qt.StrongFocus)
        self.canvas1.setFocus()
        self.canvas1.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        self.canvas1.updateGeometry()

        self.mpl_toolbar1 = NavigationToolbar(self.canvas1, self.plotGroupBox)
        self.canvas1.mpl_connect('key_press_event', self.on_key_press)

        self.figure2 = Figure(facecolor=(1, 1, 1), edgecolor=(0, 0, 0))
        self.canvas2 = FigureCanvas(self.figure2)
        self.canvas2.setParent(self.plotGroupBox)
        self.mpl_toolbar2 = NavigationToolbar(self.canvas1, self.plotGroupBox)

        layout = QVBoxLayout()
        layout.addWidget(self.canvas1)
        layout.addWidget(self.mpl_toolbar1)
        layout.addWidget(self.canvas2)
        layout.addWidget(self.mpl_toolbar2)
        self.plotGroupBox.setLayout(layout)

    def _create_data_display(self):
        self.dataGroupBox = QGroupBox("Data layout")
        data_layout = QVBoxLayout()

        group_box = QGroupBox("Spectra")
        self.spectra_list_view = QListWidget(self)
        self.spectra_list_view.setMinimumWidth(200)

        layout = QVBoxLayout()
        layout.addWidget(self.spectra_list_view)
        group_box.setLayout(layout)
        data_layout.addWidget(group_box)

        group_box = QGroupBox("ROI")
        roi_list_view = QListWidget(self)
        layout = QVBoxLayout()
        layout.addWidget(roi_list_view)
        group_box.setLayout(layout)
        data_layout.addWidget(group_box)

        group_box = QGroupBox("Elements")
        element_list_view = QListWidget(self)
        layout = QVBoxLayout()
        layout.addWidget(element_list_view)
        group_box.setLayout(layout)
        data_layout.addWidget(group_box)

        self.dataGroupBox.setLayout(data_layout)

    def _create_operations_display(self):
        self.operationsGroupBox = QGroupBox("Operations layout")
        results_layout = QVBoxLayout()

        group_box = QGroupBox("Operation")
        results_layout.addWidget(group_box)
        group_box = QGroupBox("Results")
        results_layout.addWidget(group_box)

        self.operationsGroupBox.setLayout(results_layout)

    def _create_tooltip(self):
        QToolTip.setFont(QFont('SansSerif', 10))
        self.setToolTip('This is a <b>QWidget</b> widget')

    def _create_actions(self):
        self.logger.info("MainWindow._create_actions")

        self.newAct = QAction(self.style().standardIcon(QStyle.SP_FileIcon),
                              "&New",
                              self,
                              shortcut=QKeySequence.New,
                              statusTip="Create a new file",
                              triggered=self.newFile)

        self.openAct = QAction(self.style().standardIcon(
            QStyle.SP_DirOpenIcon),
                               "&Open...",
                               self,
                               shortcut=QKeySequence.Open,
                               statusTip="Open an existing file",
                               triggered=self.open)

        self.saveAct = QAction(self.style().standardIcon(
            QStyle.SP_DialogSaveButton),
                               "&Save",
                               self,
                               shortcut=QKeySequence.Save,
                               statusTip="Save the document to disk",
                               triggered=self.save)

        self.saveAsAct = QAction(
            self.style().standardIcon(QStyle.SP_DialogSaveButton),
            "Save &As...",
            self,
            shortcut=QKeySequence.SaveAs,
            statusTip="Save the document under a new name",
            triggered=self.saveAs)

        self.exitAct = QAction(self.style().standardIcon(
            QStyle.SP_DialogCloseButton),
                               "E&xit",
                               self,
                               shortcut="Ctrl+Q",
                               statusTip="Exit the application",
                               triggered=self.close)

        self.textEdit = QTextEdit()

        self.aboutAct = QAction(self.style().standardIcon(
            QStyle.SP_MessageBoxInformation),
                                "&About",
                                self,
                                statusTip="Show the application's About box",
                                triggered=self.about)

        self.aboutQtAct = QAction(self.style().standardIcon(
            QStyle.SP_TitleBarMenuButton),
                                  "About &Qt",
                                  self,
                                  statusTip="Show the Qt library's About box",
                                  triggered=QApplication().aboutQt)

    def _create_toolbars(self):
        self.logger.info("MainWindow._create_toolbars")

        self.fileToolBar = self.addToolBar("File")
        self.fileToolBar.addAction(self.newAct)
        self.fileToolBar.addAction(self.openAct)
        self.fileToolBar.addAction(self.saveAct)

    def _create_statusbar(self):
        self.logger.info("MainWindow._create_statusbar")

        self.statusBar().showMessage("Ready")

    def _read_settings(self):
        self.logger.info("MainWindow._read_settings")

        settings = QSettings(ORGANIZATION_NAME, APPLICATION_NAME)
        pos = settings.value("pos", QPoint(200, 200))
        size = settings.value("size", QSize(400, 400))
        self.resize(size)
        self.move(pos)

    def _write_settings(self):
        self.logger.info("MainWindow._write_settings")

        settings = QSettings(ORGANIZATION_NAME, APPLICATION_NAME)
        settings.setValue("pos", self.pos())
        settings.setValue("size", self.size())

    def maybeSave(self):
        self.logger.info("MainWindow.maybeSave")

        if self.textEdit.document().isModified():
            ret = QMessageBox.warning(
                self, "Application",
                "The document has been modified.\nDo you want to save "
                "your changes?",
                QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
            if ret == QMessageBox.Save:
                return self.save()
            elif ret == QMessageBox.Cancel:
                return False
        return True

    def closeEvent(self, event):
        self.logger.info("MainWindow.closeEvent")

        if self.maybeSave():
            self._write_settings()
            event.accept()
        else:
            event.ignore()

    def newFile(self):
        self.logger.info("MainWindow.newFile")

        if self.maybeSave():
            self.textEdit.clear()
            self.setCurrentFile('')

    def open(self):
        self.logger.info("MainWindow.open")

        if self.maybeSave():
            filepath, _filtr = QFileDialog.getOpenFileName(self)
            if filepath:
                self.spectrumAnalyzer.readSpectrum(filepath)
                filename = os.path.basename(filepath)
                self.spectra_list_view.addItem(filename)
                self.spectrumAnalyzer.plotSpectrum(self.figure1)
                self.canvas1.draw()

    def save(self):
        self.logger.info("MainWindow.save")

        if self.curFile:
            return self.saveFile(self.curFile)

        return self.saveAs()

    def saveAs(self):
        self.logger.info("MainWindow.saveAs")

        fileName, _filtr = QFileDialog.getSaveFileName(self)
        if fileName:
            return self.saveFile(fileName)

        return False

    def about(self):
        self.logger.info("MainWindow.about")

        QMessageBox.about(
            self, "About xrayspectrumanalyzer",
            "The <b>xrayspectrumanalyzer</b> extract peak intensity from EDS spectrum."
        )

    def documentWasModified(self):
        self.logger.info("MainWindow.documentWasModified")

        self.setWindowModified(self.textEdit.document().isModified())

    def on_key_press(self, event):
        print('you pressed', event.key)
        # implement the default mpl key press events described at
        # http://matplotlib.org/users/navigation_toolbar.html#navigation-keyboard-shortcuts
        key_press_handler(event, self.canvas1, self.mpl_toolbar1)
Beispiel #5
0
class QtPluginErrReporter(QDialog):
    """Dialog that allows users to review and report PluginError tracebacks.

    Parameters
    ----------
    parent : QWidget, optional
        Optional parent widget for this widget.
    initial_plugin : str, optional
        If provided, errors from ``initial_plugin`` will be shown when the
        dialog is created, by default None

    Attributes
    ----------
    text_area : qtpy.QtWidgets.QTextEdit
        The text area where traceback information will be shown.
    plugin_combo : qtpy.QtWidgets.QComboBox
        The dropdown menu used to select the current plugin
    github_button : qtpy.QtWidgets.QPushButton
        A button that, when pressed, will open an issue at the current plugin's
        github issue tracker, prepopulated with a formatted traceback.  Button
        is only visible if a github URL is detected in the package metadata for
        the current plugin.
    clipboard_button : qtpy.QtWidgets.QPushButton
        A button that, when pressed, copies the current traceback information
        to the clipboard.  (HTML tags are removed in the copied text.)
    plugin_meta : qtpy.QtWidgets.QLabel
        A label that will show available plugin metadata (such as home page).
    """

    NULL_OPTION = trans._('select plugin... ')

    def __init__(
        self,
        *,
        parent: Optional[QWidget] = None,
        initial_plugin: Optional[str] = None,
    ) -> None:
        super().__init__(parent)
        from ...plugins import plugin_manager

        self.plugin_manager = plugin_manager

        self.setWindowTitle(trans._('Recorded Plugin Exceptions'))
        self.setWindowModality(Qt.NonModal)
        self.layout = QVBoxLayout()
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(10, 10, 10, 10)
        self.setLayout(self.layout)

        self.text_area = QTextEdit()
        theme = get_theme(get_settings().appearance.theme, as_dict=False)
        self._highlight = Pylighter(self.text_area.document(), "python",
                                    theme.syntax_style)
        self.text_area.setTextInteractionFlags(Qt.TextSelectableByMouse)
        self.text_area.setMinimumWidth(360)

        # Create plugin dropdown menu
        self.plugin_combo = QComboBox()
        self.plugin_combo.addItem(self.NULL_OPTION)
        bad_plugins = [e.plugin_name for e in self.plugin_manager.get_errors()]
        self.plugin_combo.addItems(list(sorted(set(bad_plugins))))
        self.plugin_combo.currentTextChanged.connect(self.set_plugin)
        self.plugin_combo.setCurrentText(self.NULL_OPTION)

        # create github button (gets connected in self.set_plugin)
        self.github_button = QPushButton(trans._('Open issue on GitHub'), self)
        self.github_button.setToolTip(
            trans._(
                "Open a web browser to submit this error log\nto the developer's GitHub issue tracker",
            ))
        self.github_button.hide()

        # create copy to clipboard button
        self.clipboard_button = QPushButton()
        self.clipboard_button.hide()
        self.clipboard_button.setObjectName("QtCopyToClipboardButton")
        self.clipboard_button.setToolTip(
            trans._("Copy error log to clipboard"))
        self.clipboard_button.clicked.connect(self.copyToClipboard)

        # plugin_meta contains a URL to the home page, (and/or other details)
        self.plugin_meta = QLabel('', parent=self)
        self.plugin_meta.setObjectName("pluginInfo")
        self.plugin_meta.setTextFormat(Qt.RichText)
        self.plugin_meta.setTextInteractionFlags(Qt.TextBrowserInteraction)
        self.plugin_meta.setOpenExternalLinks(True)
        self.plugin_meta.setAlignment(Qt.AlignRight)

        # make layout
        row_1_layout = QHBoxLayout()
        row_1_layout.setContentsMargins(11, 5, 10, 0)
        row_1_layout.addStretch(1)
        row_1_layout.addWidget(self.plugin_meta)
        row_2_layout = QHBoxLayout()
        row_2_layout.setContentsMargins(11, 5, 10, 0)
        row_2_layout.addWidget(self.plugin_combo)
        row_2_layout.addStretch(1)
        row_2_layout.addWidget(self.github_button)
        row_2_layout.addWidget(self.clipboard_button)
        row_2_layout.setSpacing(5)
        self.layout.addLayout(row_1_layout)
        self.layout.addLayout(row_2_layout)
        self.layout.addWidget(self.text_area, 1)
        self.setMinimumWidth(750)
        self.setMinimumHeight(600)

        if initial_plugin:
            self.set_plugin(initial_plugin)

    def set_plugin(self, plugin: str) -> None:
        """Set the current plugin shown in the dropdown and text area.

        Parameters
        ----------
        plugin : str
            name of a plugin that has created an error this session.
        """
        self.github_button.hide()
        self.clipboard_button.hide()
        try:
            self.github_button.clicked.disconnect()
        # when disconnecting a non-existent signal
        # PySide2 raises runtimeError, PyQt5 raises TypeError
        except (RuntimeError, TypeError):
            pass

        if not plugin or (plugin == self.NULL_OPTION):
            self.plugin_meta.setText('')
            self.text_area.setText('')
            return

        if not self.plugin_manager.get_errors(plugin):
            raise ValueError(
                trans._("No errors reported for plugin '{plugin}'",
                        plugin=plugin))

        self.plugin_combo.setCurrentText(plugin)

        err_string = format_exceptions(plugin, as_html=False, color="NoColor")
        self.text_area.setText(err_string)
        self.clipboard_button.show()

        # set metadata and outbound links/buttons
        err0 = self.plugin_manager.get_errors(plugin)[0]
        meta = standard_metadata(err0.plugin) if err0.plugin else {}
        meta_text = ''
        if not meta:
            self.plugin_meta.setText(meta_text)
            return

        url = meta.get('url')
        if url:
            meta_text += (
                '<span style="color:#999;">plugin home page:&nbsp;&nbsp;'
                f'</span><a href="{url}" style="color:#999">{url}</a>')
            if 'github.com' in url:

                def onclick():
                    import webbrowser

                    err = format_exceptions(plugin, as_html=False)
                    err = (
                        "<!--Provide detail on the error here-->\n\n\n\n"
                        "<details>\n<summary>Traceback from napari</summary>"
                        f"\n\n```\n{err}\n```\n</details>")
                    url = f'{meta.get("url")}/issues/new?&body={err}'
                    webbrowser.open(url, new=2)

                self.github_button.clicked.connect(onclick)
                self.github_button.show()
        self.plugin_meta.setText(meta_text)

    def copyToClipboard(self) -> None:
        """Copy current plugin traceback info to clipboard as plain text."""
        plugin = self.plugin_combo.currentText()
        err_string = format_exceptions(plugin, as_html=False)
        cb = QGuiApplication.clipboard()
        cb.setText(err_string)
Beispiel #6
0
class NoteEditor(QMainWindow):

    def __init__(self, parent, noteType, noteFileName="", b=None, c=None, v=None):
        super().__init__()
        self.parent, self.noteType = parent, noteType
        self.noteFileName = noteFileName
        if not self.noteType == "file":
            if v:
                self.b, self.c, self.v = b, c, v
            else:
                self.b, self.c, self.v = config.studyB, config.studyC, config.studyV

        # default - "Rich" mode for editing
        self.html = True
        # default - text is not modified; no need for saving new content
        self.parent.noteSaved = True
        config.noteOpened = True
        config.lastOpenedNote = (noteType, b, c, v)

        # specify window size
        self.resizeWindow(2/3, 2/3)

        # setup interface
        self.setupMenuBar()
        self.addToolBarBreak()
        self.setupToolBar()
        if config.hideNoteEditorStyleToolbar:
            self.toolBar.hide()
        self.addToolBarBreak()
        self.setupTextUtility()
        if config.hideNoteEditorTextUtility:
            self.ttsToolbar.hide()
            self.translateToolbar.hide()
        self.setupLayout()

        # display content when first launched
        self.displayInitialContent()
        self.editor.setFocus()

        # specify window title
        self.updateWindowTitle()

    # re-implementing close event, when users close this widget
    def closeEvent(self, event):
        if self.parent.noteSaved:
            config.noteOpened = False
            event.accept()
            if config.lastOpenedNote and config.openBibleNoteAfterEditorClosed:
                #if config.lastOpenedNote[0] == "file":
                #    self.parent.externalFileButtonClicked()
                if config.lastOpenedNote[0] == "book":
                    self.parent.openStudyBookNote()
                elif config.lastOpenedNote[0] == "chapter":
                    self.parent.openStudyChapterNote()
                elif config.lastOpenedNote[0] == "verse":
                    self.parent.openStudyVerseNote()
        else:
            if self.parent.warningNotSaved():
                self.parent.noteSaved = True
                config.noteOpened = False
                event.accept()
            else:
                self.parent.bringToForeground(self)
                event.ignore()

    # re-implement keyPressEvent, control+S for saving file
    def keyPressEvent(self, event):
        keys = {
            Qt.Key_O: self.openFileDialog,
            Qt.Key_S: self.saveNote,
            Qt.Key_B: self.format_bold,
            Qt.Key_I: self.format_italic,
            Qt.Key_U: self.format_underline,
            Qt.Key_M: self.format_custom,
            Qt.Key_D: self.format_clear,
            Qt.Key_F: self.focusSearchField,
        }
        key = event.key()
        if event.modifiers() == Qt.ControlModifier and key in keys:
            keys[key]()

    # window appearance
    def resizeWindow(self, widthFactor, heightFactor):
        availableGeometry = QGuiApplication.instance().desktop().availableGeometry()
        self.resize(availableGeometry.width() * widthFactor, availableGeometry.height() * heightFactor)

    def updateWindowTitle(self):
        if self.noteType == "file":
            if self.noteFileName:
                *_, title = os.path.split(self.noteFileName)
            else:
                title = "NEW"
        else:
            title = self.parent.bcvToVerseReference(self.b, self.c, self.v)
            if self.noteType == "book":
                title, *_ = title.split(" ")            
            elif self.noteType == "chapter":
                title, *_ = title.split(":")
        mode = {True: "rich", False: "plain"}
        notModified = {True: "", False: " [modified]"}
        self.setWindowTitle("Note Editor ({1} mode) - {0}{2}".format(title, mode[self.html], notModified[self.parent.noteSaved]))

    # switching between "rich" & "plain" mode
    def switchMode(self):
        if self.html:
            note = self.editor.toHtml()
            note = re.sub("<body style={0}[ ]*?font-family:[ ]*?'[^']*?';[ ]*?font-size:[ ]*?[0-9]+?pt;".format('"'), "<body style={0}font-family:'{1}'; font-size:{2}pt;".format('"', config.font, config.fontSize), note)
            self.editor.setPlainText(note)
            self.html = False
            self.updateWindowTitle()
        else:
            note = self.editor.toPlainText()
            self.editor.setHtml(note)
            self.html = True
            self.updateWindowTitle()
        # without this hide / show command below, QTextEdit does not update the text in some devices
        self.hide()
        self.show()

    def setupMenuBar(self):
        if config.toolBarIconFullSize:
            self.setupMenuBarFullIconSize()
        else:
            self.setupMenuBarStandardIconSize()

    def setupMenuBarStandardIconSize(self):

        self.menuBar = QToolBar()
        self.menuBar.setWindowTitle(config.thisTranslation["note_title"])
        self.menuBar.setContextMenuPolicy(Qt.PreventContextMenu)
        # In QWidget, self.menuBar is treated as the menubar without the following line
        # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window
        self.addToolBar(self.menuBar)

        newButton = QPushButton()
        newButton.setToolTip("{0}\n[Ctrl/Cmd + N]".format(config.thisTranslation["menu7_create"]))
        newButtonFile = os.path.join("htmlResources", "newfile.png")
        newButton.setIcon(QIcon(newButtonFile))
        newButton.clicked.connect(self.newNoteFile)
        self.menuBar.addWidget(newButton)

        openButton = QPushButton()
        openButton.setToolTip("{0}\n[Ctrl/Cmd + O]".format(config.thisTranslation["menu7_open"]))
        openButtonFile = os.path.join("htmlResources", "open.png")
        openButton.setIcon(QIcon(openButtonFile))
        openButton.clicked.connect(self.openFileDialog)
        self.menuBar.addWidget(openButton)

        self.menuBar.addSeparator()

        saveButton = QPushButton()
        saveButton.setToolTip("{0}\n[Ctrl/Cmd + S]".format(config.thisTranslation["note_save"]))
        saveButtonFile = os.path.join("htmlResources", "save.png")
        saveButton.setIcon(QIcon(saveButtonFile))
        saveButton.clicked.connect(self.saveNote)
        self.menuBar.addWidget(saveButton)

        saveAsButton = QPushButton()
        saveAsButton.setToolTip(config.thisTranslation["note_saveAs"])
        saveAsButtonFile = os.path.join("htmlResources", "saveas.png")
        saveAsButton.setIcon(QIcon(saveAsButtonFile))
        saveAsButton.clicked.connect(self.openSaveAsDialog)
        self.menuBar.addWidget(saveAsButton)

        self.menuBar.addSeparator()

        toolBarButton = QPushButton()
        toolBarButton.setToolTip(config.thisTranslation["note_print"])
        toolBarButtonFile = os.path.join("htmlResources", "print.png")
        toolBarButton.setIcon(QIcon(toolBarButtonFile))
        toolBarButton.clicked.connect(self.printNote)
        self.menuBar.addWidget(toolBarButton)

        self.menuBar.addSeparator()

        switchButton = QPushButton()
        switchButton.setToolTip(config.thisTranslation["note_mode"])
        switchButtonFile = os.path.join("htmlResources", "switch.png")
        switchButton.setIcon(QIcon(switchButtonFile))
        switchButton.clicked.connect(self.switchMode)
        self.menuBar.addWidget(switchButton)

        self.menuBar.addSeparator()

#        decreaseFontSizeButton = QPushButton()
#        decreaseFontSizeButton.setToolTip(config.thisTranslation["menu2_smaller"])
#        decreaseFontSizeButtonFile = os.path.join("htmlResources", "fontMinus.png")
#        decreaseFontSizeButton.setIcon(QIcon(decreaseFontSizeButtonFile))
#        decreaseFontSizeButton.clicked.connect(self.decreaseNoteEditorFontSize)
#        self.menuBar.addWidget(decreaseFontSizeButton)
#
#        increaseFontSizeButton = QPushButton()
#        increaseFontSizeButton.setToolTip(config.thisTranslation["menu2_larger"])
#        increaseFontSizeButtonFile = os.path.join("htmlResources", "fontPlus.png")
#        increaseFontSizeButton.setIcon(QIcon(increaseFontSizeButtonFile))
#        increaseFontSizeButton.clicked.connect(self.increaseNoteEditorFontSize)
#        self.menuBar.addWidget(increaseFontSizeButton)

#        self.menuBar.addSeparator()

        self.searchLineEdit = QLineEdit()
        self.searchLineEdit.setClearButtonEnabled(True)
        self.searchLineEdit.setToolTip(config.thisTranslation["menu5_search"])
        self.searchLineEdit.setMaximumWidth(400)
        self.searchLineEdit.returnPressed.connect(self.searchLineEntered)
        self.menuBar.addWidget(self.searchLineEdit)

        self.menuBar.addSeparator()

        toolBarButton = QPushButton()
        toolBarButton.setToolTip(config.thisTranslation["note_toolbar"])
        toolBarButtonFile = os.path.join("htmlResources", "toolbar.png")
        toolBarButton.setIcon(QIcon(toolBarButtonFile))
        toolBarButton.clicked.connect(self.toggleToolbar)
        self.menuBar.addWidget(toolBarButton)

        toolBarButton = QPushButton()
        toolBarButton.setToolTip(config.thisTranslation["note_textUtility"])
        toolBarButtonFile = os.path.join("htmlResources", "textUtility.png")
        toolBarButton.setIcon(QIcon(toolBarButtonFile))
        toolBarButton.clicked.connect(self.toggleTextUtility)
        self.menuBar.addWidget(toolBarButton)

        self.menuBar.addSeparator()

    def setupMenuBarFullIconSize(self):

        self.menuBar = QToolBar()
        self.menuBar.setWindowTitle(config.thisTranslation["note_title"])
        self.menuBar.setContextMenuPolicy(Qt.PreventContextMenu)
        # In QWidget, self.menuBar is treated as the menubar without the following line
        # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window
        self.addToolBar(self.menuBar)

        iconFile = os.path.join("htmlResources", "newfile.png")
        self.menuBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + N]".format(config.thisTranslation["menu7_create"]), self.newNoteFile)

        iconFile = os.path.join("htmlResources", "open.png")
        self.menuBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + O]".format(config.thisTranslation["menu7_open"]), self.openFileDialog)

        self.menuBar.addSeparator()

        iconFile = os.path.join("htmlResources", "save.png")
        self.menuBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + S]".format(config.thisTranslation["note_save"]), self.saveNote)

        iconFile = os.path.join("htmlResources", "saveas.png")
        self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["note_saveAs"], self.openSaveAsDialog)

        self.menuBar.addSeparator()

        iconFile = os.path.join("htmlResources", "print.png")
        self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["note_print"], self.printNote)

        self.menuBar.addSeparator()

        iconFile = os.path.join("htmlResources", "switch.png")
        self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["note_mode"], self.switchMode)

        self.menuBar.addSeparator()

#        iconFile = os.path.join("htmlResources", "fontMinus.png")
#        self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["menu2_smaller"], self.decreaseNoteEditorFontSize)
#
#        iconFile = os.path.join("htmlResources", "fontPlus.png")
#        self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["menu2_larger"], self.increaseNoteEditorFontSize)

#        self.menuBar.addSeparator()

        self.searchLineEdit = QLineEdit()
        self.searchLineEdit.setToolTip("{0}\n[Ctrl/Cmd + F]".format(config.thisTranslation["menu5_search"]))
        self.searchLineEdit.setMaximumWidth(400)
        self.searchLineEdit.returnPressed.connect(self.searchLineEntered)
        self.menuBar.addWidget(self.searchLineEdit)

        self.menuBar.addSeparator()

        iconFile = os.path.join("htmlResources", "toolbar.png")
        self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["note_toolbar"], self.toggleToolbar)

        iconFile = os.path.join("htmlResources", "textUtility.png")
        self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["note_textUtility"], self.toggleTextUtility)

        self.menuBar.addSeparator()

    def toggleToolbar(self):
        if config.hideNoteEditorStyleToolbar:
            self.toolBar.show()
            config.hideNoteEditorStyleToolbar = False
        else:
            self.toolBar.hide()
            config.hideNoteEditorStyleToolbar = True

    def toggleTextUtility(self):
        if config.hideNoteEditorTextUtility:
            self.ttsToolbar.show()
            self.translateToolbar.show()
            config.hideNoteEditorTextUtility = False
        else:
            self.ttsToolbar.hide()
            self.translateToolbar.hide()
            config.hideNoteEditorTextUtility = True

    def printNote(self):
        #document = QTextDocument("Sample Page")
        document = self.editor.document()
        printer = QPrinter()

        myPrintDialog = QPrintDialog(printer, self)
        if myPrintDialog.exec_() == QDialog.Accepted:
            return document.print_(printer)

    def setupToolBar(self):
        if config.toolBarIconFullSize:
            self.setupToolBarFullIconSize()
        else:
            self.setupToolBarStandardIconSize()

    def setupToolBarStandardIconSize(self):

        self.toolBar = QToolBar()
        self.toolBar.setWindowTitle(config.thisTranslation["noteTool_title"])
        self.toolBar.setContextMenuPolicy(Qt.PreventContextMenu)
        # self.toolBar can be treated as an individual widget and positioned with a specified layout
        # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window
        self.addToolBar(self.toolBar)
        
        items = (
            ("noteTool_textFont", "font.png", self.format_font),
            ("noteTool_textColor", "textColor.png", self.format_textColor),
            ("noteTool_textBackgroundColor", "textBgColor.png", self.format_textBackgroundColor),
        )
        for item in items:
            toolTip, icon, action = item
            self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar)

        self.toolBar.addSeparator()

        items = (
            ("noteTool_header1", "header1.png", self.format_header1),
            ("noteTool_header2", "header2.png", self.format_header2),
            ("noteTool_header3", "header3.png", self.format_header3),
        )
        for item in items:
            toolTip, icon, action = item
            self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar)

        self.toolBar.addSeparator()
        
        items = (
            ("{0}\n[Ctrl/Cmd + B]".format(config.thisTranslation["noteTool_bold"]), "bold.png", self.format_bold),
            ("{0}\n[Ctrl/Cmd + I]".format(config.thisTranslation["noteTool_italic"]), "italic.png", self.format_italic),
            ("{0}\n[Ctrl/Cmd + U]".format(config.thisTranslation["noteTool_underline"]), "underline.png", self.format_underline),
        )
        for item in items:
            toolTip, icon, action = item
            self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar, translation=False)

        self.toolBar.addSeparator()

        items = (
            ("noteTool_superscript", "superscript.png", self.format_superscript),
            ("noteTool_subscript", "subscript.png", self.format_subscript),
        )
        for item in items:
            toolTip, icon, action = item
            self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar)

        self.toolBar.addSeparator()

        self.parent.addStandardIconButton("{0}\n[Ctrl/Cmd + M]\n\n{1}\n* {4}\n* {5}\n* {6}\n\n{2}\n*1 {4}\n*2 {5}\n*3 {6}\n\n{3}\n{10}{4}|{5}|{6}{11}\n{10}{7}|{8}|{9}{11}".format(config.thisTranslation["noteTool_trans0"], config.thisTranslation["noteTool_trans1"], config.thisTranslation["noteTool_trans2"], config.thisTranslation["noteTool_trans3"], config.thisTranslation["noteTool_no1"], config.thisTranslation["noteTool_no2"], config.thisTranslation["noteTool_no3"], config.thisTranslation["noteTool_no4"], config.thisTranslation["noteTool_no5"], config.thisTranslation["noteTool_no6"], "{", "}"), "custom.png", self.format_custom, self.toolBar, translation=False)

        self.toolBar.addSeparator()

        items = (
            ("noteTool_left", "align_left.png", self.format_left),
            ("noteTool_centre", "align_center.png", self.format_center),
            ("noteTool_right", "align_right.png", self.format_right),
            ("noteTool_justify", "align_justify.png", self.format_justify),
        )
        for item in items:
            toolTip, icon, action = item
            self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar)

        self.toolBar.addSeparator()

        self.parent.addStandardIconButton("{0}\n[Ctrl/Cmd + D]".format(config.thisTranslation["noteTool_delete"]), "clearFormat.png", self.format_clear, self.toolBar, translation=False)

        self.toolBar.addSeparator()

        items = (
            ("noteTool_hyperlink", "hyperlink.png", self.openHyperlinkDialog),
            ("noteTool_externalImage", "gallery.png", self.openImageDialog),
        )
        for item in items:
            toolTip, icon, action = item
            self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar)

        self.toolBar.addSeparator()

        items = (
            ("noteTool_image", "addImage.png", self.addInternalImage),
            ("noteTool_exportImage", "export.png", self.exportNoteImages),
        )
        for item in items:
            toolTip, icon, action = item
            self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar)

        self.toolBar.addSeparator()

    def setupToolBarFullIconSize(self):

        self.toolBar = QToolBar()
        self.toolBar.setWindowTitle(config.thisTranslation["noteTool_title"])
        self.toolBar.setContextMenuPolicy(Qt.PreventContextMenu)
        # self.toolBar can be treated as an individual widget and positioned with a specified layout
        # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window
        self.addToolBar(self.toolBar)

        iconFile = os.path.join("htmlResources", "font.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_textFont"], self.format_font)

        iconFile = os.path.join("htmlResources", "textColor.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_textColor"], self.format_textColor)

        iconFile = os.path.join("htmlResources", "textBgColor.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_textBackgroundColor"], self.format_textBackgroundColor)

        self.toolBar.addSeparator()

        iconFile = os.path.join("htmlResources", "header1.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_header1"], self.format_header1)

        iconFile = os.path.join("htmlResources", "header2.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_header2"], self.format_header2)

        iconFile = os.path.join("htmlResources", "header3.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_header3"], self.format_header3)

        self.toolBar.addSeparator()

        iconFile = os.path.join("htmlResources", "bold.png")
        self.toolBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + B]".format(config.thisTranslation["noteTool_bold"]), self.format_bold)

        iconFile = os.path.join("htmlResources", "italic.png")
        self.toolBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + I]".format(config.thisTranslation["noteTool_italic"]), self.format_italic)

        iconFile = os.path.join("htmlResources", "underline.png")
        self.toolBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + U]".format(config.thisTranslation["noteTool_underline"]), self.format_underline)

        self.toolBar.addSeparator()

        iconFile = os.path.join("htmlResources", "custom.png")
        self.toolBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + M]\n\n{1}\n* {4}\n* {5}\n* {6}\n\n{2}\n*1 {4}\n*2 {5}\n*3 {6}\n\n{3}\n{10}{4}|{5}|{6}{11}\n{10}{7}|{8}|{9}{11}".format(config.thisTranslation["noteTool_trans0"], config.thisTranslation["noteTool_trans1"], config.thisTranslation["noteTool_trans2"], config.thisTranslation["noteTool_trans3"], config.thisTranslation["noteTool_no1"], config.thisTranslation["noteTool_no2"], config.thisTranslation["noteTool_no3"], config.thisTranslation["noteTool_no4"], config.thisTranslation["noteTool_no5"], config.thisTranslation["noteTool_no6"], "{", "}"), self.format_custom)

        self.toolBar.addSeparator()

        iconFile = os.path.join("htmlResources", "align_left.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_left"], self.format_left)

        iconFile = os.path.join("htmlResources", "align_center.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_centre"], self.format_center)

        iconFile = os.path.join("htmlResources", "align_right.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_right"], self.format_right)

        iconFile = os.path.join("htmlResources", "align_justify.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_justify"], self.format_justify)

        self.toolBar.addSeparator()

        iconFile = os.path.join("htmlResources", "clearFormat.png")
        self.toolBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + D]".format(config.thisTranslation["noteTool_delete"]), self.format_clear)

        self.toolBar.addSeparator()

        iconFile = os.path.join("htmlResources", "hyperlink.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_hyperlink"], self.openHyperlinkDialog)

        iconFile = os.path.join("htmlResources", "gallery.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_externalImage"], self.openImageDialog)

        self.toolBar.addSeparator()

        iconFile = os.path.join("htmlResources", "addImage.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_image"], self.addInternalImage)

        iconFile = os.path.join("htmlResources", "export.png")
        self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_exportImage"], self.exportNoteImages)

        self.toolBar.addSeparator()

    def setupLayout(self):
        self.editor = QTextEdit()  
        self.editor.setStyleSheet("font-family:'{0}'; font-size:{1}pt;".format(config.font, config.fontSize));
        self.editor.textChanged.connect(self.textChanged)
        self.setCentralWidget(self.editor)

        #self.layout = QGridLayout()
        #self.layout.setMenuBar(self.menuBar)
        #self.layout.addWidget(self.toolBar, 0, 0)
        #self.layout.addWidget(self.editor, 1, 0)
        #self.setLayout(self.layout)

    # adjustment of note editor font size
    def increaseNoteEditorFontSize(self):
        if self.html:
            self.editor.selectAll()
            config.noteEditorFontSize += 1
            self.editor.setFontPointSize(config.noteEditorFontSize)
            self.hide()
            self.show()

    def decreaseNoteEditorFontSize(self):
        if self.html and not config.noteEditorFontSize == 0:
            self.editor.selectAll()
            config.noteEditorFontSize -= 1
            self.editor.setFontPointSize(config.noteEditorFontSize)
            self.hide()
            self.show()

    # search field entered
    def searchLineEntered(self):
        searchString = self.searchLineEdit.text()
        if searchString:
            cursor = self.editor.document().find(searchString, self.editor.textCursor())
        if cursor:
            self.editor.setTextCursor(cursor)
        self.hide()
        self.show()

    def focusSearchField(self):
        self.searchLineEdit.setFocus()

    # track if the text being modified
    def textChanged(self):
        if self.parent.noteSaved:
            self.parent.noteSaved = False
            self.updateWindowTitle()

    # display content when first launched
    def displayInitialContent(self):
        if self.noteType == "file":
            if self.noteFileName:
                self.openNoteFile(self.noteFileName)
            else:
                self.newNoteFile()
        else:
            self.openBibleNote()

        self.editor.selectAll()
        self.editor.setFontPointSize(config.noteEditorFontSize)
        self.editor.moveCursor(QTextCursor.Start, QTextCursor.MoveAnchor)

        self.parent.noteSaved = True

    def getEmptyPage(self):
        strict = ''
        if config.includeStrictDocTypeInNote:
            strict = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">'
        return """{4}<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li {0} white-space: pre-wrap; {1}
</style></head><body style="font-family:'{2}'; font-size:{3}pt; font-weight:400; font-style:normal;">
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html>"""\
            .format("{", "}", config.font, config.fontSize, strict)

    # load chapter / verse notes from sqlite database
    def openBibleNote(self):
        if self.noteType == "book":
            note = NoteService.getBookNote(self.b)
        elif self.noteType == "chapter":
            note = NoteService.getChapterNote(self.b, self.c)
        elif self.noteType == "verse":
            note = NoteService.getVerseNote(self.b, self.c, self.v)
        if note == config.thisTranslation["empty"]:
            note = self.getEmptyPage()
        else:
            note = self.fixNoteFont(note)
        if self.html:
            self.editor.setHtml(note)
        else:
            self.editor.setPlainText(note)

    # File I / O
    def newNoteFile(self):
        if self.parent.noteSaved:
            self.newNoteFileAction()
        elif self.parent.warningNotSaved():
            self.newNoteFileAction()

    def newNoteFileAction(self):
        self.noteType = "file"
        self.noteFileName = ""
        #self.editor.clear()
        defaultText = self.getEmptyPage()
        if self.html:
            self.editor.setHtml(defaultText)
        else:
            self.editor.setPlainText(defaultText)
        self.parent.noteSaved = True
        self.updateWindowTitle()
        self.hide()
        self.show()

    def openFileDialog(self):
        if self.parent.noteSaved:
            self.openFileDialogAction()
        elif self.parent.warningNotSaved():
            self.openFileDialogAction()

    def openFileDialogAction(self):
        options = QFileDialog.Options()
        fileName, filtr = QFileDialog.getOpenFileName(self,
                config.thisTranslation["menu7_open"],
                "notes",
                "UniqueBible.app Note Files (*.uba);;HTML Files (*.html);;HTM Files (*.htm);;All Files (*)", "", options)
        if fileName:
            self.openNoteFile(fileName)

    def openNoteFile(self, fileName):
        try:
            f = open(fileName, "r", encoding="utf-8")
        except:
            print("Failed to open '{0}'".format(fileName))
        note = f.read()
        f.close()
        self.noteType = "file"
        self.noteFileName = fileName
        note = self.fixNoteFont(note)
        if self.html:
            self.editor.setHtml(note)
        else:
            self.editor.setPlainText(note)
        self.parent.noteSaved = True
        self.updateWindowTitle()
        self.hide()
        self.show()

    def saveNote(self):
        if self.html:
            note = self.editor.toHtml()
        else:
            note = self.editor.toPlainText()
        note = self.fixNoteFont(note)
        if self.noteType == "book":
            NoteService.saveBookNote(self.b, note)
            if config.openBibleNoteAfterSave:
                self.parent.openBookNote(self.b,)
            self.parent.noteSaved = True
            self.updateWindowTitle()
        elif self.noteType == "chapter":
            NoteService.saveChapterNote(self.b, self.c, note)
            if config.openBibleNoteAfterSave:
                self.parent.openChapterNote(self.b, self.c)
            self.parent.noteSaved = True
            self.updateWindowTitle()
        elif self.noteType == "verse":
            NoteService.saveVerseNote(self.b, self.c, self.v, note)
            if config.openBibleNoteAfterSave:
                self.parent.openVerseNote(self.b, self.c, self.v)
            self.parent.noteSaved = True
            self.updateWindowTitle()
        elif self.noteType == "file":
            if self.noteFileName == "":
                self.openSaveAsDialog()
            else:
                self.saveAsNote(self.noteFileName)

    def openSaveAsDialog(self):
        if self.noteFileName:
            *_, defaultName = os.path.split(self.noteFileName)
        else:
            defaultName = "new.uba"
        options = QFileDialog.Options()
        fileName, filtr = QFileDialog.getSaveFileName(self,
                config.thisTranslation["note_saveAs"],
                os.path.join("notes", defaultName),
                "UniqueBible.app Note Files (*.uba);;HTML Files (*.html);;HTM Files (*.htm);;All Files (*)", "", options)
        if fileName:
            if not "." in os.path.basename(fileName):
                fileName = fileName + ".uba"
            self.saveAsNote(fileName)

    def saveAsNote(self, fileName):
        if self.html:
            note = self.editor.toHtml()
        else:
            note = self.editor.toPlainText()
        note = self.fixNoteFont(note)
        f = open(fileName, "w", encoding="utf-8")
        f.write(note)
        f.close()
        self.noteFileName = fileName
        self.parent.addExternalFileHistory(fileName)
        self.parent.setExternalFileButton()
        self.parent.noteSaved = True
        self.updateWindowTitle()

    def fixNoteFont(self, note):
        note = re.sub("<body style={0}[ ]*?font-family:[ ]*?'[^']*?';[ ]*?font-size:[ ]*?[0-9]+?pt;".format('"'), "<body style={0}font-family:'{1}'; font-size:{2}pt;".format('"', config.font, config.fontSize), note)
        if not config.includeStrictDocTypeInNote:
            note = re.sub("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">\n""", "", note)
        return note


    # formatting styles
    def format_clear(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                selectedText = """<span style="font-family:'{0}'; font-size:{1}pt;">{2}</span>""".format(config.font, config.fontSize, selectedText)
                self.editor.insertHtml(selectedText)
            else:
                selectedText = re.sub("<[^\n<>]*?>", "", selectedText)
                self.editor.insertPlainText(selectedText)
        else:
            self.selectTextFirst()

    def format_header1(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                self.editor.insertHtml("<h1>{0}</h1>".format(selectedText))
            else:
                self.editor.insertPlainText("<h1>{0}</h1>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_header2(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                self.editor.insertHtml("<h2>{0}</h2>".format(selectedText))
            else:
                self.editor.insertPlainText("<h2>{0}</h2>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_header3(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                self.editor.insertHtml("<h3>{0}</h3>".format(selectedText))
            else:
                self.editor.insertPlainText("<h3>{0}</h3>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_font(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            ok, font = QFontDialog.getFont(QFont(config.font, config.fontSize), self)
            if ok:
                if self.html:
                    self.editor.setCurrentFont(font)
                else:
                    fontFamily, fontSize, i1, i2, fontWeight, italic, underline, strikeout, *_ = font.key().split(",")
                    spanTag = """<span style="font-family:'{0}'; font-size:{1}pt;""".format(fontFamily, fontSize)
                    # add font weight
                    if fontWeight == "25":
                        spanTag += " font-weight:200;"
                    elif fontWeight == "75":
                        spanTag += " font-weight:600;"
                    # add italic style
                    if italic == "1":
                        spanTag += " font-style:italic;"
                    # add both underline and strikeout style
                    if underline == "1" and strikeout == "1":
                        spanTag += " text-decoration: underline line-through;"
                    # add underline style
                    elif underline == "1":
                        spanTag += " text-decoration: underline;"
                    # add strikeout style
                    elif strikeout == "1":
                        spanTag += " text-decoration: line-through;"
                    # close tag
                    spanTag += '">'
                    self.editor.insertPlainText("{0}{1}</span>".format(spanTag, selectedText))
        else:
            self.selectTextFirst()
        
    def format_textColor(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            color = QColorDialog.getColor(Qt.darkRed, self)
            if color.isValid():
                if self.html:
                    self.editor.setTextColor(color)
                else:
                    self.editor.insertPlainText('<span style="color:{0};">{1}</span>'.format(color.name(), self.editor.textCursor().selectedText()))
        else:
            self.selectTextFirst()

    def format_textBackgroundColor(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            color = QColorDialog.getColor(Qt.yellow, self)
            if color.isValid():
                if self.html:
                    self.editor.setTextBackgroundColor(color)
                else:
                    self.editor.insertPlainText('<span style="background-color:{0};">{1}</span>'.format(color.name(), selectedText))
        else:
            self.selectTextFirst()

    def format_bold(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                # Reference: https://doc.qt.io/qt-5/qfont.html#Weight-enum
                # Bold = 75
                self.editor.setFontWeight(75)
            else:
                self.editor.insertPlainText("<b>{0}</b>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_italic(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                self.editor.setFontItalic(True)
            else:
                self.editor.insertPlainText("<i>{0}</i>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_underline(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                self.editor.setFontUnderline(True)
            else:
                self.editor.insertPlainText("<u>{0}</u>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_superscript(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                self.editor.insertHtml("<sup>{0}</sup>".format(selectedText))
            else:
                self.editor.insertPlainText("<sup>{0}</sup>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_subscript(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                self.editor.insertHtml("<sub>{0}</sub>".format(selectedText))
            else:
                self.editor.insertPlainText("<sub>{0}</sub>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_center(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                self.editor.setAlignment(Qt.AlignCenter)
            else:
                self.editor.insertPlainText("<div style='text-align:center;'>{0}</div>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_justify(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                self.editor.setAlignment(Qt.AlignJustify)
            else:
                self.editor.insertPlainText("<div style='text-align:justify;'>{0}</div>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_left(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                self.editor.setAlignment(Qt.AlignLeft)
            else:
                self.editor.insertPlainText("<div style='text-align:left;'>{0}</div>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_right(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            if self.html:
                self.editor.setAlignment(Qt.AlignRight)
            else:
                self.editor.insertPlainText("<div style='text-align:right;'>{0}</div>".format(selectedText))
        else:
            self.selectTextFirst()

    def format_custom(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            selectedText = self.customFormat(selectedText)
            if self.html:
                self.editor.insertHtml(selectedText)
            else:
                self.editor.insertPlainText(selectedText)
        else:
            self.selectTextFirst()

    def customFormat(self, text):
        # QTextEdit's line break character by pressing ENTER in plain & html mode "
"
        # please note that "
" is not an empty string
        text = text.replace("
", "\n")

        text = re.sub("^\*[0-9]+? (.*?)$", r"<ol><li>\1</li></ol>", text, flags=re.M)
        text = text.replace("</ol>\n<ol>", "\n")
        text = re.sub("^\* (.*?)$", r"<ul><li>\1</li></ul>", text, flags=re.M)
        text = text.replace("</ul>\n<ul>", "\n")
        text = re.sub("^{.*?}$", self.formatHTMLTable, text, flags=re.M)
        text = text.replace("</table>\n<table>", "\n")

        # add style to table here
        # please note that QTextEdit supports HTML 4, rather than HTML 5
        # take this old reference: https://www.w3schools.com/tags/tag_table.asp
        text = text.replace('<table>', '<table border="1" cellpadding="5">')

        # convert back to QTextEdit linebreak
        text = text.replace("\n", "
")

        # wrap with default font and font-size
        text = """<span style="font-family:'{0}'; font-size:{1}pt;">{2}</span>""".format(config.font, config.fontSize, text)
        return text

    def formatHTMLTable(self, match):
        row = match.group()[1:-1]
        row = "".join(["<td>{0}</td>".format(cell) for cell in row.split("|")])
        return "<table><tr>{0}</tr></table>".format(row)

    def addInternalImage(self):
        self.openImageDialog(external=False)

    def openImageDialog(self, external=True):
        options = QFileDialog.Options()
        fileName, filtr = QFileDialog.getOpenFileName(self,
                config.thisTranslation["html_open"],
                self.parent.openFileNameLabel.text(),
                "JPG Files (*.jpg);;JPEG Files (*.jpeg);;PNG Files (*.png);;GIF Files (*.gif);;BMP Files (*.bmp);;All Files (*)", "", options)
        if fileName:
            if external:
                self.linkExternalImage(fileName)
            else:
                self.embedImage(fileName)

    def embedImage(self, fileName):
        name, extension = os.path.splitext(os.path.basename(fileName))
        with open(fileName, "rb") as fileObject:
            binaryData = fileObject.read()
            encodedData = base64.b64encode(binaryData)
            asciiString = encodedData.decode('ascii')
            imageTag = '<img src="data:image/{2};base64,{0}" alt="{1}">'.format(asciiString, name, extension[1:])
            if self.html:
                self.editor.insertHtml(imageTag)
            else:
                self.editor.insertPlainText(imageTag)

    def exportNoteImages(self):
        options = QFileDialog.DontResolveSymlinks | QFileDialog.ShowDirsOnly
        directory = QFileDialog.getExistingDirectory(self,
                config.thisTranslation["select_a_folder"],
                self.parent.directoryLabel.text(), options)
        if directory:
            if self.html:
                htmlText = self.editor.toHtml()
            else:
                htmlText = self.editor.toPlainText()
            searchPattern = r'src=(["{0}])data:image/([^<>]+?);[ ]*?base64,[ ]*?([^ <>]+?)\1'.format("'")
            for counter, value in enumerate(re.findall(searchPattern, htmlText)):
                *_, ext, asciiString = value
                binaryString = asciiString.encode("ascii")
                binaryData = base64.b64decode(binaryString)
                imageFilePath = os.path.join(directory, "image{0}.{1}".format(counter + 1, ext))
                with open(imageFilePath, "wb") as fileObject:
                    fileObject.write(binaryData)

    def linkExternalImage(self, fileName):
        imageTag = '<img src="{0}" alt="UniqueBible.app">'.format(fileName)
        if self.html:
            self.editor.insertHtml(imageTag)
        else:
            self.editor.insertPlainText(imageTag)

    def openHyperlinkDialog(self):
        selectedText = self.editor.textCursor().selectedText()
        if selectedText:
            text, ok = QInputDialog.getText(self, "UniqueBible.app",
                    config.thisTranslation["noteTool_hyperlink"], QLineEdit.Normal,
                    selectedText)
            if ok and text != '':
                hyperlink = '<a href="{0}">{1}</a>'.format(text, selectedText)
                hyperlink = """<span style="font-family:'{0}'; font-size:{1}pt;">{2}</span>""".format(config.font, config.fontSize, hyperlink)
                if self.html:
                    self.editor.insertHtml(hyperlink)
                else:
                    self.editor.insertPlainText(hyperlink)
        else:
            self.selectTextFirst()

    def setupTextUtility(self):

        self.ttsToolbar = QToolBar()
        self.ttsToolbar.setWindowTitle(config.thisTranslation["noteTool_title"])
        self.ttsToolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        # self.toolBar can be treated as an individual widget and positioned with a specified layout
        # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window
        self.addToolBar(self.ttsToolbar)

        self.languageCombo = QComboBox()
        self.ttsToolbar.addWidget(self.languageCombo)
        if config.espeak:
            languages = TtsLanguages().isoLang2epeakLang
        else:
            languages = TtsLanguages().isoLang2qlocaleLang
        self.languageCodes = list(languages.keys())
        for code in self.languageCodes:
            self.languageCombo.addItem(languages[code][1])
        # Check if selected tts engine has the language user specify.
        if not (config.ttsDefaultLangauge in self.languageCodes):
            config.ttsDefaultLangauge = "en"
        # Set initial item
        initialIndex = self.languageCodes.index(config.ttsDefaultLangauge)
        self.languageCombo.setCurrentIndex(initialIndex)

        button = QPushButton(config.thisTranslation["speak"])
        button.setToolTip(config.thisTranslation["speak"])
        button.clicked.connect(self.speakText)
        self.ttsToolbar.addWidget(button)
        button = QPushButton(config.thisTranslation["stop"])
        button.setToolTip(config.thisTranslation["stop"])
        button.clicked.connect(self.parent.textCommandParser.stopTtsAudio)
        self.ttsToolbar.addWidget(button)

        self.translateToolbar = QToolBar()
        self.translateToolbar.setWindowTitle(config.thisTranslation["noteTool_title"])
        self.translateToolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        # self.toolBar can be treated as an individual widget and positioned with a specified layout
        # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window
        self.addToolBar(self.translateToolbar)

        self.fromLanguageCombo = QComboBox()
        self.translateToolbar.addWidget(self.fromLanguageCombo)
        self.fromLanguageCombo.addItems(["[Auto]"] +Translator.fromLanguageNames)
        initialIndex = 0
        self.fromLanguageCombo.setCurrentIndex(initialIndex)

        button = QPushButton(config.thisTranslation["context1_translate"])
        button.setToolTip(config.thisTranslation["context1_translate"])
        button.clicked.connect(self.translateText)
        self.translateToolbar.addWidget(button)

        self.toLanguageCombo = QComboBox()
        self.translateToolbar.addWidget(self.toLanguageCombo)
        self.toLanguageCombo.addItems(Translator.toLanguageNames)
        initialIndex = Translator.toLanguageNames.index(config.userLanguage)
        self.toLanguageCombo.setCurrentIndex(initialIndex)

    def speakText(self):
        text = self.editor.textCursor().selectedText()
        if text:
            if config.isTtsInstalled:
                if ":::" in text:
                    text = text.split(":::")[-1]
                command = "SPEAK:::{0}:::{1}".format(self.languageCodes[self.languageCombo.currentIndex()], text)
                self.parent.runTextCommand(command)
            else:
                self.displayMessage(config.thisTranslation["message_noSupport"])
        else:
            self.selectTextFirst()

    def translateText(self):
        text = self.editor.textCursor().selectedText()
        if text:
            translator = Translator()
            if translator.language_translator is not None:
                fromLanguage = Translator.fromLanguageCodes[self.fromLanguageCombo.currentIndex() - 1] if self.fromLanguageCombo.currentIndex() != 0 else translator.identify(text)
                toLanguage = Translator.toLanguageCodes[self.toLanguageCombo.currentIndex()]
                result = translator.translate(text, fromLanguage, toLanguage)
                self.editor.insertPlainText(result)
            else:
                self.displayMessage(config.thisTranslation["ibmWatsonNotEnalbed"])
                webbrowser.open("https://github.com/eliranwong/UniqueBible/wiki/IBM-Watson-Language-Translator")
        else:
            self.selectTextFirst()

    def selectTextFirst(self):
        self.displayMessage(config.thisTranslation["selectTextFirst"])

    def displayMessage(self, message="", title="UniqueBible"):
        reply = QMessageBox.information(self, title, message)
Beispiel #7
0
class QtAbout(QDialog):
    """Qt dialog window for displaying 'About napari' information.

    Parameters
    ----------
    parent : QWidget, optional
        Parent of the dialog, to correctly inherit and apply theme.
        Default is None.

    Attributes
    ----------
    citationCopyButton : napari._qt.qt_about.QtCopyToClipboardButton
        Button to copy citation information to the clipboard.
    citationTextBox : qtpy.QtWidgets.QTextEdit
        Text box containing napari citation information.
    citation_layout : qtpy.QtWidgets.QHBoxLayout
        Layout widget for napari citation information.
    infoCopyButton : napari._qt.qt_about.QtCopyToClipboardButton
        Button to copy napari version information to the clipboard.
    info_layout : qtpy.QtWidgets.QHBoxLayout
        Layout widget for napari version information.
    infoTextBox : qtpy.QtWidgets.QTextEdit
        Text box containing napari version information.
    layout : qtpy.QtWidgets.QVBoxLayout
        Layout widget for the entire 'About napari' dialog.
    """
    def __init__(self, parent=None):
        super().__init__(parent)

        self.layout = QVBoxLayout()

        # Description
        title_label = QLabel(
            trans._(
                "<b>napari: a multi-dimensional image viewer for python</b>"))
        title_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
        self.layout.addWidget(title_label)

        # Add information
        self.infoTextBox = QTextEdit()
        self.infoTextBox.setTextInteractionFlags(Qt.TextSelectableByMouse)
        self.infoTextBox.setLineWrapMode(QTextEdit.NoWrap)
        # Add text copy button
        self.infoCopyButton = QtCopyToClipboardButton(self.infoTextBox)
        self.info_layout = QHBoxLayout()
        self.info_layout.addWidget(self.infoTextBox, 1)
        self.info_layout.addWidget(self.infoCopyButton, 0, Qt.AlignTop)
        self.info_layout.setAlignment(Qt.AlignTop)
        self.layout.addLayout(self.info_layout)

        self.infoTextBox.setText(sys_info(as_html=True))
        self.infoTextBox.setMinimumSize(
            int(self.infoTextBox.document().size().width() + 19),
            int(min(self.infoTextBox.document().size().height() + 10, 500)),
        )

        self.layout.addWidget(QLabel(trans._('<b>citation information:</b>')))
        self.citationTextBox = QTextEdit(citation_text)
        self.citationTextBox.setFixedHeight(64)
        self.citationCopyButton = QtCopyToClipboardButton(self.citationTextBox)
        self.citation_layout = QHBoxLayout()
        self.citation_layout.addWidget(self.citationTextBox, 1)
        self.citation_layout.addWidget(self.citationCopyButton, 0, Qt.AlignTop)
        self.layout.addLayout(self.citation_layout)

        self.setLayout(self.layout)

    @staticmethod
    def showAbout(parent=None):
        """Display the 'About napari' dialog box.

        Parameters
        ----------
        parent : QWidget, optional
            Parent of the dialog, to correctly inherit and apply theme.
            Default is None.
        """
        d = QtAbout(parent)
        d.setObjectName('QtAbout')
        d.setWindowTitle(trans._('About'))
        d.setWindowModality(Qt.ApplicationModal)
        d.exec_()
Beispiel #8
0
class QtAbout(QDialog):
    """Qt dialog window for displaying 'About napari' information.

    Attributes
    ----------
    citationCopyButton : napari._qt.qt_about.QtCopyToClipboardButton
        Button to copy citation information to the clipboard.
    citationTextBox : qtpy.QtWidgets.QTextEdit
        Text box containing napari citation information.
    citation_layout : qtpy.QtWidgets.QHBoxLayout
        Layout widget for napari citation information.
    infoCopyButton : napari._qt.qt_about.QtCopyToClipboardButton
        Button to copy napari version information to the clipboard.
    info_layout : qtpy.QtWidgets.QHBoxLayout
        Layout widget for napari version information.
    infoTextBox : qtpy.QtWidgets.QTextEdit
        Text box containing napari version information.
    layout : qtpy.QtWidgets.QVBoxLayout
        Layout widget for the entire 'About napari' dialog.
    """
    def __init__(self):
        super().__init__()

        self.layout = QVBoxLayout()

        # Description
        title_label = QLabel(
            "<b>napari: a multi-dimensional image viewer for python</b>")
        title_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
        self.layout.addWidget(title_label)

        # Add information
        self.infoTextBox = QTextEdit()
        self.infoTextBox.setTextInteractionFlags(Qt.TextSelectableByMouse)
        self.infoTextBox.setLineWrapMode(QTextEdit.NoWrap)
        # Add text copy button
        self.infoCopyButton = QtCopyToClipboardButton(self.infoTextBox)
        self.info_layout = QHBoxLayout()
        self.info_layout.addWidget(self.infoTextBox, 1)
        self.info_layout.addWidget(self.infoCopyButton, 0, Qt.AlignTop)
        self.info_layout.setAlignment(Qt.AlignTop)
        self.layout.addLayout(self.info_layout)

        self.infoTextBox.setText(sys_info(as_html=True))
        self.infoTextBox.setMinimumSize(
            self.infoTextBox.document().size().width() + 19,
            self.infoTextBox.document().size().height() + 10,
        )

        self.layout.addWidget(QLabel('<b>citation information:</b>'))
        self.citationTextBox = QTextEdit(citation_text)
        self.citationTextBox.setFixedHeight(64)
        self.citationCopyButton = QtCopyToClipboardButton(self.citationTextBox)
        self.citation_layout = QHBoxLayout()
        self.citation_layout.addWidget(self.citationTextBox, 1)
        self.citation_layout.addWidget(self.citationCopyButton, 0, Qt.AlignTop)
        self.layout.addLayout(self.citation_layout)

        self.setLayout(self.layout)

    @staticmethod
    def showAbout(qt_viewer):
        """Display the 'About napari' dialog box.

        Paramters
        ---------
        qt_viewer : QtViewer
            QtViewer instance that the `About napari` dialog box belongs to.
        """
        d = QtAbout()
        d.setObjectName('QtAbout')
        d.setStyleSheet(qt_viewer.styleSheet())
        d.setWindowTitle('About')
        d.setWindowModality(Qt.ApplicationModal)
        d.exec_()
Beispiel #9
0
    def on_edit(self):
        list_widget = self.toolbox.currentWidget()
        curr_row = list_widget.currentRow()

        if curr_row >= 0:
            group_name = self.toolbox.itemText(self.toolbox.currentIndex())
            curr_text = list_widget.currentItem().text()
            dic = self.node_info_dic[group_name][curr_text]
            edit_layout = QHBoxLayout()
            input_widget = QTextEdit()
            edit_layout.addWidget(input_widget)
            check_button = QPushButton(text='check')
            edit_layout.addWidget(check_button)
            input_widget.setText(repr(dic['params']))
            views = [('line_ctrl', 'text', 'Node Text', dic['text']),
                     ('check_ctrl', 'inputs_changeable',
                      'Input Ports Changeable', dic['ports_changeable'][0]),
                     ('check_ctrl', 'outputs_changeable',
                      'Output Ports Changeble', dic['ports_changeable'][1]),
                     ('editor_ctrl', 'code', 'Input Python Code', dic['code'],
                      'python'),
                     ('list_ctrl', 'inputs', 'Set Inputs',
                      [[None] * len(dic['inputs']),
                       dic['inputs']], lambda: None),
                     ('list_ctrl', 'outputs', 'Set Outputs',
                      [[None] * len(dic['outputs']),
                       dic['outputs']], lambda: None),
                     ('file_ctrl', 'icon', 'Set Icon', dic['icon']),
                     ('combo_ctrl', 'group', 'Group Name', dic['group'],
                      self.groups)]
            sp = PMGPanel(parent=None, views=views)
            dialog = QDialog(self)

            def verify():
                try:
                    text = input_widget.document().toPlainText()
                    l = eval(text)
                    if isinstance(l, list):
                        dialog2 = QDialog(dialog)
                        sp2 = PMGPanel(parent=None, views=l)
                        dialog2.setLayout(QHBoxLayout())
                        dialog2.layout().addWidget(sp2)
                        dialog2.layout().addLayout(edit_layout)
                        dialog2.exec_()
                except:
                    import traceback
                    traceback.print_exc()

            check_button.clicked.connect(verify)
            dialog.setLayout(QVBoxLayout())
            dialog.layout().addWidget(sp)
            dialog.layout().addLayout(edit_layout)
            dialog.exec_()
            dic = sp.get_value()
            params = None
            try:
                params = eval(input_widget.document().toPlainText())
            except:
                import traceback
                traceback.print_exc()
            group = self.get_current_list_widget_group()
            if isinstance(params, list):
                self.node_info_dic[group][curr_text]['params'] = params
            self.node_info_dic[group][curr_text]['text'] = dic['text']
            self.node_info_dic[group][curr_text]['icon'] = dic['icon']
            self.node_info_dic[group][curr_text]['code'] = dic['code']
            self.node_info_dic[group][curr_text]['inputs'] = dic['inputs'][1]
            self.node_info_dic[group][curr_text]['outputs'] = dic['outputs'][1]
            self.node_info_dic[group][curr_text]['group'] = dic['group']
            self.node_info_dic[group][curr_text]['ports_changeable'] = [
                dic['inputs_changeable'], dic['outputs_changeable']
            ]
            list_widget.item(curr_row).setText(dic['text'])
            self.save_node_templetes()
            self.load_nodes()
        else:
            return