def _make_filter_box(self): """ Make the text box to filter the plots by name :return: A QLineEdit object with placeholder text and a clear button """ text_box = QLineEdit(self) text_box.setPlaceholderText("Filter Plots") text_box.setClearButtonEnabled(True) return text_box
def _make_filter_box(self): """ Make the text box to filter the plots by name :return: A QLineEdit object with placeholder text and a clear button """ text_box = QLineEdit(self) text_box.setPlaceholderText("Filter Plots") text_box.setClearButtonEnabled(True) return text_box
class QtPluginDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.refresh_state = RefreshState.DONE self.already_installed = set() installer_type = "mamba" if running_as_constructor_app() else "pip" self.installer = Installer(installer=installer_type) self.setup_ui() self.installer.set_output_widget(self.stdout_text) self.installer.started.connect(self._on_installer_start) self.installer.finished.connect(self._on_installer_done) self.refresh() def _on_installer_start(self): self.cancel_all_btn.setVisible(True) self.working_indicator.show() self.process_error_indicator.hide() self.close_btn.setDisabled(True) def _on_installer_done(self, exit_code): self.working_indicator.hide() if exit_code: self.process_error_indicator.show() self.cancel_all_btn.setVisible(False) self.close_btn.setDisabled(False) self.refresh() def closeEvent(self, event): if self.close_btn.isEnabled(): super().closeEvent(event) event.ignore() def refresh(self): if self.refresh_state != RefreshState.DONE: self.refresh_state = RefreshState.OUTDATED return self.refresh_state = RefreshState.REFRESHING self.installed_list.clear() self.available_list.clear() # fetch installed from npe2 import PluginManager from ...plugins import plugin_manager plugin_manager.discover() # since they might not be loaded yet self.already_installed = set() def _add_to_installed(distname, enabled, npe_version=1): norm_name = normalized_name(distname or '') if distname: try: meta = metadata(distname) except PackageNotFoundError: self.refresh_state = RefreshState.OUTDATED return # a race condition has occurred and the package is uninstalled by another thread if len(meta) == 0: # will not add builtins. return self.already_installed.add(norm_name) else: meta = {} self.installed_list.addItem( PackageMetadata( metadata_version="1.0", name=norm_name, version=meta.get('version', ''), summary=meta.get('summary', ''), home_page=meta.get('url', ''), author=meta.get('author', ''), license=meta.get('license', ''), ), installed=True, enabled=enabled, npe_version=npe_version, ) pm2 = PluginManager.instance() for manifest in pm2.iter_manifests(): distname = normalized_name(manifest.name or '') if distname in self.already_installed or distname == 'napari': continue enabled = not pm2.is_disabled(manifest.name) _add_to_installed(distname, enabled, npe_version=2) for ( plugin_name, _mod_name, distname, ) in plugin_manager.iter_available(): # not showing these in the plugin dialog if plugin_name in ('napari_plugin_engine', ): continue if distname in self.already_installed: continue _add_to_installed(distname, not plugin_manager.is_blocked(plugin_name)) self.installed_label.setText( trans._( "Installed Plugins ({amount})", amount=len(self.already_installed), )) # fetch available plugins settings = get_settings() use_hub = (running_as_bundled_app() or running_as_constructor_app() or settings.plugins.plugin_api.name == "napari_hub") if use_hub: conda_forge = running_as_constructor_app() self.worker = create_worker(iter_hub_plugin_info, conda_forge=conda_forge) else: self.worker = create_worker(iter_napari_plugin_info) self.worker.yielded.connect(self._handle_yield) self.worker.finished.connect(self.working_indicator.hide) self.worker.finished.connect(self._update_count_in_label) self.worker.finished.connect(self._end_refresh) self.worker.start() def setup_ui(self): self.resize(1080, 640) vlay_1 = QVBoxLayout(self) self.h_splitter = QSplitter(self) vlay_1.addWidget(self.h_splitter) self.h_splitter.setOrientation(Qt.Horizontal) self.v_splitter = QSplitter(self.h_splitter) self.v_splitter.setOrientation(Qt.Vertical) self.v_splitter.setMinimumWidth(500) installed = QWidget(self.v_splitter) lay = QVBoxLayout(installed) lay.setContentsMargins(0, 2, 0, 2) self.installed_label = QLabel(trans._("Installed Plugins")) self.packages_filter = QLineEdit() self.packages_filter.setPlaceholderText(trans._("filter...")) self.packages_filter.setMaximumWidth(350) self.packages_filter.setClearButtonEnabled(True) mid_layout = QVBoxLayout() mid_layout.addWidget(self.packages_filter) mid_layout.addWidget(self.installed_label) lay.addLayout(mid_layout) self.installed_list = QPluginList(installed, self.installer) self.packages_filter.textChanged.connect(self.installed_list.filter) lay.addWidget(self.installed_list) uninstalled = QWidget(self.v_splitter) lay = QVBoxLayout(uninstalled) lay.setContentsMargins(0, 2, 0, 2) self.avail_label = QLabel(trans._("Available Plugins")) mid_layout = QHBoxLayout() mid_layout.addWidget(self.avail_label) mid_layout.addStretch() lay.addLayout(mid_layout) self.available_list = QPluginList(uninstalled, self.installer) self.packages_filter.textChanged.connect(self.available_list.filter) lay.addWidget(self.available_list) self.stdout_text = QTextEdit(self.v_splitter) self.stdout_text.setReadOnly(True) self.stdout_text.setObjectName("pip_install_status") self.stdout_text.hide() buttonBox = QHBoxLayout() self.working_indicator = QLabel(trans._("loading ..."), self) sp = self.working_indicator.sizePolicy() sp.setRetainSizeWhenHidden(True) self.working_indicator.setSizePolicy(sp) self.process_error_indicator = QLabel(self) self.process_error_indicator.setObjectName("error_label") self.process_error_indicator.hide() load_gif = str(Path(napari.resources.__file__).parent / "loading.gif") mov = QMovie(load_gif) mov.setScaledSize(QSize(18, 18)) self.working_indicator.setMovie(mov) mov.start() visibility_direct_entry = not running_as_constructor_app() self.direct_entry_edit = QLineEdit(self) self.direct_entry_edit.installEventFilter(self) self.direct_entry_edit.setPlaceholderText( trans._('install by name/url, or drop file...')) self.direct_entry_edit.setVisible(visibility_direct_entry) self.direct_entry_btn = QPushButton(trans._("Install"), self) self.direct_entry_btn.setVisible(visibility_direct_entry) self.direct_entry_btn.clicked.connect(self._install_packages) self.show_status_btn = QPushButton(trans._("Show Status"), self) self.show_status_btn.setFixedWidth(100) self.cancel_all_btn = QPushButton(trans._("cancel all actions"), self) self.cancel_all_btn.setObjectName("remove_button") self.cancel_all_btn.setVisible(False) self.cancel_all_btn.clicked.connect(lambda: self.installer.cancel()) self.close_btn = QPushButton(trans._("Close"), self) self.close_btn.clicked.connect(self.accept) self.close_btn.setObjectName("close_button") buttonBox.addWidget(self.show_status_btn) buttonBox.addWidget(self.working_indicator) buttonBox.addWidget(self.direct_entry_edit) buttonBox.addWidget(self.direct_entry_btn) if not visibility_direct_entry: buttonBox.addStretch() buttonBox.addWidget(self.process_error_indicator) buttonBox.addSpacing(20) buttonBox.addWidget(self.cancel_all_btn) buttonBox.addSpacing(20) buttonBox.addWidget(self.close_btn) buttonBox.setContentsMargins(0, 0, 4, 0) vlay_1.addLayout(buttonBox) self.show_status_btn.setCheckable(True) self.show_status_btn.setChecked(False) self.show_status_btn.toggled.connect(self._toggle_status) self.v_splitter.setStretchFactor(1, 2) self.h_splitter.setStretchFactor(0, 2) self.packages_filter.setFocus() def _update_count_in_label(self): count = self.available_list.count() self.avail_label.setText( trans._("Available Plugins ({count})", count=count)) def _end_refresh(self): refresh_state = self.refresh_state self.refresh_state = RefreshState.DONE if refresh_state == RefreshState.OUTDATED: self.refresh() def eventFilter(self, watched, event): if event.type() == QEvent.DragEnter: # we need to accept this event explicitly to be able # to receive QDropEvents! event.accept() if event.type() == QEvent.Drop: md = event.mimeData() if md.hasUrls(): files = [url.toLocalFile() for url in md.urls()] self.direct_entry_edit.setText(files[0]) return True return super().eventFilter(watched, event) def _toggle_status(self, show): if show: self.show_status_btn.setText(trans._("Hide Status")) self.stdout_text.show() else: self.show_status_btn.setText(trans._("Show Status")) self.stdout_text.hide() def _install_packages(self, packages: Sequence[str] = ()): if not packages: _packages = self.direct_entry_edit.text() if os.path.exists(_packages): packages = [_packages] else: packages = _packages.split() self.direct_entry_edit.clear() if packages: self.installer.install(packages) def _handle_yield(self, data: Tuple[PackageMetadata, bool]): project_info, is_available = data if project_info.name in self.already_installed: self.installed_list.tag_outdated(project_info, is_available) else: self.available_list.addItem(project_info) if not is_available: self.available_list.tag_unavailable(project_info) self.filter() def filter(self, text: str = None) -> None: """Filter by text or set current text as filter.""" if text is None: text = self.packages_filter.text() else: self.packages_filter.setText(text) self.installed_list.filter(text) self.available_list.filter(text)
class LibraryCatalogDialog(QDialog): def __init__(self, parent): super().__init__() self.parent = parent self.setWindowTitle(config.thisTranslation["libraryCatalog"]) self.setMinimumSize(700, 500) self.setupVariables() self.setupUI() def setupVariables(self): self.isUpdating = False self.catalogEntryId = None self.localCatalog = CatalogUtil.loadLocalCatalog() self.remoteCatalog = gitHubRepoCacheData self.localCatalogData = self.getLocalCatalogItems() self.remoteCatalogData = self.getRemoteCatalogItems() self.location = "local" self.textButtonStyle = "QPushButton {background-color: #333972; color: white;} QPushButton:hover {background-color: #333972;} QPushButton:pressed { background-color: #515790;}" def setupUI(self): mainLayout = QVBoxLayout() filterLayout = QHBoxLayout() filterLayout.addWidget(QLabel(config.thisTranslation["menu5_search"])) self.filterEntry = QLineEdit() self.filterEntry.setClearButtonEnabled(True) self.filterEntry.textChanged.connect(self.resetItems) filterLayout.addWidget(self.filterEntry) mainLayout.addLayout(filterLayout) self.searchTypeBox = QGroupBox("") locationLayout = QHBoxLayout() self.localRadioButton = QRadioButton("Local") self.localRadioButton.setChecked(True) self.localRadioButton.toggled.connect( lambda: self.setLocation("local")) locationLayout.addWidget(self.localRadioButton) self.remoteRadioButton = QRadioButton("Remote") self.remoteRadioButton.toggled.connect( lambda: self.setLocation("remote")) locationLayout.addWidget(self.remoteRadioButton) self.searchTypeBox.setLayout(locationLayout) mainLayout.addWidget(self.searchTypeBox) typesLayout = QHBoxLayout() button = QPushButton("All") button.setStyleSheet(self.textButtonStyle) button.clicked.connect(lambda: self.selectAllTypes(True)) typesLayout.addWidget(button) button = QPushButton("None") button.setStyleSheet(self.textButtonStyle) button.clicked.connect(lambda: self.selectAllTypes(False)) typesLayout.addWidget(button) self.bookCheckbox = QCheckBox("BOOK") self.bookCheckbox.setChecked(True) self.bookCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.bookCheckbox) self.pdfCheckbox = QCheckBox("PDF") self.pdfCheckbox.setChecked(True) self.pdfCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.pdfCheckbox) self.docxCheckbox = QCheckBox("DOCX") self.docxCheckbox.setChecked(True) self.docxCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.docxCheckbox) self.devotionalCheckbox = QCheckBox("DEVOTIONAL") self.devotionalCheckbox.setChecked(True) self.devotionalCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.devotionalCheckbox) self.commCheckbox = QCheckBox("COMM") self.commCheckbox.setChecked(True) self.commCheckbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.commCheckbox) self.mp3Checkbox = QCheckBox("MP3") self.mp3Checkbox.setChecked(True) self.mp3Checkbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.mp3Checkbox) self.mp4Checkbox = QCheckBox("MP4") self.mp4Checkbox.setChecked(True) self.mp4Checkbox.stateChanged.connect(self.resetItems) typesLayout.addWidget(self.mp4Checkbox) mainLayout.addLayout(typesLayout) self.dataView = QTableView() self.dataView.clicked.connect(self.itemClicked) self.dataView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.dataView.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.dataView) self.dataView.setModel(self.dataViewModel) self.resetItems() mainLayout.addWidget(self.dataView) buttonLayout = QHBoxLayout() self.openButton = QPushButton(config.thisTranslation["open"]) self.openButton.setEnabled(True) self.openButton.setStyleSheet(self.textButtonStyle) self.openButton.clicked.connect(self.open) buttonLayout.addWidget(self.openButton) self.downloadButton = QPushButton(config.thisTranslation["download"]) self.downloadButton.setEnabled(False) self.downloadButton.clicked.connect(self.download) buttonLayout.addWidget(self.downloadButton) button = QPushButton(config.thisTranslation["close"]) button.setStyleSheet(self.textButtonStyle) button.clicked.connect(self.close) buttonLayout.addWidget(button) mainLayout.addLayout(buttonLayout) self.setLayout(mainLayout) def setLocation(self, location): self.location = location self.resetItems() if location == "local": self.openButton.setEnabled(True) self.openButton.setStyleSheet(self.textButtonStyle) self.downloadButton.setEnabled(False) else: self.openButton.setEnabled(False) self.openButton.setStyleSheet("") self.downloadButton.setEnabled(True) def selectAllTypes(self, value): self.pdfCheckbox.setChecked(value) self.mp3Checkbox.setChecked(value) self.mp4Checkbox.setChecked(value) self.bookCheckbox.setChecked(value) self.docxCheckbox.setChecked(value) self.devotionalCheckbox.setChecked(value) self.commCheckbox.setChecked(value) def getLocalCatalogItems(self): return self.getCatalogItems(self.localCatalog) def getRemoteCatalogItems(self): return self.getCatalogItems(self.remoteCatalog) def getCatalogItems(self, catalog): data = {} pdfCount = 0 mp3Count = 0 mp4Count = 0 bookCount = 0 docxCount = 0 commCount = 0 lexCount = 0 devotionalCount = 0 for filename, type, directory, file, description, repo, installDirectory, sha in catalog: id = "UNKNOWN" if type == "PDF": pdfCount += 1 id = "{0}-{1}".format(type, pdfCount) elif type == "MP3": mp3Count += 1 id = "{0}-{1}".format(type, mp3Count) elif type == "MP4": mp4Count += 1 id = "{0}-{1}".format(type, mp4Count) elif type == "BOOK": bookCount += 1 id = "{0}-{1}".format(type, bookCount) elif type == "DOCX": docxCount += 1 id = "{0}-{1}".format(type, docxCount) elif type == "COMM": commCount += 1 id = "{0}-{1}".format(type, commCount) elif type == "LEX": lexCount += 1 id = "{0}-{1}".format(type, lexCount) elif type == "DEVOTIONAL": devotionalCount += 1 id = "{0}-{1}".format(type, devotionalCount) data[id] = [ id, filename, type, directory, file, description, repo, installDirectory, sha ] return data def resetItems(self): self.isUpdating = True self.dataViewModel.clear() filterEntry = self.filterEntry.text().lower() rowCount = 0 colCount = 0 catalogData = self.localCatalogData if self.location == "remote": catalogData = self.remoteCatalogData for id, value in catalogData.items(): id2, filename, type, directory, file, description, repo, installDirectory, sha = value if (filterEntry == "" or filterEntry in filename.lower() or filterEntry in description.lower()): if (not self.pdfCheckbox.isChecked() and type == "PDF") or \ (not self.mp3Checkbox.isChecked() and type == "MP3") or \ (not self.mp4Checkbox.isChecked() and type == "MP4") or \ (not self.bookCheckbox.isChecked() and type == "BOOK") or \ (not self.docxCheckbox.isChecked() and type == "DOCX") or \ (not self.devotionalCheckbox.isChecked() and type == "DEVOTIONAL") or \ (not self.commCheckbox.isChecked() and type == "COMM"): continue enable = True if self.location == "remote": installDirectory = os.path.join(config.marvelData, installDirectory) if FileUtil.regexFileExists( "{0}.*".format(GithubUtil.getShortname(filename)), installDirectory): enable = False item = QStandardItem(id) item.setEnabled(enable) self.dataViewModel.setItem(rowCount, colCount, item) colCount += 1 item = QStandardItem(file) item.setEnabled(enable) self.dataViewModel.setItem(rowCount, colCount, item) colCount += 1 item = QStandardItem(directory) item.setEnabled(enable) self.dataViewModel.setItem(rowCount, colCount, item) colCount += 1 # item = QStandardItem(description) # self.dataViewModel.setItem(rowCount, colCount, item) # colCount += 1 # add row count rowCount += 1 colCount = 0 self.dataViewModel.setHorizontalHeaderLabels([ "#", config.thisTranslation["file"], config.thisTranslation["directory"], # config.thisTranslation["description"] ]) self.dataView.resizeColumnsToContents() self.isUpdating = False def itemClicked(self, index): selectedRow = index.row() self.catalogEntryId = self.dataViewModel.item(selectedRow, 0).text() if self.location == "remote": item = self.remoteCatalogData[self.catalogEntryId] id, filename, type, directory, file, description, repo, installDirectory, sha = item installDirectory = os.path.join(config.marvelData, installDirectory) if FileUtil.regexFileExists( "{0}.*".format(GithubUtil.getShortname(filename)), installDirectory): self.downloadButton.setEnabled(False) self.downloadButton.setStyleSheet("") else: self.downloadButton.setEnabled(True) self.downloadButton.setStyleSheet(self.textButtonStyle) def displayMessage(self, message="", title="UniqueBible"): QMessageBox.information(self, title, message) def saveRemoteCatalogToCache(self): data = CatalogUtil.loadRemoteCatalog() with open("util/GitHubRepoCache.py", "w", encoding="utf-8") as fileObj: fileObj.write("gitHubRepoCacheData = {0}\n".format( pprint.pformat(data))) def fixDirectory(self, directory, type): if type == "PDF": directory = directory.replace(config.marvelData, "") directory = directory.replace("/pdf", "") if len(directory) > 0 and not directory.endswith("/"): directory += "/" if len(directory) > 0 and directory.startswith("/"): directory = directory[1:] return directory def open(self): item = self.localCatalogData[self.catalogEntryId] id, filename, type, directory, file, description, repo, installDirectory, sha = item directory = self.fixDirectory(directory, type) command = "" if type == "PDF": command = "PDF:::{0}{1}".format(directory, file) elif type == "MP3": command = "VLC:::{0}{1}".format(directory, file) elif type == "MP4": command = "VLC:::{0}{1}".format(directory, file) elif type == "BOOK": if file.endswith(".book"): file = file.replace(".book", "") config.booksFolder = directory command = "BOOK:::{0}".format(file) elif type == "COMM": file = file.replace(".commentary", "") file = file[1:] config.commentariesFolder = directory command = "COMMENTARY:::{0}:::{1} {2}".format( file, BibleBooks.eng[str(config.mainB)][0], config.mainC) elif type == "DOCX": command = "DOCX:::{0}".format(file) elif type == "DEVOTIONAL": file = file.replace(".devotional", "") command = "DEVOTIONAL:::{0}".format(file) self.parent.runTextCommand(command) def download(self): self.downloadButton.setEnabled(False) self.downloadButton.setStyleSheet("") item = self.remoteCatalogData[self.catalogEntryId] id, filename, type, directory, file, description, repo, installDirectory, sha = item github = GithubUtil(repo) installDirectory = os.path.join(config.marvelData, installDirectory) file = os.path.join(installDirectory, filename + ".zip") github.downloadFile(file, sha) with zipfile.ZipFile(file, 'r') as zipped: zipped.extractall(installDirectory) os.remove(file) self.displayMessage(filename + " " + config.thisTranslation["message_installed"]) self.localCatalog = CatalogUtil.reloadLocalCatalog() self.localCatalogData = self.getLocalCatalogItems() self.resetItems()
class MiniBrowser(QWidget): def __init__(self, parent, initialUrl=None): super().__init__() # Set title self.setWindowTitle(config.thisTranslation["miniBrowser"]) # Set up variables self.parent = parent self.isFfmpegInstalled = self.parent.textCommandParser.isFfmpegInstalled( ) # Setup interface self.setupUI(initialUrl) def closeEvent(self, event): self.youTubeView.load(QUrl("about:blank")) def setupUI(self, initialUrl=None): self.youTubeView = YouTubePopover(self) mainLayout = QVBoxLayout() layout = QHBoxLayout() button = QPushButton("<") button.setToolTip(config.thisTranslation["youtube_back"]) button.clicked.connect(self.youTubeView.back) layout.addWidget(button) button = QPushButton(">") button.setToolTip(config.thisTranslation["youtube_forward"]) button.clicked.connect(self.youTubeView.forward) layout.addWidget(button) self.addressBar = QLineEdit() self.addressBar.setClearButtonEnabled(True) self.addressBar.setToolTip(config.thisTranslation["enter_fullURL"]) self.addressBar.returnPressed.connect(self.openURL) layout.addWidget(self.addressBar) button = QPushButton("mp3") button.setToolTip(config.thisTranslation["youtube_mp3"]) button.clicked.connect(lambda: self.convertToFormat("mp3")) layout.addWidget(button) if self.isFfmpegInstalled: button = QPushButton("mp4") button.setToolTip(config.thisTranslation["youtube_mp4"]) button.clicked.connect(lambda: self.convertToFormat("mp4")) layout.addWidget(button) else: button = QPushButton(config.thisTranslation["menu11_video"]) button.setToolTip(config.thisTranslation["downloadVideo"]) button.clicked.connect(self.downloadLastOption) layout.addWidget(button) button = QPushButton("+") button.setToolTip(config.thisTranslation["downloadOptions"]) button.clicked.connect(self.downloadOptions) layout.addWidget(button) mainLayout.addLayout(layout) self.addressBar.setText( config.miniBrowserHome if initialUrl is None else initialUrl) self.openURL() mainLayout.addWidget(self.youTubeView) self.setLayout(mainLayout) def getYouTubeDownloadOptions(self): url = self.addressBar.text() return self.parent.textCommandParser.getYouTubeDownloadOptions(url) def downloadSelectedOption(self, option): self.youTubeDownloadOptions.close() option = re.sub("^([0-9]+?) .*?$", r"\1", option) downloadCommand = "youtube-dl -f {0}".format(option) self.parent.textCommandParser.youtubeDownload(downloadCommand, self.addressBar.text()) def downloadLastOption(self): options = self.getYouTubeDownloadOptions() if options: self.downloadSelectedOption(options[-1]) else: self.displayMessage(config.thisTranslation["noSupportedUrlFormat"]) def downloadOptions(self): options = self.getYouTubeDownloadOptions() if options: self.youTubeDownloadOptions = YouTubeDownloadOptions(self, options) self.youTubeDownloadOptions.show() self.youTubeDownloadOptions.setFixedSize(640, 400) else: self.displayMessage(config.thisTranslation["noSupportedUrlFormat"]) def displayMessage(self, message="", title="UniqueBible"): if hasattr(config, "cli") and config.cli: print(message) else: reply = QMessageBox.information(self, title, message) def openURL(self): address = self.addressBar.text() if address: self.youTubeView.load(QUrl(address)) def convertToFormat(self, fileType): if self.isFfmpegInstalled: address = self.addressBar.text() self.downloadMpFile(fileType, address) else: self.displayMessage(config.thisTranslation["ffmpegNotFound"]) webbrowser.open( "https://github.com/eliranwong/UniqueBible/wiki/Install-ffmpeg" ) def downloadMpFile(self, fileType, address): if not address or not re.search( "youtube", address, flags=re.IGNORECASE) or "/results?search_query=" in address: self.displayMessage(config.thisTranslation["youTubeLinkNotFound"]) else: self.parent.runTextCommand("{0}:::{1}".format(fileType, address))
def generate_context_menu(self, pos: QPoint): """ Generate a context menu for contextMenuEvent Parameters ---------- pos : QPoint The point where the context menu was requested """ model_menu = QMenu() skip_text = "skip me" # Add filterbox to the context menu txt_box = QLineEdit(model_menu) txt_box.setPlaceholderText("Filter") txt_box.setClearButtonEnabled(True) txt_box_action = QWidgetAction(model_menu) txt_box_action.setDefaultWidget(txt_box) model_menu.addAction(txt_box_action) # Add result treeview to the context menu tree_view = QTreeWidget(model_menu) tree_view.header().close() tree_view_action = QWidgetAction(model_menu) tree_view_action.setDefaultWidget(tree_view) model_menu.addAction(tree_view_action) top_level_items = {} for cat in self._scene.registry.categories(): item = QTreeWidgetItem(tree_view) item.setText(0, cat) item.setData(0, Qt.UserRole, skip_text) top_level_items[cat] = item registry = self._scene.registry for model, category in registry.registered_models_category_association( ).items(): self.parent = top_level_items[category] item = QTreeWidgetItem(self.parent) item.setText(0, model) item.setData(0, Qt.UserRole, model) tree_view.expandAll() def click_handler(item): model_name = item.data(0, Qt.UserRole) if model_name == skip_text: return type_ = self._scene.registry.create(model_name) if type_: node = self._scene.create_node(type_) pos_view = self.mapToScene(pos) node.graphics_object.setPos(pos_view) self._scene.node_placed.emit(node) else: logger.debug("Model not found") model_menu.close() tree_view.itemClicked.connect(click_handler) # Setup filtering def filter_handler(text): for name, top_lvl_item in top_level_items.items(): for i in range(top_lvl_item.childCount()): child = top_lvl_item.child(i) model_name = child.data(0, Qt.UserRole) child.setHidden(text not in model_name) txt_box.textChanged.connect(filter_handler) # make sure the text box gets focus so the user doesn't have to click on it txt_box.setFocus() return model_menu
class MiscellaneousLauncher(QWidget): def __init__(self, parent): super().__init__() # set title self.setWindowTitle(config.thisTranslation["cp4"]) # set up variables self.parent = parent # setup interface self.setupUI() def setupUI(self): mainLayout = QGridLayout() leftLayout = QVBoxLayout() rightLayout = QVBoxLayout() self.highLightLauncher = HighlightLauncher(self) leftLayout.addWidget(self.highLightLauncher) rightLayout.addWidget(self.noteEditor()) rightLayout.addWidget(self.textToSpeechUtility()) rightLayout.addWidget(self.utilities()) rightLayout.addStretch() mainLayout.addLayout(leftLayout, 0, 0, 1, 2) mainLayout.addLayout(rightLayout, 0, 2, 1, 1) self.setLayout(mainLayout) def noteEditor(self): box = QGroupBox(config.thisTranslation["note_editor"]) subLayout = QHBoxLayout() button = QPushButton(config.thisTranslation["menu7_create"]) button.setToolTip(config.thisTranslation["menu7_create"]) button.clicked.connect(self.parent.parent.createNewNoteFile) subLayout.addWidget(button) button = QPushButton(config.thisTranslation["menu7_open"]) button.setToolTip(config.thisTranslation["menu7_open"]) button.clicked.connect(self.parent.parent.openTextFileDialog) subLayout.addWidget(button) box.setLayout(subLayout) return box def utilities(self): box = QGroupBox(config.thisTranslation["utilities"]) subLayout = QHBoxLayout() button = QPushButton(config.thisTranslation["presentation"]) button.setToolTip(config.thisTranslation["presentation"]) button.clicked.connect( lambda: self.parent.parent.runPlugin("Presentation")) subLayout.addWidget(button) if config.isYoutubeDownloaderInstalled: button = QPushButton(config.thisTranslation["youtube_utility"]) button.setToolTip(config.thisTranslation["youtube_utility"]) button.clicked.connect(self.parent.parent.openYouTube) subLayout.addWidget(button) box.setLayout(subLayout) return box def textToSpeechUtility(self): box = QGroupBox(config.thisTranslation["tts_utility"]) layout = QVBoxLayout() subLayout = QHBoxLayout() self.ttsEdit = QLineEdit() self.ttsEdit.setClearButtonEnabled(True) self.ttsEdit.setToolTip(config.thisTranslation["enter_text_here"]) self.ttsEdit.returnPressed.connect(self.speakText) subLayout.addWidget(self.ttsEdit) layout.addLayout(subLayout) self.ttsSlider = QSlider(Qt.Horizontal) self.ttsSlider.setToolTip(config.thisTranslation["adjustSpeed"]) self.ttsSlider.setMinimum(10) self.ttsSlider.setMaximum(310) self.ttsSlider.setValue(config.espeakSpeed if config.espeak else ( 160 + config.qttsSpeed * 150)) self.ttsSlider.valueChanged.connect( self.changeEspeakSpeed if config.espeak else self.changeQttsSpeed) layout.addWidget(self.ttsSlider) subLayout = QHBoxLayout() self.languageCombo = QComboBox() subLayout.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 index # It is essential. Otherwise, default tts language is changed by defaultTtsLanguageChanged method. ttsLanguageIndex = self.languageCodes.index(config.ttsDefaultLangauge) self.languageCombo.setCurrentIndex(ttsLanguageIndex) # Change default tts language as users select a new language self.languageCombo.currentIndexChanged.connect( self.defaultTtsLanguageChanged) button = QPushButton(config.thisTranslation["speak"]) button.setToolTip(config.thisTranslation["speak"]) button.clicked.connect(self.speakText) subLayout.addWidget(button) button = QPushButton(config.thisTranslation["stop"]) button.setToolTip(config.thisTranslation["stop"]) button.clicked.connect( self.parent.parent.textCommandParser.stopTtsAudio) subLayout.addWidget(button) layout.addLayout(subLayout) box.setLayout(layout) return box def defaultTtsLanguageChanged(self, index): config.ttsDefaultLangauge = self.languageCodes[index] def speakText(self): text = self.ttsEdit.text() 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.isRefreshing = True self.parent.runTextCommand(command) self.parent.isRefreshing = False else: self.parent.displayMessage( config.thisTranslation["message_noSupport"]) else: self.parent.displayMessage( config.thisTranslation["enterTextFirst"]) def changeQttsSpeed(self, value): config.qttsSpeed = (value * (1.5 / 300) - .5) def changeEspeakSpeed(self, value): config.espeakSpeed = value def refresh(self): ttsLanguageIndex = self.languageCodes.index(config.ttsDefaultLangauge) self.languageCombo.setCurrentIndex(ttsLanguageIndex) self.highLightLauncher.refresh()
class MiniControl(QWidget): def __init__(self, parent, selectedTab=0): super().__init__() self.textButtonStyleEnabled = "QPushButton {background-color: #151B54; color: white;} QPushButton:hover {background-color: #333972;} QPushButton:pressed { background-color: #515790;}" self.textButtonStyleDisabled = "QPushButton {background-color: #323232; color: #323232;} QPushButton:hover {background-color: #323232;} QPushButton:pressed { background-color: #323232;}" self.setWindowTitle(config.thisTranslation["remote_control"]) self.parent = parent self.devotionals = sorted( glob.glob(config.marvelData + "/devotionals/*.devotional")) # specify window size if config.qtMaterial and config.qtMaterialTheme: self.resizeWindow(1 / 2, 1 / 3) else: self.resizeWindow(2 / 5, 1 / 3) self.resizeEvent = (lambda old_method: (lambda event: (self.onResized(event), old_method(event))[-1]))( self.resizeEvent) self.bibleButtons = {} self.commentaryButtons = {} self.bookIntroButtons = {} # setup interface self.bible_layout = None self.setupUI() self.tabs.setCurrentIndex(selectedTab) # window appearance def resizeWindow(self, widthFactor, heightFactor): availableGeometry = QGuiApplication.instance().desktop( ).availableGeometry() self.setMinimumWidth(500) self.resize(availableGeometry.width() * widthFactor, availableGeometry.height() * heightFactor) def onResized(self, event): pass def closeEvent(self, event): config.miniControl = False # manage key capture def event(self, event): if event.type() == QEvent.KeyRelease: if event.modifiers() == Qt.ControlModifier: if event.key() == Qt.Key_B: self.tabs.setCurrentIndex(0) elif event.key() == Qt.Key_T: self.tabs.setCurrentIndex(1) elif event.key() == Qt.Key_C: self.tabs.setCurrentIndex(2) elif event.key() == Qt.Key_L: self.tabs.setCurrentIndex(3) elif event.key() == Qt.Key_D: self.tabs.setCurrentIndex(4) elif event.key() == Qt.Key_O: self.tabs.setCurrentIndex(5) elif event.key() == Qt.Key_Escape: self.close() return QWidget.event(self, event) # setup ui def setupUI(self): textButtonStyle = "QPushButton {background-color: #151B54; color: white;} QPushButton:hover {background-color: #333972;} QPushButton:pressed { background-color: #515790;}" mainLayout = QGridLayout() commandBox = QVBoxLayout() commandBox.setSpacing(3) commandBar = QWidget() commandLayout1 = QBoxLayout(QBoxLayout.LeftToRight) commandLayout1.setSpacing(5) self.searchLineEdit = QLineEdit() self.searchLineEdit.setClearButtonEnabled(True) self.searchLineEdit.setToolTip( config.thisTranslation["enter_command_here"]) self.searchLineEdit.returnPressed.connect(self.searchLineEntered) self.searchLineEdit.setFixedWidth(450) commandLayout1.addWidget(self.searchLineEdit) enterButton = QPushButton(config.thisTranslation["enter"]) enterButton.setFixedWidth(100) enterButton.clicked.connect(self.searchLineEntered) commandLayout1.addWidget(enterButton) # commandLayout1.addStretch() commandBox.addLayout(commandLayout1) if config.showMiniKeyboardInMiniControl: commandLayout2 = QBoxLayout(QBoxLayout.LeftToRight) commandLayout2.setSpacing(5) keys = [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', ':', '-', ',', '.', ' ', '<', 'X' ] for key in keys: button = QPushButton(key) button.setMaximumWidth(30) button.clicked.connect(partial(self.keyEntryAction, key)) commandLayout2.addWidget(button) commandLayout2.addStretch() commandBox.addLayout(commandLayout2) if config.showMiniKeyboardInMiniControl and config.isTtsInstalled: ttsLayout = QBoxLayout(QBoxLayout.LeftToRight) ttsLayout.setSpacing(5) self.languageCombo = QComboBox() ttsLayout.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) # setting tts default language here is confusing; better place in menu #setDefaultButton = QPushButton(config.thisTranslation["setDefault"]) #setDefaultButton.setFixedWidth(130) #setDefaultButton.clicked.connect(self.setTtsDefaultLanguage) #ttsLayout.addWidget(setDefaultButton) speakButton = QPushButton(config.thisTranslation["speak"]) speakButton.setFixedWidth(100) speakButton.clicked.connect(self.speakCommandFieldText) ttsLayout.addWidget(speakButton) stopButton = QPushButton(config.thisTranslation["stop"]) stopButton.setFixedWidth(100) stopButton.clicked.connect( self.parent.textCommandParser.stopTtsAudio) ttsLayout.addWidget(stopButton) ttsLayout.addStretch() commandBox.addLayout(ttsLayout) commandBar.setLayout(commandBox) mainLayout.addWidget(commandBar, 0, 0, Qt.AlignCenter) self.tabs = QTabWidget() self.tabs.currentChanged.connect(self.tabChanged) mainLayout.addWidget(self.tabs, 1, 0, Qt.AlignCenter) parser = BibleVerseParser(config.parserStandarisation) self.bookMap = parser.standardAbbreviation bookNums = list(self.bookMap.keys()) self.bookNumGps = [ bookNums[0:10], bookNums[10:20], bookNums[20:30], bookNums[30:39], bookNums[39:49], bookNums[49:59], bookNums[59:69], bookNums[69:79], bookNums[79:86], bookNums[86:94], bookNums[94:99], bookNums[99:104], bookNums[104:110], bookNums[110:119], bookNums[119:124], bookNums[124:129], bookNums[129:139], bookNums[139:149], bookNums[149:159], bookNums[159:169], bookNums[169:174], bookNums[174:179], bookNums[179:189], bookNums[189:199], ] # Bible books tab self.bible = QWidget() self.populateBooksButtons(config.mainText) self.tabs.addTab(self.bible, config.thisTranslation["bible"]) # Bible translations tab self.biblesBox = QWidget() self.biblesBoxContainer = QVBoxLayout() collectionsLayout = self.newRowLayout() if len(config.bibleCollections) > 0: button = QPushButton("All") button.setStyleSheet(textButtonStyle) button.clicked.connect(partial(self.selectCollection, "All")) collectionsLayout.addWidget(button) count = 0 for collection in sorted(config.bibleCollections.keys()): button = QPushButton(collection) button.setStyleSheet(textButtonStyle) button.clicked.connect( partial(self.selectCollection, collection)) collectionsLayout.addWidget(button) count += 1 if count > 5: count = 0 self.biblesBoxContainer.addLayout(collectionsLayout) collectionsLayout = self.newRowLayout() self.biblesBoxContainer.addLayout(collectionsLayout) self.bibleBoxWidget = QWidget() self.bibleBoxLayout = QVBoxLayout() self.bibleBoxLayout.setContentsMargins(0, 0, 0, 0) self.bibleBoxLayout.setSpacing(1) row_layout = self.newRowLayout() row_layout.setContentsMargins(0, 0, 0, 0) row_layout.setSpacing(1) biblesSqlite = BiblesSqlite() bibles = biblesSqlite.getBibleList() count = 0 for bible in bibles: button = QPushButton(bible) if bible in config.bibleDescription: button.setToolTip("{0}".format(config.bibleDescription[bible])) button.clicked.connect(partial(self.bibleAction, bible)) row_layout.addWidget(button) count += 1 if count > 6: count = 0 self.bibleBoxLayout.addLayout(row_layout) row_layout = self.newRowLayout() self.bibleButtons[bible] = button self.bibleBoxLayout.addLayout(row_layout) self.bibleBoxLayout.addStretch() self.biblesBoxContainer.addLayout(self.bibleBoxLayout) self.biblesBoxContainer.addStretch() self.biblesBox.setLayout(self.biblesBoxContainer) self.tabs.addTab(self.biblesBox, config.thisTranslation["translations"]) # Commentaries tab commentaries_box = QWidget() box_layout = QVBoxLayout() box_layout.setContentsMargins(0, 0, 0, 0) box_layout.setSpacing(1) row_layout = self.newRowLayout() button = QPushButton(config.thisTranslation["activeOnly"]) button.setStyleSheet(textButtonStyle) button.clicked.connect(self.activeCommentaries) row_layout.addWidget(button) box_layout.addLayout(row_layout) row_layout = self.newRowLayout() commentaries = Commentary().getCommentaryList() count = 0 for commentary in commentaries: button = QPushButton(commentary) button.setToolTip(Commentary.fileLookup[commentary]) button.clicked.connect(partial(self.commentaryAction, commentary)) self.commentaryButtons[commentary] = button row_layout.addWidget(button) count += 1 if count > 6: count = 0 box_layout.addLayout(row_layout) row_layout = self.newRowLayout() box_layout.addLayout(row_layout) box_layout.addStretch() commentaries_box.setLayout(box_layout) self.tabs.addTab(commentaries_box, config.thisTranslation["commentaries"]) # Lexicons tab lexicons_box = QWidget() box_layout = QVBoxLayout() box_layout.setContentsMargins(0, 0, 0, 0) box_layout.setSpacing(1) row_layout = self.newRowLayout() lexicons = LexiconData().lexiconList count = 0 for lexicon in lexicons: button = QPushButton(lexicon) if lexicon in config.lexiconDescription: button.setToolTip("{0}".format( config.lexiconDescription[lexicon])) button.clicked.connect(partial(self.lexiconAction, lexicon)) row_layout.addWidget(button) count += 1 if count > 6: count = 0 box_layout.addLayout(row_layout) row_layout = self.newRowLayout() box_layout.addLayout(row_layout) box_layout.addStretch() lexicons_box.setLayout(box_layout) self.tabs.addTab(lexicons_box, config.thisTranslation["lexicons"]) # Dictionaries tab dictionaries_box = QWidget() box_layout = QVBoxLayout() box_layout.setContentsMargins(0, 0, 0, 0) box_layout.setSpacing(1) row_layout = self.newRowLayout() dictionaries = IndexesSqlite().dictionaryList count = 0 for dictionary in dictionaries: button = QPushButton(dictionary[0]) button.setToolTip(dictionary[1]) button.clicked.connect( partial(self.dictionaryAction, dictionary[0])) row_layout.addWidget(button) count += 1 if count > 6: count = 0 box_layout.addLayout(row_layout) row_layout = self.newRowLayout() box_layout.addLayout(row_layout) box_layout.addStretch() dictionaries_box.setLayout(box_layout) self.tabs.addTab(dictionaries_box, config.thisTranslation["dictionaries"]) # Book intros tab bookIntros_box = QWidget() box_layout = QVBoxLayout() box_layout.setContentsMargins(0, 0, 0, 0) box_layout.setSpacing(1) row_layout = self.newRowLayout() button = QPushButton(config.thisTranslation["activeOnly"]) button.setStyleSheet(textButtonStyle) button.clicked.connect(self.activeBookIntros) row_layout.addWidget(button) box_layout.addLayout(row_layout) row_layout = self.newRowLayout() commentaries = Commentary().getCommentaryList() count = 0 for commentary in commentaries: button = QPushButton(commentary) button.setToolTip(Commentary.fileLookup[commentary]) button.clicked.connect(partial(self.bookIntroAction, commentary)) self.bookIntroButtons[commentary] = button row_layout.addWidget(button) count += 1 if count > 6: count = 0 box_layout.addLayout(row_layout) row_layout = self.newRowLayout() box_layout.addLayout(row_layout) box_layout.addStretch() bookIntros_box.setLayout(box_layout) self.tabs.addTab(bookIntros_box, config.thisTranslation["bookIntro"]) # Devotionals tab if len(self.devotionals) > 0: devotionals_box = QWidget() box_layout = QVBoxLayout() box_layout.setContentsMargins(0, 0, 0, 0) box_layout.setSpacing(1) row_layout = self.newRowLayout() count = 0 for file in self.devotionals: name = Path(file).stem button = QPushButton(name) # button.setToolTip(dictionary[1]) button.clicked.connect(partial(self.devotionalAction, name)) row_layout.addWidget(button) count += 1 if count > 2: count = 0 box_layout.addLayout(row_layout) row_layout.addStretch() row_layout = self.newRowLayout() for i in range(count, 3): button = QPushButton("") row_layout.addWidget(button) box_layout.addLayout(row_layout) box_layout.addStretch() devotionals_box.setLayout(box_layout) self.tabs.addTab(devotionals_box, config.thisTranslation["devotionals"]) self.tabs.setCurrentIndex(config.miniControlInitialTab) self.setLayout(mainLayout) def populateBooksButtons(self, bibleName): books = Bible(bibleName).getBookList() if self.bible_layout is not None: while self.bible_layout.count(): child = self.bible_layout.takeAt(0) if child.widget(): child.widget().deleteLater() else: self.bible_layout = QVBoxLayout() self.bible_layout.setContentsMargins(0, 0, 0, 0) self.bible_layout.setSpacing(1) for bookNumGp in self.bookNumGps: gp = QWidget() layout = self.newRowLayout() for bookNum in bookNumGp: if int(bookNum) in books: text = self.bookMap[bookNum] button = QPushButton(text) if config.developer: button.setToolTip("{0} - {1}".format( BibleBooks.eng[bookNum][1], bookNum)) else: button.setToolTip("{0}".format( BibleBooks.eng[bookNum][1])) button.clicked.connect( partial(self.bibleBookAction, bookNum)) layout.addWidget(button) gp.setLayout(layout) self.bible_layout.addWidget(gp) # for bookNumGp in self.bookNumGps[5:]: # gp = QWidget() # layout = self.newRowLayout() # for bookNum in bookNumGp: # text = self.bookMap[bookNum] # button = QPushButton(text) # button.clicked.connect(partial(self.bibleBookAction, bookNum)) # layout.addWidget(button) # gp.setLayout(layout) # bible_layout.addWidget(gp) self.bible_layout.addStretch() self.bible.setLayout(self.bible_layout) def newRowLayout(self): row_layout = QHBoxLayout() row_layout.setContentsMargins(0, 0, 0, 0) row_layout.setSpacing(1) return row_layout def tabChanged(self, index): prefix = "" if index == 0: prefix = "BIBLE:::{0}:::".format(config.mainText) elif index == 1: prefix = "TEXT:::" elif index == 2: prefix = "COMMENTARY:::{0}:::".format(config.commentaryText) elif index == 3: prefix = "LEXICON:::" elif index == 4: prefix = "SEARCHTOOL:::" if not config.clearCommandEntry: self.searchLineEdit.setText(prefix) def searchLineEntered(self): saveOpenBibleWindowContentOnNextTab = config.openBibleWindowContentOnNextTab searchString = self.searchLineEdit.text() if ":::" not in searchString or ":::{0}:::".format( config.mainText) in searchString: config.openBibleWindowContentOnNextTab = False self.parent.textCommandLineEdit.setText(searchString) self.parent.runTextCommand(searchString) self.searchLineEdit.setFocus() self.populateBooksButtons(config.mainText) config.openBibleWindowContentOnNextTab = saveOpenBibleWindowContentOnNextTab #def setTtsDefaultLanguage(self): #config.ttsDefaultLangauge = self.languageCodes[self.languageCombo.currentIndex()] def speakCommandFieldText(self): text = self.searchLineEdit.text() if ":::" in text: text = text.split(":::")[-1] command = "SPEAK:::{0}:::{1}".format( self.languageCodes[self.languageCombo.currentIndex()], text) self.runCommmand(command) def bibleBookAction(self, book): command = "{0} ".format(self.bookMap[book]) self.runCommmand(command) self.searchLineEdit.setFocus() def keyEntryAction(self, key): text = self.searchLineEdit.text() if key == "X": text = "" elif key == "<": text = text[:-1] else: text += key self.searchLineEdit.setText(text) def bibleAction(self, bible): command = "BIBLE:::{0}:::{1}".format( bible, self.parent.verseReference("main")[1]) self.runCommmand(command) command = "_bibleinfo:::{0}".format(bible) self.parent.runTextCommand(command) self.populateBooksButtons(config.mainText) def commentaryAction(self, commentary): command = "COMMENTARY:::{0}:::{1}".format( commentary, self.parent.verseReference("main")[1]) self.runCommmand(command) command = "_commentaryinfo:::{0}".format(commentary) self.parent.runTextCommand(command) def bookIntroAction(self, commentary): command = "COMMENTARY:::{0}:::{1}".format( commentary, BibleBooks().eng[str(config.mainB)][-1]) self.runCommmand(command) command = "_commentaryinfo:::{0}".format(commentary) self.parent.runTextCommand(command) def devotionalAction(self, devotional): command = "DEVOTIONAL:::{0}".format(devotional) self.runCommmand(command) def lexiconAction(self, lexicon): searchString = self.searchLineEdit.text() if ":::" not in searchString: TextCommandParser.last_lexicon_entry = searchString command = "SEARCHLEXICON:::{0}:::{1}".format( lexicon, TextCommandParser.last_lexicon_entry) self.runCommmand(command) def dictionaryAction(self, dictionary): searchString = self.searchLineEdit.text() if ":::" not in searchString: TextCommandParser.last_text_search = searchString command = "SEARCHTOOL:::{0}:::{1}".format( dictionary, TextCommandParser.last_text_search) self.runCommmand(command) def runCommmand(self, command): self.searchLineEdit.setText(command) self.parent.runTextCommand(command) self.parent.textCommandLineEdit.setText(command) self.populateBooksButtons(config.mainText) def selectCollection(self, collection): if not collection == "All": biblesInCollection = config.bibleCollections[collection] for bible in self.bibleButtons.keys(): button = self.bibleButtons[bible] if collection == "All": button.setEnabled(True) button.setStyleSheet("") else: if bible in biblesInCollection: button.setEnabled(True) button.setStyleSheet("") else: button.setEnabled(False) button.setStyleSheet(self.textButtonStyleDisabled) def activeCommentaries(self): activeCommentaries = [ item[0] for item in Commentary().getCommentaryListThatHasBookAndChapter( config.mainB, config.mainC) ] for commentary in self.commentaryButtons.keys(): button = self.commentaryButtons[commentary] if commentary in activeCommentaries: button.setEnabled(True) button.setStyleSheet("") else: button.setEnabled(False) button.setStyleSheet(self.textButtonStyleDisabled) def activeBookIntros(self): activeCommentaries = [ item[0] for item in Commentary().getCommentaryListThatHasBookAndChapter( config.mainB, 0) ] for commentary in self.bookIntroButtons.keys(): button = self.bookIntroButtons[commentary] if commentary in activeCommentaries: button.setEnabled(True) button.setStyleSheet("") else: button.setEnabled(False) button.setStyleSheet(self.textButtonStyleDisabled)
class MasterControl(QWidget): def __init__(self, parent, initialTab=0, b=config.mainB, c=config.mainC, v=config.mainV, text=config.mainText): super().__init__() self.isRefreshing = True self.parent = parent # set title self.setWindowTitle(config.thisTranslation["controlPanel"]) if config.restrictControlPanelWidth and config.screenWidth > config.masterControlWidth: self.setFixedWidth(config.masterControlWidth) # setup item option lists self.setupResourceLists() # setup interface self.text = text self.setupUI(b, c, v, text, initialTab) # setup keyboard shortcuts self.setupKeyboardShortcuts() self.isRefreshing = False def setupKeyboardShortcuts(self): for index, shortcut in enumerate( (sc.openControlPanelTab0, sc.openControlPanelTab1, sc.openControlPanelTab2, sc.openControlPanelTab3, sc.openControlPanelTab4, sc.openControlPanelTab5)): shortcut = QShortcut(QKeySequence(shortcut), self) shortcut.activated.connect( lambda index=index: self.tabs.setCurrentIndex(index)) # manage key capture def event(self, event): if event.type() == QEvent.KeyRelease: if event.key() == Qt.Key_Escape: self.hide() if isinstance(event, QEvent): return QWidget.event(self, event) def closeEvent(self, event): # Control panel is designed for frequent use # Hiding it instead of closing may save time from reloading event.ignore() self.hide() def setupResourceLists(self): self.parent.setupResourceLists() # bible versions self.textList = self.parent.textList self.textFullNameList = self.parent.textFullNameList self.strongBibles = self.parent.strongBibles if self.parent.versionCombo is not None and config.menuLayout in ( "classic", "focus", "aleph"): for index, fullName in enumerate(self.textFullNameList): self.parent.versionCombo.setItemData(index, fullName, Qt.ToolTipRole) # commentaries self.commentaryList = self.parent.commentaryList #self.commentaryFullNameList = [Commentary(module).commentaryInfo() for module in self.commentaryList] self.commentaryFullNameList = self.parent.commentaryFullNameList # reference book # menu10_dialog self.referenceBookList = self.parent.referenceBookList # topic # menu5_topics self.topicListAbb = self.parent.topicListAbb self.topicList = self.parent.topicList # lexicon # context1_originalLexicon self.lexiconList = self.parent.lexiconList # dictionary # context1_dict self.dictionaryListAbb = self.parent.dictionaryListAbb self.dictionaryList = self.parent.dictionaryList # encyclopedia # context1_encyclopedia self.encyclopediaListAbb = self.parent.encyclopediaListAbb self.encyclopediaList = self.parent.encyclopediaList # 3rd-party dictionary # menu5_3rdDict self.thirdPartyDictionaryList = self.parent.thirdPartyDictionaryList # pdf list self.pdfList = self.parent.pdfList # docx list self.docxList = self.parent.docxList def setupUI(self, b, c, v, text, initialTab): mainLayout = QVBoxLayout() mainLayout.addWidget(self.sharedWidget()) mainLayout.addWidget(self.tabWidget(b, c, v, text, initialTab)) self.setLayout(mainLayout) def sharedWidget(self): sharedWidget = QWidget() sharedWidgetLayout = QVBoxLayout() subLayout = QHBoxLayout() subLayout.addWidget(self.commandFieldWidget()) subLayout.addWidget(self.autoCloseCheckBox()) sharedWidgetLayout.addLayout(subLayout) sharedWidget.setLayout(sharedWidgetLayout) return sharedWidget def updateBibleTabText(self, reference): self.tabs.setTabText(0, reference) def tabWidget(self, b, c, v, text, initialTab): self.tabs = QTabWidget() # 0 self.bibleTab = BibleExplorer(self, (b, c, v, text)) self.tabs.addTab(self.bibleTab, config.thisTranslation["cp0"]) self.tabs.setTabToolTip(0, sc.openControlPanelTab0) # 1 libraryTab1 = LibraryLauncher(self) self.tabs.addTab(libraryTab1, config.thisTranslation["cp1"]) self.tabs.setTabToolTip(1, sc.openControlPanelTab1) # 2 libraryTab2 = Library2Launcher(self) self.tabs.addTab(libraryTab2, config.thisTranslation["cp2"]) self.tabs.setTabToolTip(2, sc.openControlPanelTab2) # 3 self.toolTab = SearchLauncher(self) self.tabs.addTab(self.toolTab, config.thisTranslation["cp3"]) self.tabs.setTabToolTip(3, sc.openControlPanelTab3) # 4 self.historyTab = HistoryLauncher(self) self.tabs.addTab(self.historyTab, config.thisTranslation["cp4"]) self.tabs.setTabToolTip(4, sc.openControlPanelTab4) # 5 self.miscellaneousTab = MiscellaneousLauncher(self) self.tabs.addTab(self.miscellaneousTab, config.thisTranslation["cp5"]) self.tabs.setTabToolTip(5, sc.openControlPanelTab5) # 6 if config.isVlcInstalled: mediaTab = MediaLauncher(self) self.tabs.addTab(mediaTab, config.thisTranslation["mediaPlayer"]) self.tabs.setTabToolTip(6, sc.openControlPanelTab6) #7 self.morphologyTab = MorphologyLauncher(self) self.tabs.addTab(self.morphologyTab, config.thisTranslation["cp7"]) self.tabs.setTabToolTip(7, sc.openControlPanelTab7) # set action with changing tabs self.tabs.currentChanged.connect(self.tabChanged) # set initial tab self.tabs.setCurrentIndex(initialTab) return self.tabs def commandFieldWidget(self): self.commandField = QLineEdit() self.commandField.setClearButtonEnabled(True) self.commandField.setToolTip( config.thisTranslation["enter_command_here"]) self.commandField.returnPressed.connect(self.commandEntered) return self.commandField def autoCloseCheckBox(self): checkbox = QCheckBox() checkbox.setText(config.thisTranslation["autoClose"]) checkbox.setToolTip(config.thisTranslation["autoCloseToolTip"]) checkbox.setChecked(config.closeControlPanelAfterRunningCommand) checkbox.stateChanged.connect( self.closeControlPanelAfterRunningCommandChanged) return checkbox # Common layout def buttonsWidget(self, buttonElementTupleTuple, r2l=False, translation=True): buttons = QWidget() buttonsLayouts = QVBoxLayout() buttonsLayouts.setSpacing(3) for buttonElementTuple in buttonElementTupleTuple: buttonsLayouts.addLayout( self.buttonsLayout(buttonElementTuple, r2l, translation)) buttons.setLayout(buttonsLayouts) return buttons def buttonsLayout(self, buttonElementTuple, r2l=False, translation=True): buttonsLayout = QBoxLayout( QBoxLayout.RightToLeft if r2l else QBoxLayout.LeftToRight) buttonsLayout.setSpacing(5) for label, action in buttonElementTuple: buttonLabel = config.thisTranslation[ label] if translation else label button = QPushButton(buttonLabel) button.clicked.connect(action) buttonsLayout.addWidget(button) return buttonsLayout def comboFeatureLayout(self, feature, combo, action): # QGridLayout: https://stackoverflow.com/questions/61451279/how-does-setcolumnstretch-and-setrowstretch-works layout = QGridLayout() layout.setSpacing(5) # combo layout.addWidget(combo, 0, 0, 1, 3) # button button = QPushButton(config.thisTranslation[feature]) button.clicked.connect(action) layout.addWidget(button, 0, 3, 1, 1) return layout # Actions def closeControlPanelAfterRunningCommandChanged(self): config.closeControlPanelAfterRunningCommand = not config.closeControlPanelAfterRunningCommand def updateBCVText(self, b, c, v, text): self.bibleTab.updateBCVText(b, c, v, text) def commandEntered(self): command = self.commandField.text() self.runTextCommand(command, False) def runTextCommand(self, command, printCommand=True, reloadMainWindow=False): if printCommand: self.commandField.setText(command) self.parent.textCommandLineEdit.setText(command) self.parent.runTextCommand(command) if reloadMainWindow: self.parent.reloadCurrentRecord() if config.closeControlPanelAfterRunningCommand and not self.isRefreshing: self.hide() def tabChanged(self, index): self.isRefreshing = True # refresh content if index == 4: self.historyTab.refresh() elif index == 5: self.miscellaneousTab.refresh() # set focus if index == 3: self.toolTab.searchField.setFocus() if config.contextItem: self.toolTab.searchField.setText(config.contextItem) config.contextItem = "" elif self.parent.mainView.currentWidget().selectedText(): self.toolTab.searchField.setText( self.parent.mainView.currentWidget().selectedText()) elif self.parent.studyView.currentWidget().selectedText(): self.toolTab.searchField.setText( self.parent.studyView.currentWidget().selectedText()) else: self.commandField.setFocus() self.isRefreshing = False def displayMessage(self, message="", title="UniqueBible"): reply = QMessageBox.information(self, title, message)
class PropertyBrowser(QDialog): def __init__(self, mmcore: Optional[CMMCorePlus] = None, parent: Optional[QWidget] = None): super().__init__(parent) self._mmc = mmcore or get_core_singleton() self._prop_table = PropertyTable(mmcore) self._show_read_only: bool = True self._filters: Set[DeviceType] = set() self._filter_text = QLineEdit() self._filter_text.setClearButtonEnabled(True) self._filter_text.setPlaceholderText( "Filter by device or property name...") self._filter_text.textChanged.connect(self._update_filter) right = QWidget() right.setLayout(QVBoxLayout()) right.layout().addWidget(self._filter_text) right.layout().addWidget(self._prop_table) left = QWidget() left.setLayout(QVBoxLayout()) left.layout().addWidget(self._make_checkboxes()) self.setLayout(QHBoxLayout()) self.layout().setContentsMargins(6, 12, 12, 12) self.layout().setSpacing(0) self.layout().addWidget(left) self.layout().addWidget(right) self._mmc.events.systemConfigurationLoaded.connect(self._update_filter) self.destroyed.connect(self._disconnect) def _disconnect(self) -> None: self._mmc.events.systemConfigurationLoaded.disconnect( self._update_filter) def _update_filter(self): filt = self._filter_text.text().lower() for r in range(self._prop_table.rowCount()): wdg = cast(PropertyWidget, self._prop_table.cellWidget(r, 1)) if wdg.isReadOnly() and not self._show_read_only: # sourcery skip self._prop_table.hideRow(r) elif wdg.deviceType() in self._filters: self._prop_table.hideRow(r) elif filt and filt not in self._prop_table.item(r, 0).text().lower(): self._prop_table.hideRow(r) else: self._prop_table.showRow(r) def _toggle_filter(self, label: str): self._filters.symmetric_difference_update(DevTypeLabels[label]) self._update_filter() def _make_checkboxes(self): dev_gb = QGroupBox("Device Type") dev_gb.setLayout(QGridLayout()) dev_gb.layout().setSpacing(6) all_btn = QPushButton("All") dev_gb.layout().addWidget(all_btn, 0, 0, 1, 1) none_btn = QPushButton("None") dev_gb.layout().addWidget(none_btn, 0, 1, 1, 1) for i, label in enumerate(DevTypeLabels): cb = QCheckBox(label) cb.setChecked(DevTypeLabels[label] not in self._filters) cb.toggled.connect(partial(self._toggle_filter, label)) dev_gb.layout().addWidget(cb, i + 1, 0, 1, 2) @all_btn.clicked.connect def _check_all(): for cxbx in dev_gb.findChildren(QCheckBox): cxbx.setChecked(True) @none_btn.clicked.connect def _check_none(): for cxbx in dev_gb.findChildren(QCheckBox): cxbx.setChecked(False) for i in dev_gb.findChildren(QWidget): i.setFocusPolicy(Qt.FocusPolicy.NoFocus) ro = QCheckBox("Show read-only") ro.setChecked(self._show_read_only) ro.toggled.connect(self._set_show_read_only) ro.setFocusPolicy(Qt.FocusPolicy.NoFocus) c = QWidget() c.setLayout(QVBoxLayout()) c.layout().addWidget(dev_gb) c.layout().addWidget(ro) c.layout().addStretch() return c def _set_show_read_only(self, state: bool): self._show_read_only = bool(state) self._update_filter()
class QtPluginDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.installer = Installer() self.setup_ui() self.installer.set_output_widget(self.stdout_text) self.installer.process.started.connect(self._on_installer_start) self.installer.process.finished.connect(self._on_installer_done) self.refresh() def _on_installer_start(self): self.show_status_btn.setChecked(True) self.working_indicator.show() self.process_error_indicator.hide() def _on_installer_done(self, exit_code, exit_status): self.working_indicator.hide() if exit_code: self.process_error_indicator.show() else: self.show_status_btn.setChecked(False) self.refresh() self.plugin_sorter.refresh() def refresh(self): self.installed_list.clear() self.available_list.clear() # fetch installed from ...plugins import plugin_manager plugin_manager.discover() # since they might not be loaded yet already_installed = set() for plugin_name, mod_name, distname in plugin_manager.iter_available(): # not showing these in the plugin dialog if plugin_name in ('napari_plugin_engine', ): continue if distname: already_installed.add(distname) meta = standard_metadata(distname) else: meta = {} self.installed_list.addItem( ProjectInfo( normalized_name(distname or ''), meta.get('version', ''), meta.get('url', ''), meta.get('summary', ''), meta.get('author', ''), meta.get('license', ''), ), plugin_name=plugin_name, enabled=plugin_name in plugin_manager.plugins, ) # self.v_splitter.setSizes([70 * self.installed_list.count(), 10, 10]) # fetch available plugins self.worker = create_worker(iter_napari_plugin_info) def _handle_yield(project_info): if project_info.name in already_installed: self.installed_list.tag_outdated(project_info) else: self.available_list.addItem(project_info) self.worker.yielded.connect(_handle_yield) self.worker.finished.connect(self.working_indicator.hide) self.worker.finished.connect(self._update_count_in_label) self.worker.start() def setup_ui(self): self.resize(1080, 640) vlay_1 = QVBoxLayout(self) self.h_splitter = QSplitter(self) vlay_1.addWidget(self.h_splitter) self.h_splitter.setOrientation(Qt.Horizontal) self.v_splitter = QSplitter(self.h_splitter) self.v_splitter.setOrientation(Qt.Vertical) self.v_splitter.setMinimumWidth(500) self.plugin_sorter = QtPluginSorter(parent=self.h_splitter) self.plugin_sorter.layout().setContentsMargins(2, 0, 0, 0) self.plugin_sorter.hide() installed = QWidget(self.v_splitter) lay = QVBoxLayout(installed) lay.setContentsMargins(0, 2, 0, 2) self.installed_label = QLabel(trans._("Installed Plugins")) self.installed_filter = QLineEdit() self.installed_filter.setPlaceholderText("search...") self.installed_filter.setMaximumWidth(350) self.installed_filter.setClearButtonEnabled(True) mid_layout = QHBoxLayout() mid_layout.addWidget(self.installed_label) mid_layout.addWidget(self.installed_filter) mid_layout.addStretch() lay.addLayout(mid_layout) self.installed_list = QPluginList(installed, self.installer) self.installed_filter.textChanged.connect(self.installed_list.filter) lay.addWidget(self.installed_list) uninstalled = QWidget(self.v_splitter) lay = QVBoxLayout(uninstalled) lay.setContentsMargins(0, 2, 0, 2) self.avail_label = QLabel(trans._("Available Plugins")) self.avail_filter = QLineEdit() self.avail_filter.setPlaceholderText("search...") self.avail_filter.setMaximumWidth(350) self.avail_filter.setClearButtonEnabled(True) mid_layout = QHBoxLayout() mid_layout.addWidget(self.avail_label) mid_layout.addWidget(self.avail_filter) mid_layout.addStretch() lay.addLayout(mid_layout) self.available_list = QPluginList(uninstalled, self.installer) self.avail_filter.textChanged.connect(self.available_list.filter) lay.addWidget(self.available_list) self.stdout_text = QTextEdit(self.v_splitter) self.stdout_text.setReadOnly(True) self.stdout_text.setObjectName("pip_install_status") self.stdout_text.hide() buttonBox = QHBoxLayout() self.working_indicator = QLabel(trans._("loading ..."), self) sp = self.working_indicator.sizePolicy() sp.setRetainSizeWhenHidden(True) self.working_indicator.setSizePolicy(sp) self.process_error_indicator = QLabel(self) self.process_error_indicator.setObjectName("error_label") self.process_error_indicator.hide() load_gif = str(Path(napari.resources.__file__).parent / "loading.gif") mov = QMovie(load_gif) mov.setScaledSize(QSize(18, 18)) self.working_indicator.setMovie(mov) mov.start() self.direct_entry_edit = QLineEdit(self) self.direct_entry_edit.installEventFilter(self) self.direct_entry_edit.setPlaceholderText( trans._('install by name/url, or drop file...')) self.direct_entry_btn = QPushButton(trans._("Install"), self) self.direct_entry_btn.clicked.connect(self._install_packages) self.show_status_btn = QPushButton(trans._("Show Status"), self) self.show_status_btn.setFixedWidth(100) self.show_sorter_btn = QPushButton(trans._("<< Show Sorter"), self) self.close_btn = QPushButton(trans._("Close"), self) self.close_btn.clicked.connect(self.accept) buttonBox.addWidget(self.show_status_btn) buttonBox.addWidget(self.working_indicator) buttonBox.addWidget(self.direct_entry_edit) buttonBox.addWidget(self.direct_entry_btn) buttonBox.addWidget(self.process_error_indicator) buttonBox.addSpacing(60) buttonBox.addWidget(self.show_sorter_btn) buttonBox.addWidget(self.close_btn) buttonBox.setContentsMargins(0, 0, 4, 0) vlay_1.addLayout(buttonBox) self.show_status_btn.setCheckable(True) self.show_status_btn.setChecked(False) self.show_status_btn.toggled.connect(self._toggle_status) self.show_sorter_btn.setCheckable(True) self.show_sorter_btn.setChecked(False) self.show_sorter_btn.toggled.connect(self._toggle_sorter) self.v_splitter.setStretchFactor(1, 2) self.h_splitter.setStretchFactor(0, 2) self.avail_filter.setFocus() def _update_count_in_label(self): count = self.available_list.count() self.avail_label.setText( trans._("Available Plugins ({count})", count=count)) def eventFilter(self, watched, event): if event.type() == QEvent.DragEnter: # we need to accept this event explicitly to be able # to receive QDropEvents! event.accept() if event.type() == QEvent.Drop: md = event.mimeData() if md.hasUrls(): files = [url.toLocalFile() for url in md.urls()] self.direct_entry_edit.setText(files[0]) return True return super().eventFilter(watched, event) def _toggle_sorter(self, show): if show: self.show_sorter_btn.setText(trans._(">> Hide Sorter")) self.plugin_sorter.show() else: self.show_sorter_btn.setText(trans._("<< Show Sorter")) self.plugin_sorter.hide() def _toggle_status(self, show): if show: self.show_status_btn.setText(trans._("Hide Status")) self.stdout_text.show() else: self.show_status_btn.setText(trans._("Show Status")) self.stdout_text.hide() def _install_packages(self, packages: Sequence[str] = ()): if not packages: _packages = self.direct_entry_edit.text() if os.path.exists(_packages): packages = [_packages] else: packages = _packages.split() self.direct_entry_edit.clear() if packages: self.installer.install(packages)
class SearchLauncher(QWidget): def __init__(self, parent): super().__init__() self.parent = parent # set title self.setWindowTitle(config.thisTranslation["tools"]) # set up variables self.bibleSearchModeTuple = ("SEARCH", "SEARCHALL", "ANDSEARCH", "ORSEARCH", "ADVANCEDSEARCH", "REGEXSEARCH") config.bibleSearchRange = config.thisTranslation["all"] # setup interface self.setupUI() def setupUI(self): mainLayout0 = QVBoxLayout() mainLayout0.addWidget(self.searchFieldWidget()) mainLayout = QHBoxLayout() mainLayout.addWidget(self.column1Widget()) mainLayout.addWidget(self.column2Widget()) mainLayout0.addLayout(mainLayout) self.setLayout(mainLayout0) self.searchField.clear() def column1Widget(self): widget = QWidget() widgetLayout0 = QVBoxLayout() bibleWidget = QGroupBox(config.thisTranslation["bible"]) bibleLayout = QVBoxLayout() bibleLayout.setSpacing(10) self.bibleCombo = CheckableComboBox(self.parent.textList, [config.mainText], toolTips=self.parent.textFullNameList) bibleLayout.addLayout(self.parent.comboFeatureLayout("html_searchBible2", self.bibleCombo, self.searchBible)) subLayout = QHBoxLayout() subLayout.setSpacing(5) leftGroupLayout = QVBoxLayout() centerGroupLayout = QVBoxLayout() rightGroupLayout = QVBoxLayout() subLayout.addLayout(leftGroupLayout) subLayout.addLayout(centerGroupLayout) subLayout.addLayout(rightGroupLayout) for index, searchMode in enumerate(self.bibleSearchModeTuple): radioButton = QRadioButton(searchMode) radioButton.setToolTip(config.thisTranslation["searchRB{0}".format(index)]) radioButton.toggled.connect(lambda checked, mode=index: self.searchModeChanged(checked, mode)) if index == config.bibleSearchMode: radioButton.setChecked(True) if index % 3 == 0: leftGroupLayout.addWidget(radioButton) elif index % 3 == 1: centerGroupLayout.addWidget(radioButton) else: rightGroupLayout.addWidget(radioButton) leftGroupLayout.addStretch() centerGroupLayout.addStretch() rightGroupLayout.addStretch() bibleLayout.addLayout(subLayout) if len(config.bibleCollections) > 0: navigationLayout6 = self.navigationLayout6() bibleLayout.addWidget(navigationLayout6) bibleWidget.setLayout(bibleLayout) widgetLayout0.addWidget(bibleWidget) # Books range selection booksWidget = QGroupBox(config.thisTranslation["menu10_books"]) booksLayout = QHBoxLayout() booksLayout.setSpacing(1) for range in [config.thisTranslation["all"], "OT", "NT", "Custom"]: radioButton = QRadioButton(range) radioButton.toggled.connect(lambda checked, range=range: self.booksChanged(checked, range)) if config.bibleSearchRange == range: radioButton.setChecked(True) booksLayout.addWidget(radioButton) if range == "Custom": self.customRangeRadioButton = radioButton self.customBooksRangeSearchField = QLineEdit() self.customBooksRangeSearchField.setText(config.customBooksRangeSearch) self.customBooksRangeSearchField.textChanged.connect(self.customBooksRangeChanged) booksLayout.addWidget(self.customBooksRangeSearchField) booksWidget.setLayout(booksLayout) widgetLayout0.addWidget(booksWidget) subLayout = QHBoxLayout() buttonRow = ( ("menu_bookNotes", lambda: self.runSearchCommand("SEARCHBOOKNOTE")), ("menu_chapterNotes", lambda: self.runSearchCommand("SEARCHCHAPTERNOTE")), ("menu_verseNotes", lambda: self.runSearchCommand("SEARCHVERSENOTE")), ) subLayout.addLayout(self.parent.buttonsLayout(buttonRow)) #subLayout.addWidget(self.highlightNoteSearchResultCheckBox()) #subLayout.addStretch() widgetLayout0.addLayout(subLayout) buttonRow1 = ( ("menu5_names", lambda: self.runSearchCommand("SEARCHTOOL:::HBN")), ("menu5_characters", lambda: self.runSearchCommand("SEARCHTOOL:::EXLBP")), ("menu5_locations", lambda: self.runSearchCommand("SEARCHTOOL:::EXLBL")), ("biblePromises", lambda: self.runSearchCommand("SEARCHBOOK:::Bible_Promises")), ) buttonRow2 = ( ("bibleHarmonies", lambda: self.runSearchCommand("SEARCHBOOK:::Harmonies_and_Parallels")), ("menu5_allBook", lambda: self.runSearchCommand("SEARCHBOOK:::ALL")), ("favouriteBooks", lambda: self.runSearchCommand("SEARCHBOOK:::FAV")), ("pdfFiles", lambda: self.runSearchCommand("SEARCHPDF")), ) buttonRow3 = ( ("allBooksPDF", lambda: self.runSearchCommand("SEARCHALLBOOKSPDF")), ("_blank", lambda: self.blankOperation()), ("_blank", lambda: self.blankOperation()), ("_blank", lambda: self.blankOperation()), ) widgetLayout0.addWidget(self.parent.buttonsWidget((buttonRow1, buttonRow2, buttonRow3))) widgetLayout0.addStretch() widget.setLayout(widgetLayout0) return widget def navigationLayout6(self): rows = [] row = [ ("All", lambda: self.selectCollection("All")), ("None", lambda: self.selectCollection("None")), ] count = len(row) for collection in sorted(config.bibleCollections.keys()): row.append((collection, partial(self.selectCollection, collection))) count += 1 if count % 6 == 0: rows.append(row) row = [] if len(row) > 0: rows.append(row) return self.parent.buttonsWidget(rows, False, False) def column2Widget(self): widget = QWidget() widgetLayout = QVBoxLayout() widgetLayout.setSpacing(10) subLayout = QHBoxLayout() self.bookCombo = CheckableComboBox(self.parent.referenceBookList, [config.book]) subLayout.addLayout(self.parent.comboFeatureLayout("menu5_selectBook", self.bookCombo, self.searchBook)) #checkbox = self.highlightBookSearchResultCheckBox() #subLayout.addWidget(checkbox) widgetLayout.addLayout(subLayout) self.topicCombo = QComboBox() initialTopicIndex = self.parent.topicListAbb.index(config.topic) if config.topic in self.parent.topicListAbb else 0 self.lexiconCombo = QComboBox() initialLexiconIndex = self.parent.lexiconList.index(config.lexicon) if config.lexicon in self.parent.lexiconList else 0 self.reverseLexiconCombo = QComboBox() initialReverseLexiconIndex = self.parent.lexiconList.index(config.lexicon) if config.lexicon in self.parent.lexiconList else 0 self.encyclopediaCombo = QComboBox() initialEncyclopediaIndex = self.parent.encyclopediaListAbb.index(config.encyclopedia) if config.encyclopedia in self.parent.encyclopediaListAbb else 0 self.dictionaryCombo = QComboBox() initialDictionaryIndex = self.parent.dictionaryListAbb.index(config.dictionary) if config.dictionary in self.parent.dictionaryListAbb else 0 self.thirdPartyDictionaryCombo = QComboBox() initialThridPartyDictionaryIndex = self.parent.thirdPartyDictionaryList.index(config.thirdDictionary) if config.thirdDictionary in self.parent.thirdPartyDictionaryList else 0 self.lexiconList = self.parent.lexiconList self.lexiconList.append(config.thisTranslation['searchAllLexicons']) self.dictionaryList = self.parent.dictionaryList self.dictionaryList.append(config.thisTranslation['searchAllDictionaries']) self.dictionaryListAbb = self.parent.dictionaryListAbb self.dictionaryListAbb.append(config.thisTranslation['searchAllDictionaries']) self.thirdPartyDictionaryList = self.parent.thirdPartyDictionaryList self.thirdPartyDictionaryList.append(config.thisTranslation['searchAllDictionaries']) features = ( (self.topicCombo, "menu5_topics", lambda: self.runSearchSelection("topic"), self.parent.topicList, initialTopicIndex), (self.lexiconCombo, "menu5_lexicon", lambda: self.runSearchSelection("lexicon"), self.lexiconList, initialLexiconIndex), (self.reverseLexiconCombo, "menu5_reverseLexicon", lambda: self.runSearchSelection("reverselexicon"), self.lexiconList, initialReverseLexiconIndex), (self.encyclopediaCombo, "context1_encyclopedia", lambda: self.runSearchSelection("encyclopedia"), self.parent.encyclopediaList, initialEncyclopediaIndex), (self.dictionaryCombo, "context1_dict", lambda: self.runSearchSelection("dictionary"), self.dictionaryList, initialDictionaryIndex), (self.thirdPartyDictionaryCombo, "menu5_3rdDict", lambda: self.runSearchSelection("thirdPartyDictionary"), self.thirdPartyDictionaryList, initialThridPartyDictionaryIndex), ) for combo, feature, action, items, initialIndex in features: widgetLayout.addLayout(self.singleSelectionLayout(combo, feature, action, items, initialIndex)) widget.setLayout(widgetLayout) return widget def searchFieldWidget(self): self.searchField = QLineEdit() self.searchField.setClearButtonEnabled(True) self.searchField.setToolTip(config.thisTranslation["menu5_searchItems"]) self.searchField.returnPressed.connect(self.searchBible) return self.searchField def singleSelectionLayout(self, combo, feature, action, items, initialIndex=0): combo.addItems(items) combo.setCurrentIndex(initialIndex) return self.parent.comboFeatureLayout(feature, combo, action) def highlightNoteSearchResultCheckBox(self): self.searchNoteCheckbox = QCheckBox() self.searchNoteCheckbox.setMaximumWidth(30) self.searchNoteCheckbox.setToolTip(config.thisTranslation["highlightNoteSearchResult"]) self.searchNoteCheckbox.setChecked(True if config.noteSearchString else False) self.searchNoteCheckbox.stateChanged.connect(self.searchNoteCheckboxChanged) return self.searchNoteCheckbox def highlightBookSearchResultCheckBox(self): self.searchBookCheckbox = QCheckBox() self.searchBookCheckbox.setMaximumWidth(30) self.searchBookCheckbox.setToolTip(config.thisTranslation["highlightBookSearchResult"]) self.searchBookCheckbox.setChecked(True if config.bookSearchString else False) self.searchBookCheckbox.stateChanged.connect(self.searchBookCheckboxChanged) return self.searchBookCheckbox # Button actions def searchModeChanged(self, checked, mode): if checked: config.bibleSearchMode = mode def booksChanged(self, checked, range): if checked: config.bibleSearchRange = range def customBooksRangeChanged(self, text): if len(text) > 0: self.customRangeRadioButton.setChecked(True) config.customBooksRangeSearch = text def getSearchItem(self): searchItem = self.searchField.text() noItemMessage = config.thisTranslation["menu5_searchItems"] if not searchItem: self.searchField.setText(noItemMessage) return "" elif searchItem == noItemMessage: return "" else: return searchItem def searchHighlight(self, index): command = "SEARCHHIGHLIGHT:::hl{0}".format(index + 1) self.parent.runTextCommand(command) def searchBible(self): searchItem = self.getSearchItem() if searchItem: command = "{0}:::{1}:::{2}".format(self.bibleSearchModeTuple[config.bibleSearchMode], "_".join(self.bibleCombo.checkItems), self.searchField.text()) if config.bibleSearchRange == "Custom": command += ":::{0}".format(config.customBooksRangeSearch) elif config.bibleSearchRange != config.thisTranslation["all"]: command += ":::{0}".format(config.bibleSearchRange) self.parent.runTextCommand(command) def searchBook(self): searchItem = self.getSearchItem() if searchItem: command = "{0}:::{1}:::{2}".format("SEARCHBOOK", ",".join(self.bookCombo.checkItems), self.searchField.text()) self.parent.runTextCommand(command) def searchBookCheckboxChanged(self, state): if not state: config.bookSearchString = "" self.parent.parent.reloadCurrentRecord() if config.closeControlPanelAfterRunningCommand and not self.isRefreshing: self.parent.hide() def searchNoteCheckboxChanged(self, state): if not state: config.noteSearchString = "" self.parent.parent.reloadCurrentRecord() if config.closeControlPanelAfterRunningCommand and not self.parent.isRefreshing: self.parent.hide() def runSearchCommand(self, prefix): searchItem = self.getSearchItem() if searchItem: command = "{0}:::{1}".format(prefix, self.searchField.text()) self.parent.runTextCommand(command) def runSearchSelection(self, resource): searchItem = self.getSearchItem() if searchItem: comboDict = { "topic": (self.topicCombo, self.parent.topicListAbb), "lexicon": (self.lexiconCombo, self.lexiconList), "reverselexicon": (self.reverseLexiconCombo, self.lexiconList), "encyclopedia": (self.encyclopediaCombo, self.parent.encyclopediaListAbb), "dictionary": (self.dictionaryCombo, self.dictionaryListAbb), "thirdPartyDictionary": (self.thirdPartyDictionaryCombo, self.thirdPartyDictionaryList), } combo, resourceList = comboDict[resource] selectedItem = resourceList[combo.currentIndex()] if resource == "lexicon": keyword = "LEXICON" elif resource == "reverselexicon": keyword = "REVERSELEXICON" elif resource == "thirdPartyDictionary": keyword = "SEARCHTHIRDDICTIONARY" else: keyword = "SEARCHTOOL" prefix = "{0}:::{1}".format(keyword, selectedItem) self.runSearchCommand(prefix) def blankOperation(self): return def selectCollection(self, collection): if collection == "All": self.bibleCombo.checkAll() elif collection == "None": self.bibleCombo.clearAll() else: self.bibleCombo.checkFromList(config.bibleCollections[collection])
class MorphologyLauncher(QWidget): def __init__(self, parent): super().__init__() self.setWindowTitle(config.thisTranslation["cp7"]) self.parent = parent self.bookList = BibleBooks.getStandardBookAbbreviations() self.setupUI() def setupUI(self): mainLayout = QVBoxLayout() subLayout = QHBoxLayout() subLayout.addWidget(self.searchFieldWidget()) button = QPushButton("Search") button.clicked.connect(self.searchMorphology) subLayout.addWidget(button) mainLayout.addLayout(subLayout) subLayout = QHBoxLayout() subLayout.addWidget(QLabel("Start:")) self.startBookCombo = QComboBox() subLayout.addWidget(self.startBookCombo) self.startBookCombo.addItems(self.bookList) self.startBookCombo.setCurrentIndex(0) subLayout.addWidget(QLabel("End:")) self.endBookCombo = QComboBox() subLayout.addWidget(self.endBookCombo) self.endBookCombo.addItems(self.bookList) self.endBookCombo.setCurrentIndex(65) subLayout.addWidget(QLabel(" ")) button = QPushButton("Entire Bible") button.clicked.connect(lambda: self.selectBookCombos(0, 65)) subLayout.addWidget(button) button = QPushButton("Current Book") button.clicked.connect(lambda: self.selectBookCombos(config.mainB-1, config.mainB-1)) subLayout.addWidget(button) button = QPushButton("OT") button.clicked.connect(lambda: self.selectBookCombos(0, 38)) subLayout.addWidget(button) button = QPushButton("NT") button.clicked.connect(lambda: self.selectBookCombos(39, 65)) subLayout.addWidget(button) button = QPushButton("Gospels") button.clicked.connect(lambda: self.selectBookCombos(39, 42)) subLayout.addWidget(button) subLayout.addStretch() mainLayout.addLayout(subLayout) subLayout = QHBoxLayout() self.searchTypeBox = QGroupBox("Type") layout = QVBoxLayout() self.strongsRadioButton = QRadioButton("Lexical") self.strongsRadioButton.setToolTip("G2424") self.strongsRadioButton.toggled.connect(lambda checked, mode="Lexical": self.searchTypeChanged(checked, mode)) self.strongsRadioButton.setChecked(True) layout.addWidget(self.strongsRadioButton) radioButton = QRadioButton("Word") radioButton.setToolTip("Ἰησοῦς") radioButton.toggled.connect(lambda checked, mode="Word": self.searchTypeChanged(checked, mode)) layout.addWidget(radioButton) radioButton = QRadioButton("Gloss") radioButton.setToolTip("Jesus") radioButton.toggled.connect(lambda checked, mode="Gloss": self.searchTypeChanged(checked, mode)) layout.addWidget(radioButton) # radioButton = QRadioButton("Transliteration") # radioButton.setToolTip("Iēsous") # radioButton.toggled.connect(lambda checked, mode="Transliteration": self.searchTypeChanged(checked, mode)) # layout.addWidget(radioButton) self.searchTypeBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.searchTypeBox) languageList = ["Greek", "Hebrew"] self.languageBox = QGroupBox("Language") layout = QVBoxLayout() for count, lang in enumerate(languageList): button = QRadioButton(lang) button.toggled.connect(lambda checked, mode=lang: self.languageChanged(checked, mode)) layout.addWidget(button) if count == 0: self.greekButton = button self.languageBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.languageBox) posList = ["Adjective", "Adverb", "Article", "Conjunction", "Interjection", "Noun", "Participle", "Preposition", "Pronoun", "Verb", ""] self.posCheckBoxes = [] self.partOfSpeechBox = QGroupBox("Part of speech") layout = QVBoxLayout() for count, pos in enumerate(posList): button = QRadioButton(pos) button.toggled.connect(lambda checked, mode=pos: self.partOfSpeechChanged(checked, mode)) layout.addWidget(button) if count == 0: self.nounButton = button self.partOfSpeechBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.partOfSpeechBox) greekCaseList = ["Accusative", "Dative", "Genitive", "Nominative", "Vocative"] self.greekCaseCheckBoxes = [] self.greekCaseBox = QGroupBox("Case") self.greekCaseBox.hide() layout = QVBoxLayout() for case in greekCaseList: checkbox = QCheckBox(case) layout.addWidget(checkbox) self.greekCaseCheckBoxes.append(checkbox) checkbox.stateChanged.connect(lambda checked, case=case: self.checkBoxChanged(checked, case, self.greekCaseCheckBoxes)) self.greekCaseBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.greekCaseBox) greekTenseList = ["Aorist", "Future", "Imperfect", "Perfect", "Pluperfect", "Present"] self.greekTenseCheckBoxes = [] self.greekTenseBox = QGroupBox("Tense") self.greekTenseBox.hide() layout = QVBoxLayout() for tense in greekTenseList: checkbox = QCheckBox(tense) layout.addWidget(checkbox) self.greekTenseCheckBoxes.append(checkbox) checkbox.stateChanged.connect(lambda checked, tense=tense: self.checkBoxChanged(checked, tense, self.greekTenseCheckBoxes)) self.greekTenseBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.greekTenseBox) voiceList = ["Active", "Middle", "Passive"] self.voiceCheckBoxes = [] self.voiceBox = QGroupBox("Voice") self.voiceBox.hide() layout = QVBoxLayout() for voice in voiceList: checkbox = QCheckBox(voice) layout.addWidget(checkbox) self.voiceCheckBoxes.append(checkbox) checkbox.stateChanged.connect(lambda checked, voice=voice: self.checkBoxChanged(checked, voice, self.voiceCheckBoxes)) self.voiceBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.voiceBox) greekMoodList = ["Imperative", "Indicative", "Optative", "Subjunctive"] self.greekMoodCheckBoxes = [] self.greekMoodBox = QGroupBox("Mood") self.greekMoodBox.hide() layout = QVBoxLayout() for mood in greekMoodList: checkbox = QCheckBox(mood) layout.addWidget(checkbox) self.greekMoodCheckBoxes.append(checkbox) checkbox.stateChanged.connect(lambda checked, mood=mood: self.checkBoxChanged(checked, mood, self.greekMoodCheckBoxes)) self.greekMoodBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.greekMoodBox) hebrewStateList = ["Construct", "Absolute"] self.hebrewStateCheckBoxes = [] self.hebrewStateBox = QGroupBox("State") self.hebrewStateBox.hide() layout = QVBoxLayout() for state in hebrewStateList: checkbox = QCheckBox(state) layout.addWidget(checkbox) self.hebrewStateCheckBoxes.append(checkbox) checkbox.stateChanged.connect(lambda checked, state=state: self.checkBoxChanged(checked, state, self.hebrewStateCheckBoxes)) self.hebrewStateBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.hebrewStateBox) hebrewStateList = ["Construct", "Absolute"] self.hebrewStateCheckBoxes = [] self.hebrewStateBox = QGroupBox("State") self.hebrewStateBox.hide() layout = QVBoxLayout() for stem in hebrewStateList: checkbox = QCheckBox(stem) layout.addWidget(checkbox) self.hebrewStateCheckBoxes.append(checkbox) checkbox.stateChanged.connect(lambda checked, stem=stem: self.checkBoxChanged(checked, stem, self.hebrewStateCheckBoxes)) self.hebrewStateBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.hebrewStateBox) hebrewPatternList = ["Hif‘il", "Hitpa“el", "Nif‘al", "Pi“el", "Pu“al", "Qal", "Wayyiqtol"] self.hebrewPatternCheckBoxes = [] self.hebrewPatternBox = QGroupBox("State") self.hebrewPatternBox.hide() layout = QVBoxLayout() for stem in hebrewPatternList: checkbox = QCheckBox(stem) layout.addWidget(checkbox) self.hebrewPatternCheckBoxes.append(checkbox) # checkbox.stateChanged.connect(lambda checked, stem=stem: self.checkBoxChanged(checked, stem, self.hebrewPatternCheckBoxes)) self.hebrewPatternBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.hebrewPatternBox) personList = ["First", "Second", "Third"] self.personCheckBoxes = [] self.personBox = QGroupBox("Person") self.personBox.hide() layout = QVBoxLayout() for person in personList: checkbox = QCheckBox(person) layout.addWidget(checkbox) self.personCheckBoxes.append(checkbox) checkbox.stateChanged.connect(lambda checked, person=person: self.checkBoxChanged(checked, person, self.personCheckBoxes)) self.personBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.personBox) numberList = ["Singular", "Plural", "Dual"] self.numberCheckBoxes = [] self.numberBox = QGroupBox("Number") self.numberBox.hide() layout = QVBoxLayout() for number in numberList: checkbox = QCheckBox(number) layout.addWidget(checkbox) self.numberCheckBoxes.append(checkbox) checkbox.stateChanged.connect(lambda checked, number=number: self.checkBoxChanged(checked, number, self.numberCheckBoxes)) self.numberBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.numberBox) genderList = ["Masculine", "Feminine", "Neuter"] self.genderCheckBoxes = [] self.genderBox = QGroupBox("Gender") self.genderBox.hide() layout = QVBoxLayout() for gender in genderList: checkbox = QCheckBox(gender) layout.addWidget(checkbox) self.genderCheckBoxes.append(checkbox) checkbox.stateChanged.connect(lambda checked, gender=gender: self.checkBoxChanged(checked, gender, self.genderCheckBoxes)) self.genderBox.setLayout(layout) layout.addStretch() subLayout.addWidget(self.genderBox) # TODO: hebrewList = ["Absolute", "", "Infinitive", "Pronominal", ""] self.spacerBox1 = QGroupBox(" ") layout = QVBoxLayout() layout.addStretch() self.spacerBox1.setLayout(layout) subLayout.addWidget(self.spacerBox1) self.spacerBox2 = QGroupBox(" ") layout = QVBoxLayout() layout.addStretch() self.spacerBox2.setLayout(layout) subLayout.addWidget(self.spacerBox2) self.spacerBox3 = QGroupBox(" ") layout = QVBoxLayout() layout.addStretch() self.spacerBox3.setLayout(layout) subLayout.addWidget(self.spacerBox3) self.spacerBox4 = QGroupBox(" ") layout = QVBoxLayout() layout.addStretch() self.spacerBox4.setLayout(layout) subLayout.addWidget(self.spacerBox4) self.spacerBox5 = QGroupBox(" ") layout = QVBoxLayout() layout.addStretch() self.spacerBox5.setLayout(layout) subLayout.addWidget(self.spacerBox5) mainLayout.addLayout(subLayout) mainLayout.addStretch() self.setLayout(mainLayout) self.greekButton.setChecked(True) self.nounButton.setChecked(True) def selectBookCombos(self, start, end): self.startBookCombo.setCurrentIndex(start) self.endBookCombo.setCurrentIndex(end) def searchTypeChanged(self, checked, type): self.type = type def searchFieldWidget(self): self.searchField = QLineEdit() self.searchField.setClearButtonEnabled(True) self.searchField.setToolTip(config.thisTranslation["menu5_searchItems"]) self.searchField.returnPressed.connect(self.searchMorphology) return self.searchField def checkBoxChanged(self, state, value, checkboxes): if int(state) > 0: for checkbox in checkboxes: if checkbox.isChecked() and value != checkbox.text(): checkbox.setChecked(False) def languageChanged(self, checked, language): if checked: self.language = language self.updateAllCheckboxes() def partOfSpeechChanged(self, checked, pos): if checked: self.pos = pos self.updateAllCheckboxes() def updateAllCheckboxes(self): self.genderBox.hide() self.numberBox.hide() self.greekCaseBox.hide() self.personBox.hide() self.greekTenseBox.hide() self.greekMoodBox.hide() self.voiceBox.hide() self.hebrewStateBox.hide() self.hebrewPatternBox.hide() self.spacerBox1.hide() self.spacerBox2.hide() self.spacerBox3.hide() self.spacerBox4.hide() self.spacerBox5.hide() if self.language == "Greek": if self.pos in ("Noun", "Adjective"): self.greekCaseBox.show() self.numberBox.show() self.genderBox.show() self.spacerBox4.show() self.spacerBox5.show() elif self.pos in ("Pronoun", "Article"): self.greekCaseBox.show() self.numberBox.show() self.genderBox.show() self.personBox.show() self.spacerBox5.show() elif self.pos == "Verb": self.greekTenseBox.show() self.voiceBox.show() self.greekMoodBox.show() self.personBox.show() self.numberBox.show() elif self.pos == "Adverb": self.greekCaseBox.show() self.numberBox.show() self.genderBox.show() self.spacerBox4.show() self.spacerBox5.show() else: self.spacerBox1.show() self.spacerBox2.show() self.spacerBox3.show() self.spacerBox4.show() self.spacerBox5.show() else: if self.pos in ("Noun", "Adjective", "Preposition", "Pronoun"): self.numberBox.show() self.genderBox.show() self.hebrewStateBox.show() self.spacerBox4.show() self.spacerBox5.show() elif self.pos in ("Verb", "Adverb"): self.personBox.show() self.genderBox.show() self.numberBox.show() self.hebrewStateBox.show() self.hebrewPatternBox.show() else: self.spacerBox1.show() self.spacerBox2.show() self.spacerBox3.show() self.spacerBox4.show() self.spacerBox5.show() def searchMorphology(self): searchTerm = self.searchField.text() if len(searchTerm) > 1: morphologyList = [] morphologyList.append(self.pos) if self.language == "Greek": if self.pos in ("Noun", "Adjective"): checkBoxList = [self.greekCaseCheckBoxes, self.numberCheckBoxes, self.genderCheckBoxes] elif self.pos == ("Pronoun", "Article"): checkBoxList = [self.greekCaseCheckBoxes, self.numberCheckBoxes, self.genderCheckBoxes, self.personCheckBoxes] elif self.pos == "Verb": checkBoxList = [self.greekTenseCheckBoxes, self.voiceCheckBoxes, self.greekMoodCheckBoxes, self.personCheckBoxes, self.numberCheckBoxes] elif self.pos == "Adverb": checkBoxList = [self.greekCaseCheckBoxes, self.numberCheckBoxes, self.genderCheckBoxes] else: checkBoxList = [] else: if self.pos in ("Noun", "Adjective", "Preposition", "Pronoun"): checkBoxList = [self.numberCheckBoxes, self.genderCheckBoxes, self.hebrewStateCheckBoxes] elif self.pos in ("Verb", "Adverb"): checkBoxList = [self.personCheckBoxes, self.genderCheckBoxes, self.numberCheckBoxes, self.hebrewStateCheckBoxes, self.hebrewPatternCheckBoxes] else: checkBoxList = [] for checkBoxes in checkBoxList: for checkbox in checkBoxes: if checkbox.isChecked(): morphologyList.append(checkbox.text()) morphology = ",".join(morphologyList) startBook = self.startBookCombo.currentIndex() + 1 endBook = self.endBookCombo.currentIndex() + 1 if endBook < startBook: endBook = startBook if self.type == "Lexical": command = "SEARCHMORPHOLOGYBYLEX:::{0}:::{1}:::{2}-{3}".format(searchTerm, morphology, startBook, endBook) elif self.type == "Word": command = "SEARCHMORPHOLOGYBYWORD:::{0}:::{1}:::{2}-{3}".format(searchTerm, morphology, startBook, endBook) elif self.type == "Gloss": command = "SEARCHMORPHOLOGYBYGLOSS:::{0}:::{1}:::{2}-{3}".format(searchTerm, morphology, startBook, endBook) self.parent.runTextCommand(command)
class MiniControl(QWidget): def __init__(self, parent): super().__init__() self.setWindowTitle(config.thisTranslation["remote_control"]) self.parent = parent # specify window size if config.qtMaterial and config.qtMaterialTheme: self.resizeWindow(1 / 2, 1 / 3) else: self.resizeWindow(2 / 5, 1 / 3) self.resizeEvent = (lambda old_method: (lambda event: (self.onResized(event), old_method(event))[-1]))( self.resizeEvent) # setup interface self.setupUI() # window appearance def resizeWindow(self, widthFactor, heightFactor): availableGeometry = QGuiApplication.instance().desktop( ).availableGeometry() self.setMinimumWidth(500) self.resize(availableGeometry.width() * widthFactor, availableGeometry.height() * heightFactor) def onResized(self, event): pass def closeEvent(self, event): config.miniControl = False # manage key capture def event(self, event): if event.type() == QEvent.KeyRelease: if event.modifiers() == Qt.ControlModifier: if event.key() == Qt.Key_B: self.tabs.setCurrentIndex(0) elif event.key() == Qt.Key_T: self.tabs.setCurrentIndex(1) elif event.key() == Qt.Key_C: self.tabs.setCurrentIndex(2) elif event.key() == Qt.Key_L: self.tabs.setCurrentIndex(3) elif event.key() == Qt.Key_D: self.tabs.setCurrentIndex(4) elif event.key() == Qt.Key_Escape: self.close() return QWidget.event(self, event) # setup ui def setupUI(self): mainLayout = QGridLayout() commandBox = QVBoxLayout() commandBox.setSpacing(3) commandBar = QWidget() commandLayout1 = QBoxLayout(QBoxLayout.LeftToRight) commandLayout1.setSpacing(5) self.searchLineEdit = QLineEdit() self.searchLineEdit.setClearButtonEnabled(True) self.searchLineEdit.setToolTip( config.thisTranslation["enter_command_here"]) self.searchLineEdit.returnPressed.connect(self.searchLineEntered) self.searchLineEdit.setFixedWidth(300) commandLayout1.addWidget(self.searchLineEdit) enterButton = QPushButton(config.thisTranslation["enter"]) enterButton.setFixedWidth(100) enterButton.clicked.connect(self.searchLineEntered) commandLayout1.addWidget(enterButton) commandLayout1.addStretch() commandBox.addLayout(commandLayout1) commandLayout2 = QBoxLayout(QBoxLayout.LeftToRight) commandLayout2.setSpacing(5) keys = [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', ':', '-', ',', '.', ' ', '<', 'X' ] for key in keys: button = QPushButton(key) button.setMaximumWidth(30) button.clicked.connect(partial(self.keyEntryAction, key)) commandLayout2.addWidget(button) commandLayout2.addStretch() commandBox.addLayout(commandLayout2) if config.isTtsInstalled: ttsLayout = QBoxLayout(QBoxLayout.LeftToRight) ttsLayout.setSpacing(5) self.languageCombo = QComboBox() ttsLayout.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) # setting tts default language here is confusing; better place in menu #setDefaultButton = QPushButton(config.thisTranslation["setDefault"]) #setDefaultButton.setFixedWidth(130) #setDefaultButton.clicked.connect(self.setTtsDefaultLanguage) #ttsLayout.addWidget(setDefaultButton) speakButton = QPushButton(config.thisTranslation["speak"]) speakButton.setFixedWidth(100) speakButton.clicked.connect(self.speakCommandFieldText) ttsLayout.addWidget(speakButton) stopButton = QPushButton(config.thisTranslation["stop"]) stopButton.setFixedWidth(100) stopButton.clicked.connect( self.parent.textCommandParser.stopTtsAudio) ttsLayout.addWidget(stopButton) ttsLayout.addStretch() commandBox.addLayout(ttsLayout) commandBar.setLayout(commandBox) mainLayout.addWidget(commandBar, 0, 0, Qt.AlignCenter) self.tabs = QTabWidget() self.tabs.currentChanged.connect(self.tabChanged) mainLayout.addWidget(self.tabs, 1, 0, Qt.AlignCenter) parser = BibleVerseParser(config.parserStandarisation) self.bookMap = parser.standardAbbreviation bookNums = list(self.bookMap.keys()) bookNumGps = [ bookNums[0:10], bookNums[10:20], bookNums[20:30], bookNums[30:39], bookNums[39:49], bookNums[49:59], bookNums[59:66], ] bible = QWidget() bible_layout = QVBoxLayout() bible_layout.setMargin(0) bible_layout.setSpacing(0) for bookNumGp in bookNumGps[0:5]: gp = QWidget() layout = self.newRowLayout() for bookNum in bookNumGp: text = self.bookMap[bookNum] button = QPushButton(text) button.clicked.connect(partial(self.bibleBookAction, bookNum)) layout.addWidget(button) gp.setLayout(layout) bible_layout.addWidget(gp) for bookNumGp in bookNumGps[5:]: gp = QWidget() layout = self.newRowLayout() for bookNum in bookNumGp: text = self.bookMap[bookNum] button = QPushButton(text) button.clicked.connect(partial(self.bibleBookAction, bookNum)) layout.addWidget(button) gp.setLayout(layout) bible_layout.addWidget(gp) bible_layout.addStretch() bible.setLayout(bible_layout) self.tabs.addTab(bible, config.thisTranslation["bible"]) bibles_box = QWidget() box_layout = QVBoxLayout() box_layout.setMargin(0) box_layout.setSpacing(0) row_layout = self.newRowLayout() biblesSqlite = BiblesSqlite() bibles = biblesSqlite.getBibleList() count = 0 for bible in bibles: button = QPushButton(bible) button.clicked.connect(partial(self.bibleAction, bible)) row_layout.addWidget(button) count += 1 if count > 6: count = 0 box_layout.addLayout(row_layout) row_layout = self.newRowLayout() box_layout.addLayout(row_layout) box_layout.addStretch() bibles_box.setLayout(box_layout) self.tabs.addTab(bibles_box, config.thisTranslation["translations"]) commentaries_box = QWidget() box_layout = QVBoxLayout() box_layout.setMargin(0) box_layout.setSpacing(0) row_layout = self.newRowLayout() commentaries = Commentary().getCommentaryList() count = 0 for commentary in commentaries: button = QPushButton(commentary) button.clicked.connect(partial(self.commentaryAction, commentary)) row_layout.addWidget(button) count += 1 if count > 6: count = 0 box_layout.addLayout(row_layout) row_layout = self.newRowLayout() box_layout.addLayout(row_layout) box_layout.addStretch() commentaries_box.setLayout(box_layout) self.tabs.addTab(commentaries_box, config.thisTranslation["commentaries"]) lexicons_box = QWidget() box_layout = QVBoxLayout() box_layout.setMargin(0) box_layout.setSpacing(0) row_layout = self.newRowLayout() lexicons = LexiconData().lexiconList count = 0 for lexicon in lexicons: button = QPushButton(lexicon) button.clicked.connect(partial(self.lexiconAction, lexicon)) row_layout.addWidget(button) count += 1 if count > 6: count = 0 box_layout.addLayout(row_layout) row_layout = self.newRowLayout() box_layout.addLayout(row_layout) box_layout.addStretch() lexicons_box.setLayout(box_layout) self.tabs.addTab(lexicons_box, config.thisTranslation["lexicons"]) dictionaries_box = QWidget() box_layout = QVBoxLayout() box_layout.setMargin(0) box_layout.setSpacing(0) row_layout = self.newRowLayout() dictionaries = IndexesSqlite().dictionaryList count = 0 for dictionary in dictionaries: button = QPushButton(dictionary[0]) button.setToolTip(dictionary[1]) button.clicked.connect( partial(self.dictionaryAction, dictionary[0])) row_layout.addWidget(button) count += 1 if count > 6: count = 0 box_layout.addLayout(row_layout) row_layout = self.newRowLayout() box_layout.addLayout(row_layout) box_layout.addStretch() dictionaries_box.setLayout(box_layout) self.tabs.addTab(dictionaries_box, config.thisTranslation["dictionaries"]) self.tabs.setCurrentIndex(config.miniControlInitialTab) self.setLayout(mainLayout) def newRowLayout(self): row_layout = QHBoxLayout() row_layout.setSpacing(0) row_layout.setMargin(0) return row_layout def tabChanged(self, index): prefix = "" if index == 0: prefix = "BIBLE:::{0}:::".format(config.mainText) elif index == 1: prefix = "TEXT:::" elif index == 2: prefix = "COMMENTARY:::{0}:::".format(config.commentaryText) elif index == 3: prefix = "LEXICON:::" elif index == 4: prefix = "SEARCHTOOL:::" if not config.clearCommandEntry: self.searchLineEdit.setText(prefix) def searchLineEntered(self): searchString = self.searchLineEdit.text() self.parent.textCommandLineEdit.setText(searchString) self.parent.runTextCommand(searchString) self.searchLineEdit.setFocus() #def setTtsDefaultLanguage(self): #config.ttsDefaultLangauge = self.languageCodes[self.languageCombo.currentIndex()] def speakCommandFieldText(self): text = self.searchLineEdit.text() if ":::" in text: text = text.split(":::")[-1] command = "SPEAK:::{0}:::{1}".format( self.languageCodes[self.languageCombo.currentIndex()], text) self.runCommmand(command) def bibleBookAction(self, book): command = "{0} ".format(self.bookMap[book]) self.runCommmand(command) self.searchLineEdit.setFocus() def keyEntryAction(self, key): text = self.searchLineEdit.text() if key == "X": text = "" elif key == "<": text = text[:-1] else: text += key self.searchLineEdit.setText(text) def bibleAction(self, bible): command = "BIBLE:::{0}:::{1}".format( bible, self.parent.verseReference("main")[1]) self.runCommmand(command) command = "_bibleinfo:::{0}".format(bible) self.parent.runTextCommand(command) def commentaryAction(self, commentary): command = "COMMENTARY:::{0}:::{1}".format( commentary, self.parent.verseReference("main")[1]) self.runCommmand(command) command = "_commentaryinfo:::{0}".format(commentary) self.parent.runTextCommand(command) def lexiconAction(self, lexicon): command = "LEXICON:::{0}:::{1}".format( lexicon, TextCommandParser.last_lexicon_entry) self.runCommmand(command) def dictionaryAction(self, dictionary): command = "SEARCHTOOL:::{0}:::{1}".format( dictionary, TextCommandParser.last_text_search) self.runCommmand(command) def runCommmand(self, command): self.searchLineEdit.setText(command) self.parent.runTextCommand(command) self.parent.textCommandLineEdit.setText(command)
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 MasterControl(QWidget): def __init__(self, parent, initialTab=0, b=config.mainB, c=config.mainC, v=config.mainV, text=config.mainText): super().__init__() self.isRefreshing = True self.parent = parent # set title self.setWindowTitle(config.thisTranslation["controlPanel"]) if config.restrictControlPanelWidth and config.screenWidth > config.masterControlWidth: self.setFixedWidth(config.masterControlWidth) # setup item option lists self.setupResourceLists() # setup interface self.text = text self.setupUI(b, c, v, text, initialTab) self.isRefreshing = False # manage key capture def event(self, event): if event.type() == QEvent.KeyRelease: if event.modifiers() == Qt.ControlModifier: if event.key() == Qt.Key_B: self.tabs.setCurrentIndex(0) elif event.key() == Qt.Key_L: self.tabs.setCurrentIndex(1) elif event.key() == Qt.Key_F: self.tabs.setCurrentIndex(2) elif event.key() == Qt.Key_Y: self.tabs.setCurrentIndex(3) elif event.key() == Qt.Key_M: self.tabs.setCurrentIndex(4) elif event.key() == Qt.Key_Escape: self.hide() return QWidget.event(self, event) def closeEvent(self, event): # Control panel is designed for frequent use # Hiding it instead of closing may save time from reloading event.ignore() self.hide() def setupResourceLists(self): self.parent.setupResourceLists() # bible versions self.textList = self.parent.textList self.textFullNameList = self.parent.textFullNameList self.strongBibles = self.parent.strongBibles if self.parent.versionCombo is not None and config.menuLayout in ( "classic", "focus", "aleph"): for index, fullName in enumerate(self.textFullNameList): self.parent.versionCombo.setItemData(index, fullName, Qt.ToolTipRole) # commentaries self.commentaryList = self.parent.commentaryList #self.commentaryFullNameList = [Commentary(module).commentaryInfo() for module in self.commentaryList] self.commentaryFullNameList = self.parent.commentaryFullNameList # reference book # menu10_dialog self.referenceBookList = self.parent.referenceBookList # topic # menu5_topics self.topicListAbb = self.parent.topicListAbb self.topicList = self.parent.topicList # lexicon # context1_originalLexicon self.lexiconList = self.parent.lexiconList # dictionary # context1_dict self.dictionaryListAbb = self.parent.dictionaryListAbb self.dictionaryList = self.parent.dictionaryList # encyclopedia # context1_encyclopedia self.encyclopediaListAbb = self.parent.encyclopediaListAbb self.encyclopediaList = self.parent.encyclopediaList # 3rd-party dictionary # menu5_3rdDict self.thirdPartyDictionaryList = self.parent.thirdPartyDictionaryList # def setupItemLists(self): # # bible versions # self.textList = BiblesSqlite().getBibleList() # self.textFullNameList = [Bible(text).bibleInfo() for text in self.textList] # self.strongBibles = [text for text in self.textList if Bible(text).bibleStrong()] # if self.parent.versionCombo is not None and config.menuLayout in ("classic", "focus", "aleph"): # for index, fullName in enumerate(self.textFullNameList): # self.parent.versionCombo.setItemData(index, fullName, Qt.ToolTipRole) # # commentaries # self.commentaryList = Commentary().getCommentaryList() # #self.commentaryFullNameList = [Commentary(module).commentaryInfo() for module in self.commentaryList] # self.commentaryFullNameList = [] # for module in self.commentaryList: # info = Commentary(module).commentaryInfo() # if info == "https://Marvel.Bible Commentary" and module in Commentary.marvelCommentaries: # info = Commentary.marvelCommentaries[module] # self.commentaryFullNameList.append(info) # # reference book # # menu10_dialog # bookData = BookData() # self.referenceBookList = [book for book, *_ in bookData.getBookList()] # # open database # indexes = IndexesSqlite() # # topic # # menu5_topics # topicDictAbb2Name = {abb: name for abb, name in indexes.topicList} # self.topicListAbb = list(topicDictAbb2Name.keys()) # topicDict = {name: abb for abb, name in indexes.topicList} # self.topicList = list(topicDict.keys()) # # lexicon # # context1_originalLexicon # self.lexiconList = LexiconData().lexiconList # # dictionary # # context1_dict # dictionaryDictAbb2Name = {abb: name for abb, name in indexes.dictionaryList} # self.dictionaryListAbb = list(dictionaryDictAbb2Name.keys()) # dictionaryDict = {name: abb for abb, name in indexes.dictionaryList} # self.dictionaryList = list(dictionaryDict.keys()) # # encyclopedia # # context1_encyclopedia # encyclopediaDictAbb2Name = {abb: name for abb, name in indexes.encyclopediaList} # self.encyclopediaListAbb = list(encyclopediaDictAbb2Name.keys()) # encyclopediaDict = {name: abb for abb, name in indexes.encyclopediaList} # self.encyclopediaList = list(encyclopediaDict.keys()) # # 3rd-party dictionary # # menu5_3rdDict # self.thirdPartyDictionaryList = ThirdPartyDictionary(self.parent.textCommandParser.isThridPartyDictionary(config.thirdDictionary)).moduleList def setupUI(self, b, c, v, text, initialTab): mainLayout = QVBoxLayout() mainLayout.addWidget(self.sharedWidget()) mainLayout.addWidget(self.tabWidget(b, c, v, text, initialTab)) self.setLayout(mainLayout) def sharedWidget(self): sharedWidget = QWidget() sharedWidgetLayout = QVBoxLayout() subLayout = QHBoxLayout() subLayout.addWidget(self.commandFieldWidget()) subLayout.addWidget(self.autoCloseCheckBox()) sharedWidgetLayout.addLayout(subLayout) sharedWidget.setLayout(sharedWidgetLayout) return sharedWidget def updateBibleTabText(self, reference): self.tabs.setTabText(0, reference) def tabWidget(self, b, c, v, text, initialTab): self.tabs = QTabWidget() # 0 self.bibleTab = BibleExplorer(self, (b, c, v, text)) self.tabs.addTab(self.bibleTab, config.thisTranslation["cp0"]) self.tabs.setTabToolTip(0, config.thisTranslation["cp0Tip"]) # 1 libraryTab = LibraryLauncher(self) self.tabs.addTab(libraryTab, config.thisTranslation["cp1"]) self.tabs.setTabToolTip(1, config.thisTranslation["cp1Tip"]) # 2 self.toolTab = SearchLauncher(self) self.tabs.addTab(self.toolTab, config.thisTranslation["cp2"]) self.tabs.setTabToolTip(2, config.thisTranslation["cp2Tip"]) # 3 self.historyTab = HistoryLauncher(self) self.tabs.addTab(self.historyTab, config.thisTranslation["cp3"]) self.tabs.setTabToolTip(3, config.thisTranslation["cp3Tip"]) # 4 self.miscellaneousTab = MiscellaneousLauncher(self) self.tabs.addTab(self.miscellaneousTab, config.thisTranslation["cp4"]) self.tabs.setTabToolTip(4, config.thisTranslation["cp4Tip"]) # set action with changing tabs self.tabs.currentChanged.connect(self.tabChanged) # set initial tab self.tabs.setCurrentIndex(initialTab) return self.tabs def commandFieldWidget(self): self.commandField = QLineEdit() self.commandField.setClearButtonEnabled(True) self.commandField.setToolTip( config.thisTranslation["enter_command_here"]) self.commandField.returnPressed.connect(self.commandEntered) return self.commandField def autoCloseCheckBox(self): checkbox = QCheckBox() checkbox.setText(config.thisTranslation["autoClose"]) checkbox.setToolTip(config.thisTranslation["autoCloseToolTip"]) checkbox.setChecked(config.closeControlPanelAfterRunningCommand) checkbox.stateChanged.connect( self.closeControlPanelAfterRunningCommandChanged) return checkbox # Common layout def buttonsWidget(self, buttonElementTupleTuple, r2l=False, translation=True): buttons = QWidget() buttonsLayouts = QVBoxLayout() buttonsLayouts.setSpacing(3) for buttonElementTuple in buttonElementTupleTuple: buttonsLayouts.addLayout( self.buttonsLayout(buttonElementTuple, r2l, translation)) buttons.setLayout(buttonsLayouts) return buttons def buttonsLayout(self, buttonElementTuple, r2l=False, translation=True): buttonsLayout = QBoxLayout( QBoxLayout.RightToLeft if r2l else QBoxLayout.LeftToRight) buttonsLayout.setSpacing(5) for label, action in buttonElementTuple: buttonLabel = config.thisTranslation[ label] if translation else label button = QPushButton(buttonLabel) button.clicked.connect(action) buttonsLayout.addWidget(button) return buttonsLayout def comboFeatureLayout(self, feature, combo, action): # QGridLayout: https://stackoverflow.com/questions/61451279/how-does-setcolumnstretch-and-setrowstretch-works layout = QGridLayout() layout.setSpacing(5) # combo layout.addWidget(combo, 0, 0, 1, 3) # button button = QPushButton(config.thisTranslation[feature]) button.clicked.connect(action) layout.addWidget(button, 0, 3, 1, 1) return layout # Actions def closeControlPanelAfterRunningCommandChanged(self): config.closeControlPanelAfterRunningCommand = not config.closeControlPanelAfterRunningCommand def updateBCVText(self, b, c, v, text): self.bibleTab.updateBCVText(b, c, v, text) def commandEntered(self): command = self.commandField.text() self.runTextCommand(command, False) def runTextCommand(self, command, printCommand=True, reloadMainWindow=False): if printCommand: self.commandField.setText(command) self.parent.textCommandLineEdit.setText(command) self.parent.runTextCommand(command) if reloadMainWindow: self.parent.reloadCurrentRecord() if config.closeControlPanelAfterRunningCommand and not self.isRefreshing: self.hide() def tabChanged(self, index): self.isRefreshing = True # refresh content if index == 3: self.historyTab.refresh() elif index == 4: self.miscellaneousTab.refresh() # set focus if index == 2: self.toolTab.searchField.setFocus() if config.contextItem: self.toolTab.searchField.setText(config.contextItem) config.contextItem = "" elif self.parent.mainView.currentWidget().selectedText(): self.toolTab.searchField.setText( self.parent.mainView.currentWidget().selectedText()) elif self.parent.studyView.currentWidget().selectedText(): self.toolTab.searchField.setText( self.parent.studyView.currentWidget().selectedText()) else: self.commandField.setFocus() self.isRefreshing = False def displayMessage(self, message="", title="UniqueBible"): reply = QMessageBox.information(self, title, message)
class SampleLogsView(QSplitter): """Sample Logs View This contains a table of the logs, a plot of the currently selected logs, and the statistics of the selected log. """ def __init__(self, presenter, parent=None, window_flags=Qt.Window, name='', isMD=False, noExp=0): super(SampleLogsView, self).__init__(parent) self.presenter = presenter self.setWindowTitle("{} sample logs".format(name)) self.setWindowFlags(window_flags) self.setAttribute(Qt.WA_DeleteOnClose, True) # left hand side self.frame_left = QFrame() layout_left = QVBoxLayout() # add a spin box for MD workspaces if isMD: layout_mult_expt_info = QHBoxLayout() layout_mult_expt_info.addWidget(QLabel("Experiment Info #")) self.experimentInfo = QSpinBox() self.experimentInfo.setMaximum(noExp - 1) self.experimentInfo.valueChanged.connect( self.presenter.changeExpInfo) layout_mult_expt_info.addWidget(self.experimentInfo) layout_mult_expt_info.addSpacerItem( QSpacerItem(10, 10, QSizePolicy.Expanding)) layout_left.addLayout(layout_mult_expt_info) # create a line edit to allow for filtering keys self.line_edit = QLineEdit() self.line_edit.setClearButtonEnabled(True) self.line_edit.setToolTip("Type here to filter the logs") self.line_edit.setPlaceholderText("Search the logs") self.line_edit.textEdited.connect(self.presenter.search_key_changed) layout_left.addWidget(self.line_edit) # Create sample log table self.table = QTableView() self.table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table.doubleClicked.connect(self.presenter.doubleClicked) self.table.contextMenuEvent = self.tableMenu layout_left.addWidget(self.table) self.frame_left.setLayout(layout_left) self.addWidget(self.frame_left) #right hand side self.frame_right = QFrame() layout_right = QVBoxLayout() #Add full_time and experimentinfo options layout_options = QHBoxLayout() if isMD: layout_options.addWidget(QLabel("Experiment Info #")) self.experimentInfo = QSpinBox() self.experimentInfo.setMaximum(noExp - 1) self.experimentInfo.valueChanged.connect( self.presenter.changeExpInfo) layout_options.addWidget(self.experimentInfo) #check boxes self.full_time = QCheckBox("Relative Time") self.full_time.setToolTip( "Shows relative time in seconds from the start of the run.") self.full_time.setChecked(True) self.full_time.stateChanged.connect(self.presenter.plot_logs) layout_options.addWidget(self.full_time) self.show_filtered = QCheckBox("Filtered Data") self.show_filtered.setToolTip( "Filtered data only shows data while running and in this period.\nInvalid values are also filtered." ) self.show_filtered.setChecked(True) self.show_filtered.stateChanged.connect( self.presenter.filtered_changed) layout_options.addWidget(self.show_filtered) self.spaceItem = QSpacerItem(10, 10, QSizePolicy.Expanding) layout_options.addSpacerItem(self.spaceItem) layout_right.addLayout(layout_options) # Sample log plot self.fig = Figure() self.canvas = FigureCanvas(self.fig) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.canvas.mpl_connect('button_press_event', self.presenter.plot_clicked) self.ax = self.fig.add_subplot(111, projection='mantid') layout_right.addWidget(self.canvas) # Sample stats self.create_stats_widgets() layout_stats = QFormLayout() layout_stats.addRow('', QLabel("Log Statistics")) layout_stats.addRow('Min:', self.stats_widgets["minimum"]) layout_stats.addRow('Max:', self.stats_widgets["maximum"]) layout_stats.addRow('Time Avg:', self.stats_widgets["time_mean"]) layout_stats.addRow('Time Std Dev:', self.stats_widgets["time_standard_deviation"]) layout_stats.addRow('Mean (unweighted):', self.stats_widgets["mean"]) layout_stats.addRow('Median (unweighted):', self.stats_widgets["median"]) layout_stats.addRow('Std Dev:', self.stats_widgets["standard_deviation"]) layout_stats.addRow('Duration:', self.stats_widgets["duration"]) layout_right.addLayout(layout_stats) self.frame_right.setLayout(layout_right) self.addWidget(self.frame_right) self.setStretchFactor(0, 1) self.resize(1200, 800) self.show() def closeEvent(self, event): self.deleteLater() super(SampleLogsView, self).closeEvent(event) def tableMenu(self, event): """Right click menu for table, can plot or print selected logs""" menu = QMenu(self) plotAction = menu.addAction("Plot selected") plotAction.triggered.connect(self.presenter.new_plot_logs) plotAction = menu.addAction("Print selected") plotAction.triggered.connect(self.presenter.print_selected_logs) menu.exec_(event.globalPos()) def set_model(self, model): """Set the model onto the table""" self.model = model self.table.setModel(self.model) self.table.resizeColumnsToContents() self.table.horizontalHeader().setSectionResizeMode( 2, QHeaderView.Stretch) self.table.selectionModel().selectionChanged.connect( self.presenter.update) def show_plot_and_stats(self, show_plot_and_stats): """sets wether the plot and stats section should be visible""" if self.frame_right.isVisible() != show_plot_and_stats: # the desired state is nor the current state self.setUpdatesEnabled(False) current_width = self.frame_right.width() if current_width: self.last_width = current_width else: current_width = self.last_width if show_plot_and_stats: self.resize(self.width() + current_width, self.height()) else: self.resize(self.width() - current_width, self.height()) self.frame_right.setVisible(show_plot_and_stats) self.setUpdatesEnabled(True) def plot_selected_logs(self, ws, exp, rows): """Update the plot with the selected rows""" if self.frame_right.isVisible(): self.ax.clear() self.create_ax_by_rows(self.ax, ws, exp, rows) try: self.fig.canvas.draw() except ValueError as ve: #this can throw an error if the plot has recently been hidden, but the error does not matter if not str(ve).startswith("Image size of"): raise def new_plot_selected_logs(self, ws, exp, rows): """Create a new plot, in a separate window for selected rows""" fig, ax = plt.subplots(subplot_kw={'projection': 'mantid'}) self.create_ax_by_rows(ax, ws, exp, rows) fig.show() def create_ax_by_rows(self, ax, ws, exp, rows): """Creates the plots for given rows onto axis ax""" for row in rows: log_text = self.get_row_log_name(row) ax.plot(ws, LogName=log_text, label=log_text, FullTime=not self.full_time.isChecked(), Filtered=self.show_filtered.isChecked(), ExperimentInfo=exp) ax.set_ylabel('') if ax.get_legend_handles_labels()[0]: ax.legend() def set_log_controls(self, are_logs_filtered): """Sets log specific settings based on the log clicked on""" self.show_filtered.setEnabled(are_logs_filtered) def get_row_log_name(self, i): """Returns the log name of particular row""" return str(self.model.item(i, 0).text()) def get_exp(self): """Get set experiment info number""" return self.experimentInfo.value() def get_selected_row_indexes(self): """Return a list of selected row from table""" return [ row.row() for row in self.table.selectionModel().selectedRows() ] def set_selected_rows(self, rows): """Set seleceted rows in table""" mode = QItemSelectionModel.Select | QItemSelectionModel.Rows for row in rows: self.table.selectionModel().select(self.model.index(row, 0), mode) def create_stats_widgets(self): """Creates the statistics widgets""" self.stats_widgets = { "minimum": QLineEdit(), "maximum": QLineEdit(), "mean": QLineEdit(), "median": QLineEdit(), "standard_deviation": QLineEdit(), "time_mean": QLineEdit(), "time_standard_deviation": QLineEdit(), "duration": QLineEdit() } for widget in self.stats_widgets.values(): widget.setReadOnly(True) def set_statistics(self, stats): """Updates the statistics widgets from stats dictionary""" for param in self.stats_widgets.keys(): self.stats_widgets[param].setText('{:.6}'.format( getattr(stats, param))) def clear_statistics(self): """Clears the values in statistics widgets""" for widget in self.stats_widgets.values(): widget.clear()