def __init__(self, config_file, help_tool): QWidget.__init__(self) layout = QVBoxLayout() toolbar = QToolBar("toolbar") save_action = toolbar.addAction(resourceIcon("ide/disk"), "Save") save_action.triggered.connect(self.save) save_as_action = toolbar.addAction(resourceIcon("ide/save_as"), "Save As") save_as_action.triggered.connect(self.saveAs) # reload_icon = toolbar.style().standardIcon(QStyle.SP_BrowserReload) # reload_action = toolbar.addAction(reload_icon, "Reload") # reload_action.triggered.connect(self.reload) toolbar.addSeparator() toolbar.addAction(help_tool.getAction()) stretchy_separator = QWidget() stretchy_separator.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar.addWidget(stretchy_separator) search = SearchBox() search.setMaximumWidth(200) search.setContentsMargins(5, 2, 5, 2) toolbar.addWidget(search) layout.addWidget(toolbar) self.ide_panel = IdePanel() layout.addWidget(self.ide_panel, 1) self.config_file = config_file with open(config_file) as f: config_file_text = f.read() self.highlighter = KeywordHighlighter(self.ide_panel.document()) search.filterChanged.connect(self.highlighter.setSearchString) self.parseDefines(config_file_text) self.ide_panel.document().setPlainText(config_file_text) cursor = self.ide_panel.textCursor() cursor.setPosition(0) self.ide_panel.setTextCursor(cursor) self.ide_panel.setFocus() self.setLayout(layout)
def __init__(self, *args): QMainWindow.__init__(self, *args) self.plot = BodePlot(self) self.plot.setContentsMargins(5, 5, 5, 0) self.setContextMenuPolicy(Qt.NoContextMenu) self.setCentralWidget(self.plot) toolBar = QToolBar(self) self.addToolBar(toolBar) btnPrint = QToolButton(toolBar) btnPrint.setText("Print") btnPrint.setIcon(QIcon(QPixmap(print_xpm))) btnPrint.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) toolBar.addWidget(btnPrint) btnPrint.clicked.connect(self.print_) btnExport = QToolButton(toolBar) btnExport.setText("Export") btnExport.setIcon(QIcon(QPixmap(print_xpm))) btnExport.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) toolBar.addWidget(btnExport) btnExport.clicked.connect(self.exportDocument) toolBar.addSeparator() dampBox = QWidget(toolBar) dampLayout = QHBoxLayout(dampBox) dampLayout.setSpacing(0) dampLayout.addWidget(QWidget(dampBox), 10) # spacer dampLayout.addWidget(QLabel("Damping Factor", dampBox), 0) dampLayout.addSpacing(10) toolBar.addWidget(dampBox) self.statusBar() self.showInfo()
def fill_layout(self): """Fill grid layout""" import qwt for fname in get_tests(qwt): self.add_test(fname) toolbar = QToolBar(self) all_act = QAction(self.get_std_icon("DialogYesButton"), "", self) all_act.setIconText("Run all tests") all_act.triggered.connect(lambda checked: run_all_tests(wait=False)) folder_act = QAction(self.get_std_icon("DirOpenIcon"), "", self) folder_act.setIconText("Open tests folder") open_test_folder = lambda checked: os.startfile(TEST_PATH) folder_act.triggered.connect(open_test_folder) about_act = QAction(self.get_std_icon("FileDialogInfoView"), "", self) about_act.setIconText("About") about_act.triggered.connect(self.about) for action in (all_act, folder_act, None, about_act): if action is None: toolbar.addSeparator() else: toolbar.addAction(action) toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.addToolBar(toolbar)
class UiLinelistsWindow(object): # this code was taken as-is from the Designer. # Cleaning it up sounds like a lower priority # task for now. def setupUi(self, MainWindow, title): MainWindow.setWindowTitle(title) MainWindow.setObjectName("MainWindow") MainWindow.resize(600, 850) MainWindow.setMinimumSize(QSize(300, 350)) self.centralWidget = QWidget(MainWindow) self.centralWidget.setObjectName("centralWidget") self.gridLayout = QGridLayout(self.centralWidget) self.gridLayout.setContentsMargins(11, 11, 11, 11) self.gridLayout.setSpacing(6) self.gridLayout.setObjectName("gridLayout") self.horizontalLayout_5 = QHBoxLayout() self.horizontalLayout_5.setContentsMargins(11, 11, 11, 11) self.horizontalLayout_5.setSpacing(6) self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.lines_selected_label = QLabel(self.centralWidget) self.lines_selected_label.setObjectName("lines_selected_label") self.horizontalLayout_5.addWidget(self.lines_selected_label) self.label = QLabel(self.centralWidget) self.label.setObjectName("label") self.horizontalLayout_5.addWidget(self.label) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_5.addItem(spacerItem) self.draw_button = QPushButton(self.centralWidget) self.draw_button.setObjectName("draw_button") self.horizontalLayout_5.addWidget(self.draw_button) self.erase_button = QPushButton(self.centralWidget) self.erase_button.setObjectName("erase_button") self.horizontalLayout_5.addWidget(self.erase_button) self.dismiss_button = QPushButton(self.centralWidget) self.dismiss_button.setObjectName("dismiss_button") self.horizontalLayout_5.addWidget(self.dismiss_button) self.gridLayout.addLayout(self.horizontalLayout_5, 4, 0, 1, 1) self.verticalLayout_11 = QVBoxLayout() self.verticalLayout_11.setContentsMargins(11, 11, 11, 11) self.verticalLayout_11.setSpacing(6) self.verticalLayout_11.setObjectName("verticalLayout_11") self.tabWidget = QTabWidget(self.centralWidget) self.tabWidget.setObjectName("tabWidget") self.tabWidget.setTabsClosable(True) self.verticalLayout_11.addWidget(self.tabWidget) self.gridLayout.addLayout(self.verticalLayout_11, 0, 0, 1, 1) self.horizontalLayout_7 = QHBoxLayout() self.horizontalLayout_7.setContentsMargins(11, 11, 11, 11) self.horizontalLayout_7.setSpacing(6) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_7.addItem(spacerItem) self.horizontalLayout_7.setObjectName("horizontalLayout_7") self.gridLayout.addLayout(self.horizontalLayout_7, 2, 0, 2, 1) MainWindow.setCentralWidget(self.centralWidget) # self.menuBar = QMenuBar(MainWindow) # self.menuBar.setGeometry(QRect(0, 0, 767, 22)) # self.menuBar.setObjectName("menuBar") # # self.menuFile = QMenu(self.menuBar) # self.menuFile.setObjectName("menuFile") # # MainWindow.setMenuBar(self.menuBar) self.mainToolBar = QToolBar(MainWindow) self.mainToolBar.setMovable(False) self.mainToolBar.setFloatable(False) self.mainToolBar.setObjectName("mainToolBar") MainWindow.addToolBar(Qt.TopToolBarArea, self.mainToolBar) # self.statusBar = QStatusBar(MainWindow) # self.statusBar.setObjectName("statusBar") # MainWindow.setStatusBar(self.statusBar) self.actionOpen = QAction(MainWindow) icon = QIcon(os.path.join(ICON_PATH, "Open Folder-48.png")) self.actionOpen.setIcon(icon) self.actionOpen.setObjectName("actionOpen") self.actionExport = QAction(MainWindow) icon = QIcon(os.path.join(ICON_PATH, "Export-48.png")) self.actionExport.setIcon(icon) self.actionExport.setObjectName("actionExport") self.line_list_selector = QComboBox() self.line_list_selector.setToolTip( "Select line list from internal library") self.actionExit = QAction(MainWindow) self.actionExit.setObjectName("actionExit") self.actionRemove = QAction(MainWindow) self.actionRemove.setObjectName("actionRemove") self.actionChange_Color = QAction(MainWindow) self.actionChange_Color.setObjectName("actionChange_Color") # self.menuFile.addAction(self.actionOpen) # self.menuFile.addSeparator() # self.menuFile.addAction(self.actionExit) # self.menuBar.addAction(self.menuFile.menuAction()) self.mainToolBar.addAction(self.actionOpen) self.mainToolBar.addAction(self.actionExport) self.mainToolBar.addSeparator() self.mainToolBar.addWidget(self.line_list_selector) self.retranslateUi(MainWindow) QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QCoreApplication.translate self.lines_selected_label.setText(_translate("MainWindow", "0")) self.lines_selected_label.setToolTip( "Total number of lines selected in all sets.") self.label.setText(_translate("MainWindow", "lines selected")) self.label.setToolTip("Total number of lines selected in all sets.") self.draw_button.setText(_translate("MainWindow", "Draw")) self.draw_button.setToolTip( "Plot markers for all selected lines in all sets.") self.erase_button.setText(_translate("MainWindow", "Erase")) self.erase_button.setToolTip("Erase all markers") self.dismiss_button.setText(_translate("MainWindow", "Dismiss")) self.dismiss_button.setToolTip("Dismiss this window") # self.menuFile.setTitle(_translate("MainWindow", "File")) self.actionOpen.setText(_translate("MainWindow", "Open")) self.actionExport.setText( _translate("MainWindow", "Export plotted lines")) self.actionExit.setText(_translate("MainWindow", "Exit")) self.actionRemove.setText(_translate("MainWindow", "Remove")) self.actionRemove.setToolTip( _translate("MainWindow", "Removes the selected layer")) self.actionChange_Color.setText( _translate("MainWindow", "Change Color")) self.actionChange_Color.setToolTip( _translate("MainWindow", "Change the line color selected layer"))
class MainWindowLightsOut(QMainWindow): """Main Window.""" def __init__(self): """Init Main Window.""" super().__init__() # Title and set icon self.setWindowTitle(f"LightsOut by ok97465 - {VER}") icon = QIcon() icon.addPixmap(QPixmap(r'ok_64x64.ico'), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) # Setup toolbar self.toolbar = QToolBar() self.new_btn = None self.clear_btn = None self.n_lights_1axis_spinbox = None self.show_solution_chkbox = None self.setup_toolbar() # Setup Button self.btn_grid_table = QTableWidget(self) # Setup Status Bar self.n_clicked = 0 self.clicked_label = QLabel("0", self) self.n_solution_1_label = QLabel("0", self) self.setup_status_bar() # Setup info of lights out self.manage_puzzle = ManageLightsOutPuzzle() self.new_game() # Setup Main Layout self.setCentralWidget(self.btn_grid_table) self.resize_main_window() QTimer.singleShot(100, self.resize_main_window) def resize_main_window(self): """Resize mainwindow to fit table.""" self.toolbar.adjustSize() self.statusBar().adjustSize() w = CELL_SIZE * self.manage_puzzle.n_lights_1axis w += self.btn_grid_table.frameWidth() * 2 w = max([w, self.toolbar.width(), self.statusBar().width()]) h = CELL_SIZE * self.manage_puzzle.n_lights_1axis h += self.btn_grid_table.frameWidth() * 2 h += self.toolbar.frameSize().height() h += self.statusBar().height() self.resize(w, h) def new_game(self): """Create New Game.""" self.manage_puzzle.new_puzzle(self.n_lights_1axis_spinbox.value()) self.setup_btn_grid(self.manage_puzzle.n_lights_1axis) self.show_puzzle() self.n_solution_1_label.setText( f"{self.manage_puzzle.count_1_of_solution()}") self.resize_main_window() def setup_toolbar(self): """Set up toolbar.""" self.addToolBar(self.toolbar) self.toolbar.setMovable(False) self.toolbar.setFloatable(False) self.toolbar.setStyleSheet( "QToolButton {{height:{30}px;width:{30}px;}}") self.new_btn = create_toolbutton(self, qta.icon("mdi.new-box", color=ICON_COLOR), "Start new game.", triggered=self.new_game) self.toolbar.addWidget(self.new_btn) self.toolbar.addSeparator() self.clear_btn = create_toolbutton(self, qta.icon("fa5s.eraser", color=ICON_COLOR), "Click을 초기화 한다.", triggered=self.show_puzzle) self.toolbar.addWidget(self.clear_btn) self.toolbar.addSeparator() self.n_lights_1axis_spinbox = QSpinBox(self) self.n_lights_1axis_spinbox.setValue(4) self.n_lights_1axis_spinbox.setRange(2, 10) self.n_lights_1axis_spinbox.setAlignment(Qt.AlignRight) self.n_lights_1axis_spinbox.setToolTip( "Set Number of light in 1 axis.") self.toolbar.addWidget(self.n_lights_1axis_spinbox) self.toolbar.addSeparator() self.show_solution_chkbox = QCheckBox("Solution", self) self.show_solution_chkbox.setStyleSheet(""" background : "#32414B" """) self.show_solution_chkbox.setToolTip("Show the solution.") self.show_solution_chkbox.stateChanged.connect(self.show_solution) self.toolbar.addWidget(self.show_solution_chkbox) self.toolbar.addSeparator() self.toolbar.adjustSize() def setup_status_bar(self): """Set up status bar.""" status_bar = QStatusBar(self) status_bar.addPermanentWidget(QLabel("Clicked", self)) status_bar.addPermanentWidget(self.clicked_label) status_bar.addPermanentWidget(QLabel("Solution", self)) status_bar.addPermanentWidget(self.n_solution_1_label) self.setStatusBar(status_bar) def setup_btn_grid(self, n_lights_1axis): """Set up grid of buttons.""" table = self.btn_grid_table if n_lights_1axis != table.rowCount(): table.clear() table.setSelectionMode(QAbstractItemView.NoSelection) table.setColumnCount(n_lights_1axis) table.setRowCount(n_lights_1axis) table.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) table.verticalHeader().setSectionResizeMode(QHeaderView.Fixed) table.horizontalHeader().setDefaultSectionSize(CELL_SIZE) table.verticalHeader().setDefaultSectionSize(CELL_SIZE) table.horizontalHeader().hide() table.verticalHeader().hide() for idx_row in range(n_lights_1axis): for idx_col in range(n_lights_1axis): btn = QPushButton(self) btn.setStyleSheet(BTN_STYLE) btn.setCheckable(True) btn.setChecked(True) btn.clicked.connect( self.clicked_btn_of_grid_factory(idx_row, idx_col)) table.setCellWidget(idx_row, idx_col, btn) self.show_solution() def clicked_btn_of_grid_factory(self, idx_row, idx_col): """Generate lambda function of clicked_btn_of_grid.""" return lambda: self.clicked_btn_of_grid(idx_row, idx_col) def clicked_btn_of_grid(self, idx_row, idx_col): """Change state of button around clicked button.""" self.change_state_btn(idx_row - 1, idx_col + 0) self.change_state_btn(idx_row + 1, idx_col + 0) self.change_state_btn(idx_row + 0, idx_col - 1) self.change_state_btn(idx_row + 0, idx_col + 1) self.n_clicked += 1 self.refresh_n_clicked() self.check_solve() def change_state_btn(self, idx_row, idx_col): """Change state of button.""" btn = self.btn_grid_table.cellWidget(idx_row, idx_col) if btn is not None: btn.setChecked(not btn.isChecked()) def show_solution(self): """Show the solution on the button.""" n_lights = self.manage_puzzle.n_lights_1axis solution = self.manage_puzzle.mat_solution for idx_row in range(n_lights): for idx_col in range(n_lights): btn = self.btn_grid_table.cellWidget(idx_row, idx_col) if btn is not None: if self.show_solution_chkbox.isChecked(): if solution[idx_row, idx_col] == 1: btn.setText("◉") else: btn.setText("") else: btn.setText("") def refresh_n_clicked(self): """Refresh number of clicked.""" self.clicked_label.setText(f"{self.n_clicked}") def show_puzzle(self): """Show puzzle.""" n_lights = self.manage_puzzle.n_lights_1axis puzzle = self.manage_puzzle.mat_puzzle for idx_row in range(n_lights): for idx_col in range(n_lights): btn = self.btn_grid_table.cellWidget(idx_row, idx_col) if btn is not None: if puzzle[idx_row, idx_col] == 1: btn.setChecked(True) else: btn.setChecked(False) self.n_clicked = 0 self.refresh_n_clicked() def check_solve(self): """Check if the problem is solved.""" n_lights = self.manage_puzzle.n_lights_1axis for idx_row in range(n_lights): for idx_col in range(n_lights): btn = self.btn_grid_table.cellWidget(idx_row, idx_col) if btn is not None: if btn.isChecked(): return n_solution = self.manage_puzzle.count_1_of_solution() QMessageBox.information(self, "Succeess", ("Congratulation\n" f"clicked : {self.n_clicked}\n" f"solution : {n_solution}"))
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)
class ViewerWindow(QMainWindow, Ui_ViewerWindow): def __init__(self, app_manager, images_list, *args, **kwargs): super(ViewerWindow, self).__init__(*args, **kwargs) self.setupUi(self) # Init self self.app = app_manager self.settings = self.app.settings.get_viewer_settings() self.images_list = images_list self.window_moving = False self.win_old_pos = None self.lbl_old_pos = None self.image = next(self.images_list) self.viewer_policy = {"scale": 'fit_auto'} # Init UI self.actions = WindowActions(self.app) self.toolbar = QToolBar('toolbar') self.init_ui() # Init events self.label.mouseDoubleClickEvent = self.label_double_click_event self.label.mousePressEvent = self.label_mouse_press_event self.label.mouseMoveEvent = self.label_mouse_move_event def init_ui(self): # Setup: window self.setWindowTitle(self.app.app_name) self.setWindowIcon(self.app.ui.window_icon) self.centralwidget.layout().setContentsMargins(0, 0, 0, 0) self.statusbar.setVisible(self.settings[V_SHOW_STATUS_BAR]) # Setup: actions self.actions.previous.triggered.connect(self.previous_action) self.actions.next.triggered.connect(self.next_action) self.actions.fit_to_window.triggered.connect(self.fit_to_window) self.actions.fit_to_width.triggered.connect(self.fit_to_width) self.actions.fit_to_height.triggered.connect(self.fit_to_height) self.actions.show_original_size.triggered.connect(self.original_size) self.actions.zoom_in.triggered.connect(self.zoom_in_action) self.actions.zoom_out.triggered.connect(self.zoom_out_action) self.actions.rotate_right.triggered.connect(self.rotate_right_action) self.actions.rotate_left.triggered.connect(self.rotate_left_action) self.actions.flip_vertically.triggered.connect(self.flip_vertically_action) self.actions.flip_horizontally.triggered.connect(self.flip_horizontally_action) self.actions.reload.triggered.connect(self.reload_action) self.actions.show_statusbar.triggered.connect(self.show_statusbar_action) self.actions.slideshow.triggered.connect(self.slideshow_action) self.actions.settings.triggered.connect(self.settings_action) self.actions.minimize.triggered.connect(self.minimize_action) self.actions.maximize.triggered.connect(self.maximize_action) self.actions.about.triggered.connect(self.about_action) self.actions.exit.triggered.connect(self.exit_action) # Setup: context menu self.label.addAction(self.actions.previous) self.label.addAction(self.actions.next) self.label.addAction(self.actions.fit_to_window) self.label.addAction(self.actions.fit_to_width) self.label.addAction(self.actions.fit_to_height) self.label.addAction(self.actions.show_original_size) self.label.addAction(self.actions.zoom_in) self.label.addAction(self.actions.zoom_out) self.label.addAction(self.actions.rotate_right) self.label.addAction(self.actions.rotate_left) self.label.addAction(self.actions.flip_vertically) self.label.addAction(self.actions.flip_horizontally) self.label.addAction(self.actions.reload) self.label.addAction(self.actions.show_statusbar) self.label.addAction(self.actions.slideshow) self.label.addAction(self.actions.settings) self.label.addAction(self.actions.about) self.label.addAction(self.actions.exit) # Setup: toolbar self.toolbar.setMovable(False) self.toolbar.setContextMenuPolicy(Qt.NoContextMenu) self.setContextMenuPolicy(Qt.NoContextMenu) self.addToolBar(self.toolbar) self.toolbar.addAction(self.actions.previous) self.toolbar.addAction(self.actions.next) self.toolbar.addSeparator() self.toolbar.addAction(self.actions.fit_to_window) self.toolbar.addAction(self.actions.fit_to_width) self.toolbar.addAction(self.actions.fit_to_height) self.toolbar.addAction(self.actions.show_original_size) self.toolbar.addAction(self.actions.zoom_in) self.toolbar.addAction(self.actions.zoom_out) self.toolbar.addSeparator() self.toolbar.addAction(self.actions.rotate_right) self.toolbar.addAction(self.actions.rotate_left) self.toolbar.addAction(self.actions.flip_vertically) self.toolbar.addAction(self.actions.flip_horizontally) self.toolbar.addSeparator() self.toolbar.addAction(self.actions.reload) self.toolbar.addWidget(self.actions.separator) self.toolbar.addAction(self.actions.show_statusbar) self.toolbar.addAction(self.actions.slideshow) # Setup Compact theme if self.app.ui.app_theme == 'Compact': self.setWindowFlags(Qt.FramelessWindowHint) self.toolbar.addAction(self.actions.minimize) self.toolbar.addAction(self.actions.maximize) self.toolbar.addAction(self.actions.exit) # Restore last window state self._restore_geometry() def repaint_image(self): width = self.centralWidget().width() - 2 height = self.centralWidget().height() - 2 pixmap = self.image.pixmap(self.viewer_policy, width, height) self.label.setPixmap(pixmap) # Actions def previous_action(self): self.image = self.images_list.__prev__() self.repaint_image() def next_action(self): self.image = self.images_list.__next__() self.repaint_image() def zoom_in_action(self): self.image.zoom_in() self.repaint_image() self._center_label() def zoom_out_action(self): self.image.zoom_out() self.repaint_image() self._center_label() def rotate_right_action(self): self.image.rotate_right() self.repaint_image() def rotate_left_action(self): self.image.rotate_left() self.repaint_image() def flip_vertically_action(self): self.image.flip_vertically() self.repaint_image() def flip_horizontally_action(self): self.image.flip_horizontally() self.repaint_image() def fit_to_window(self): if self.actions.fit_to_window.isChecked(): self.viewer_policy["scale"] = 'fit_to_window' else: self.viewer_policy["scale"] = 'fit_auto' self.actions.fit_to_width.setChecked(False) self.actions.fit_to_height.setChecked(False) self.actions.show_original_size.setChecked(False) self.repaint_image() def fit_to_width(self): if self.actions.fit_to_width.isChecked(): self.viewer_policy["scale"] = 'fit_to_width' else: self.viewer_policy["scale"] = 'fit_auto' self.actions.fit_to_window.setChecked(False) self.actions.fit_to_height.setChecked(False) self.actions.show_original_size.setChecked(False) self.repaint_image() def fit_to_height(self): if self.actions.fit_to_height.isChecked(): self.viewer_policy["scale"] = 'fit_to_height' else: self.viewer_policy["scale"] = 'fit_auto' self.actions.fit_to_window.setChecked(False) self.actions.fit_to_width.setChecked(False) self.actions.show_original_size.setChecked(False) self.repaint_image() def original_size(self): if self.actions.show_original_size.isChecked(): self.viewer_policy["scale"] = 'original_size' else: self.viewer_policy["scale"] = 'fit_auto' self.actions.fit_to_window.setChecked(False) self.actions.fit_to_width.setChecked(False) self.actions.fit_to_height.setChecked(False) self.repaint_image() def reload_action(self): self.image.reload() self.repaint_image() def show_statusbar_action(self): if self.statusbar.isHidden(): self.statusbar.show() else: self.statusbar.hide() self.repaint_image() def slideshow_action(self): if self.isFullScreen(): self._show_normal() else: self._show_fullscreen() def minimize_action(self): self.showMinimized() def maximize_action(self): if self.isMaximized(): self._show_normal() else: self._show_maximized() def settings_action(self): dialog = SettingsDialog(self.app) dialog.exec_() def about_action(self): dialog = AboutDialog(self.app) dialog.exec_() def exit_action(self): if self.isFullScreen(): self._show_normal() else: self._save_geometry() self.close() self.app.quit() # Events def resizeEvent(self, event): self.repaint_image() def label_double_click_event(self, _): self._reset_viewer() def label_mouse_press_event(self, event): if event.button() == Qt.LeftButton: self.lbl_old_pos = event.pos() def label_mouse_move_event(self, event): if event.buttons() == Qt.LeftButton: offset = self.lbl_old_pos - event.pos() self.lbl_old_pos = event.pos() self.scrollArea.verticalScrollBar().setValue(self.scrollArea.verticalScrollBar().value() + offset.y()) self.scrollArea.horizontalScrollBar().setValue(self.scrollArea.horizontalScrollBar().value() + offset.x()) def mouseDoubleClickEvent(self, event): if event.button() == Qt.LeftButton: self.maximize_action() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.win_old_pos = event.globalPos() self.window_moving = True def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: self.setWindowOpacity(1) self.window_moving = False def mouseMoveEvent(self, event): if self.window_moving and not self.isMaximized() and not self.isFullScreen(): delta = QPoint(event.globalPos() - self.win_old_pos) self.move(self.x() + delta.x(), self.y() + delta.y()) self.win_old_pos = event.globalPos() self.setWindowOpacity(0.5) # Helpers def _show_message(self, msg): self.statusBar().showMessage(msg) def _show_normal(self): self.showNormal() self.actions.maximize.setIcon(self.app.ui.maximize_icon) self.actions.minimize.setVisible(True) self.actions.maximize.setVisible(True) def _show_maximized(self): self.showMaximized() self.actions.maximize.setIcon(self.app.ui.restore_icon) def _show_fullscreen(self): self.showFullScreen() self.actions.minimize.setVisible(False) self.actions.maximize.setVisible(False) def _save_geometry(self): geometry = self.saveGeometry() self.app.settings.set(VIEWER_WINDOW_GEOMETRY, geometry) def _restore_geometry(self): if self.settings[V_SAVE_WINDOW_GEOMETRY]: try: self.restoreGeometry(self.settings[VIEWER_WINDOW_GEOMETRY]) if self.isMaximized(): self._show_maximized() else: self._show_normal() except Exception: self._center_window() else: self._center_window() self.win_old_pos = self.pos() def _center_window(self): self.setFixedSize(self.app.ui.best_window_width, self.app.ui.best_window_height) frame_geometry = self.frameGeometry() frame_geometry.moveCenter(self.app.ui.screen_center) self.move(frame_geometry.topLeft()) def _center_label(self): h_max = self.scrollArea.horizontalScrollBar().maximum() v_max = self.scrollArea.verticalScrollBar().maximum() self.scrollArea.horizontalScrollBar().setValue(h_max / 2) self.scrollArea.verticalScrollBar().setValue(v_max / 2) def _reset_viewer(self): self.viewer_policy["scale"] = 'fit_auto' self.image.reload() self.actions.fit_to_window.setChecked(False) self.actions.fit_to_width.setChecked(False) self.actions.fit_to_height.setChecked(False) self.actions.show_original_size.setChecked(False) self.repaint_image()