class ChannelPropertiesDialog(QDialog): def __init__(self, parent, info, title="Channel Properties"): super().__init__(parent) self.setWindowTitle(title) self.model = QStandardItemModel(info["nchan"], 4) self.model.setHorizontalHeaderLabels(["#", "Label", "Type", "Bad"]) for index, ch in enumerate(info["chs"]): item = QStandardItem() item.setData(index, Qt.DisplayRole) item.setFlags(item.flags() & ~Qt.ItemIsEditable) self.model.setItem(index, 0, item) self.model.setItem(index, 1, QStandardItem(ch["ch_name"])) kind = channel_type(info, index).upper() self.model.setItem(index, 2, QStandardItem(str(kind))) bad = QStandardItem() bad.setData(ch["ch_name"] in info["bads"], Qt.UserRole) bad.setCheckable(True) bad.setEditable(False) checked = ch["ch_name"] in info["bads"] bad.setCheckState(Qt.Checked if checked else Qt.Unchecked) self.model.setItem(index, 3, bad) self.model.itemChanged.connect(bad_changed) self.proxymodel = MySortFilterProxyModel() self.proxymodel.setDynamicSortFilter(False) self.proxymodel.setSourceModel(self.model) self.view = QTableView() self.view.setModel(self.proxymodel) self.view.setItemDelegateForColumn(2, ComboBoxDelegate(self.view)) self.view.setEditTriggers(QAbstractItemView.AllEditTriggers) self.view.verticalHeader().setVisible(False) self.view.horizontalHeader().setStretchLastSection(True) self.view.setShowGrid(False) self.view.setSelectionMode(QAbstractItemView.NoSelection) self.view.setSortingEnabled(True) self.view.sortByColumn(0, Qt.AscendingOrder) vbox = QVBoxLayout(self) vbox.addWidget(self.view) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) vbox.addWidget(self.buttonbox) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) self.resize(475, 650) self.view.setColumnWidth(0, 70) self.view.setColumnWidth(1, 155) self.view.setColumnWidth(2, 90)
class ConfigFlagsWindow(QDialog): def __init__(self, parent): super().__init__() self.parent = parent # set title self.setWindowTitle(config.thisTranslation["menu_config_flags"]) self.setMinimumSize(830, 500) # set variables self.setupVariables() # setup interface self.setupUI() def setupVariables(self): self.isUpdating = False def setupUI(self): mainLayout = QVBoxLayout() title = QLabel(config.thisTranslation["menu_config_flags"]) title.mouseReleaseEvent = self.openWiki mainLayout.addWidget(title) filterLayout = QHBoxLayout() filterLayout.addWidget(QLabel(config.thisTranslation["menu5_search"])) self.filterEntry = QLineEdit() self.filterEntry.textChanged.connect(self.resetItems) filterLayout.addWidget(self.filterEntry) mainLayout.addLayout(filterLayout) self.dataView = QTableView() self.dataView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.dataView.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.dataView) self.dataView.setModel(self.dataViewModel) self.resetItems() self.dataViewModel.itemChanged.connect(self.itemChanged) mainLayout.addWidget(self.dataView) buttonLayout = QHBoxLayout() button = QPushButton(config.thisTranslation["close"]) button.clicked.connect(self.close) buttonLayout.addWidget(button) button = QPushButton(config.thisTranslation["restoreAllDefaults"]) button.clicked.connect(self.restoreAllDefaults) buttonLayout.addWidget(button) mainLayout.addLayout(buttonLayout) self.setLayout(mainLayout) def getOptions(self): options = [ ("showControlPanelOnStartup", config.showControlPanelOnStartup, self.showControlPanelOnStartupChanged, False, config.thisTranslation["showControlPanelOnStartup"]), ("preferControlPanelForCommandLineEntry", config.preferControlPanelForCommandLineEntry, self.preferControlPanelForCommandLineEntryChanged, False, config.thisTranslation["preferControlPanelForCommandLineEntry"]), ("closeControlPanelAfterRunningCommand", config.closeControlPanelAfterRunningCommand, self.closeControlPanelAfterRunningCommandChanged, True, config.thisTranslation["closeControlPanelAfterRunningCommand"]), ("restrictControlPanelWidth", config.restrictControlPanelWidth, self.restrictControlPanelWidthChanged, False, config.thisTranslation["restrictControlPanelWidth"]), ("clearCommandEntry", config.clearCommandEntry, self.clearCommandEntryChanged, False, config.thisTranslation["clearCommandEntry"]), ("openBibleWindowContentOnNextTab", config.openBibleWindowContentOnNextTab, self.openBibleWindowContentOnNextTabChanged, False, config.thisTranslation["openBibleWindowContentOnNextTab"]), ("openStudyWindowContentOnNextTab", config.openStudyWindowContentOnNextTab, self.openStudyWindowContentOnNextTabChanged, True, config.thisTranslation["openStudyWindowContentOnNextTab"]), ("populateTabsOnStartup", config.populateTabsOnStartup, self.populateTabsOnStartupChanged, False, config.thisTranslation["populateTabsOnStartup"]), ("qtMaterial", config.qtMaterial, self.qtMaterialChanged, False, config.thisTranslation["qtMaterial"]), ("addBreakAfterTheFirstToolBar", config.addBreakAfterTheFirstToolBar, self.addBreakAfterTheFirstToolBarChanged, True, config.thisTranslation["addBreakAfterTheFirstToolBar"]), ("addBreakBeforeTheLastToolBar", config.addBreakBeforeTheLastToolBar, self.addBreakBeforeTheLastToolBarChanged, False, config.thisTranslation["addBreakBeforeTheLastToolBar"]), ("parserStandarisation", (config.parserStandarisation == "YES"), self.parserStandarisationChanged, False, config.thisTranslation["parserStandarisation"]), ("useLiteVerseParsing", config.useLiteVerseParsing, self.useLiteVerseParsingChanged, False, config.thisTranslation["useLiteVerseParsing"]), ("parseEnglishBooksOnly", config.parseEnglishBooksOnly, self.parseEnglishBooksOnlyChanged, False, config.thisTranslation["parseEnglishBooksOnly"]), ("parseWordDocument", config.parseWordDocument, self.parseWordDocumentChanged, True, config.thisTranslation["parseWordDocument"]), ("convertChapterVerseDotSeparator", config.convertChapterVerseDotSeparator, self.convertChapterVerseDotSeparatorChanged, True, config.thisTranslation["convertChapterVerseDotSeparator"]), ("parseBookChapterWithoutSpace", config.parseBookChapterWithoutSpace, self.parseBookChapterWithoutSpaceChanged, True, config.thisTranslation["parseBookChapterWithoutSpace"]), ("parseBooklessReferences", config.parseBooklessReferences, self.parseBooklessReferencesChanged, True, config.thisTranslation["parseBooklessReferences"]), ("searchBibleIfCommandNotFound", config.searchBibleIfCommandNotFound, self.searchBibleIfCommandNotFoundChanged, True, config.thisTranslation["searchBibleIfCommandNotFound"]), ("regexSearchBibleIfCommandNotFound", config.regexSearchBibleIfCommandNotFound, self.regexSearchBibleIfCommandNotFoundChanged, False, config.thisTranslation["regexSearchBibleIfCommandNotFound"]), ("preferHtmlMenu", config.preferHtmlMenu, self.preferHtmlMenuChanged, False, config.thisTranslation["preferHtmlMenu"]), ("showVerseNumbersInRange", config.showVerseNumbersInRange, self.showVerseNumbersInRangeChanged, True, config.thisTranslation["showVerseNumbersInRange"]), ("addFavouriteToMultiRef", config.addFavouriteToMultiRef, self.addFavouriteToMultiRefChanged, False, config.thisTranslation["addFavouriteToMultiRef"]), ("enableVerseHighlighting", config.enableVerseHighlighting, self.enableVerseHighlightingChanged, True, config.thisTranslation["enableVerseHighlighting"]), ("regexCaseSensitive", config.regexCaseSensitive, self.regexCaseSensitiveChanged, False, config.thisTranslation["regexCaseSensitive"]), ("alwaysDisplayStaticMaps", config.alwaysDisplayStaticMaps, self.alwaysDisplayStaticMapsChanged, False, config.thisTranslation["alwaysDisplayStaticMaps"]), ("exportEmbeddedImages", config.exportEmbeddedImages, self.exportEmbeddedImagesChanged, True, config.thisTranslation["exportEmbeddedImages"]), ("clickToOpenImage", config.clickToOpenImage, self.clickToOpenImageChanged, True, config.thisTranslation["clickToOpenImage"]), ("showNoteIndicatorOnBibleChapter", config.showNoteIndicatorOnBibleChapter, self.parent.enableNoteIndicatorButtonClicked, True, config.thisTranslation["showNoteIndicatorOnBibleChapter"]), ("openBibleNoteAfterSave", config.openBibleNoteAfterSave, self.openBibleNoteAfterSaveChanged, False, config.thisTranslation["openBibleNoteAfterSave"]), ("openBibleNoteAfterEditorClosed", config.openBibleNoteAfterEditorClosed, self.openBibleNoteAfterEditorClosedChanged, False, config.thisTranslation["openBibleNoteAfterEditorClosed"]), ("hideNoteEditorStyleToolbar", config.hideNoteEditorStyleToolbar, self.hideNoteEditorStyleToolbarChanged, False, config.thisTranslation["hideNoteEditorStyleToolbar"]), ("hideNoteEditorTextUtility", config.hideNoteEditorTextUtility, self.hideNoteEditorTextUtilityChanged, True, config.thisTranslation["hideNoteEditorTextUtility"]), ("overwriteNoteFont", config.overwriteNoteFont, self.overwriteNoteFontChanged, True, config.thisTranslation["overwriteNoteFont"]), ("overwriteNoteFontSize", config.overwriteNoteFontSize, self.overwriteNoteFontSizeChanged, True, config.thisTranslation["overwriteNoteFontSize"]), ("overwriteBookFont", config.overwriteBookFont, self.overwriteBookFontChanged, True, config.thisTranslation["overwriteBookFont"]), ("overwriteBookFontSize", config.overwriteBookFontSize, self.overwriteBookFontSizeChanged, True, config.thisTranslation["overwriteBookFontSize"]), ("openBookInNewWindow", config.openBookInNewWindow, self.openBookInNewWindowChanged, False, config.thisTranslation["openBookInNewWindow"]), ("openPdfViewerInNewWindow", config.openPdfViewerInNewWindow, self.openPdfViewerInNewWindowChanged, False, config.thisTranslation["openPdfViewerInNewWindow"]), ("virtualKeyboard", config.virtualKeyboard, self.virtualKeyboardChanged, False, config.thisTranslation["virtualKeyboard"]), ("useWebbrowser", config.useWebbrowser, self.useWebbrowserChanged, True, config.thisTranslation["useWebbrowser"]), ("removeHighlightOnExit", config.removeHighlightOnExit, self.removeHighlightOnExitChanged, False, config.thisTranslation["removeHighlightOnExit"]), ("disableModulesUpdateCheck", config.disableModulesUpdateCheck, self.disableModulesUpdateCheckChanged, True, config.thisTranslation["disableModulesUpdateCheck"]), ("updateWithGitPull", config.updateWithGitPull, self.updateWithGitPullChanged, False, config.thisTranslation["updateWithGitPull"]), ("enableGist", config.enableGist, self.enableGistChanged, False, config.thisTranslation["enableGist"]), ("enableMacros", config.enableMacros, self.enableMacrosChanged, False, config.thisTranslation["enableMacros"]), ("enablePlugins", config.enablePlugins, self.enablePluginsChanged, True, config.thisTranslation["enablePlugins"]), ("hideBlankVerseCompare", config.hideBlankVerseCompare, self.hideBlankVerseCompareChanged, False, config.thisTranslation["hideBlankVerseCompare"]), ("enforceCompareParallel", config.enforceCompareParallel, self.parent.enforceCompareParallelButtonClicked, False, config.thisTranslation["enforceCompareParallel"]), ("enableMenuUnderline", config.enableMenuUnderline, self.enableMenuUnderlineChanged, True, config.thisTranslation["enableMenuUnderline"]), ("openBibleInMainViewOnly", config.openBibleInMainViewOnly, self.parent.enableStudyBibleButtonClicked, False, config.thisTranslation["openBibleInMainViewOnly"]), ("addOHGBiToMorphologySearch", config.addOHGBiToMorphologySearch, self.addOHGBiToMorphologySearchChanged, True, config.thisTranslation["addOHGBiToMorphologySearch"]), ("includeStrictDocTypeInNote", config.includeStrictDocTypeInNote, self.includeStrictDocTypeInNoteChanged, False, config.thisTranslation["includeStrictDocTypeInNote"]), ("parseTextConvertNotesToBook", config.parseTextConvertNotesToBook, self.parseTextConvertNotesToBookChanged, False, config.thisTranslation["parseTextConvertNotesToBook"]), ("parseTextConvertHTMLToBook", config.parseTextConvertHTMLToBook, self.parseTextConvertHTMLToBookChanged, False, config.thisTranslation["parseTextConvertHTMLToBook"]), ("displayCmdOutput", config.displayCmdOutput, self.displayCmdOutputChanged, False, config.thisTranslation["displayCmdOutput"]), ("disableLoadLastOpenFilesOnStartup", config.disableLoadLastOpenFilesOnStartup, self.disableLoadLastOpenFilesOnStartupChanged, False, config.thisTranslation["disableLoadLastOpenFilesOnStartup"]), ("disableOpenPopupWindowOnStartup", config.disableOpenPopupWindowOnStartup, self.disableOpenPopupWindowOnStartupChanged, True, config.thisTranslation["disableOpenPopupWindowOnStartup"]), ("showMiniKeyboardInMiniControl", config.showMiniKeyboardInMiniControl, self.showMiniKeyboardInMiniControlChanged, False, config.thisTranslation["showMiniKeyboardInMiniControl"]), ] if config.isTtsInstalled: options += [ ("useLangDetectOnTts", config.useLangDetectOnTts, self.useLangDetectOnTtsChanged, False, config.thisTranslation["useLangDetectOnTts"]), ("ttsEnglishAlwaysUS", config.ttsEnglishAlwaysUS, self.ttsEnglishAlwaysUSChanged, False, config.thisTranslation["ttsEnglishAlwaysUS"]), ("ttsEnglishAlwaysUK", config.ttsEnglishAlwaysUK, self.ttsEnglishAlwaysUKChanged, False, config.thisTranslation["ttsEnglishAlwaysUK"]), ("ttsChineseAlwaysMandarin", config.ttsChineseAlwaysMandarin, self.ttsChineseAlwaysMandarinChanged, False, config.thisTranslation["ttsChineseAlwaysMandarin"]), ("ttsChineseAlwaysCantonese", config.ttsChineseAlwaysCantonese, self.ttsChineseAlwaysCantoneseChanged, False, config.thisTranslation["ttsChineseAlwaysCantonese"]), ] if platform.system() == "Linux": options += [ ("linuxStartFullScreen", config.linuxStartFullScreen, self.linuxStartFullScreenChanged, False, config.thisTranslation["linuxStartFullScreen"]), ("fcitx", config.fcitx, self.fcitxChanged, False, config.thisTranslation["fcitx"]), ("ibus", config.ibus, self.ibusChanged, False, config.thisTranslation["ibus"]), ("espeak", config.espeak, self.espeakChanged, False, config.thisTranslation["espeak"]), ] if config.developer: options += [ ("forceGenerateHtml", config.forceGenerateHtml, self.forceGenerateHtmlChanged, False, config.thisTranslation["forceGenerateHtml"]), ("enableLogging", config.enableLogging, self.enableLoggingChanged, False, config.thisTranslation["enableLogging"]), ("logCommands", config.logCommands, self.logCommandsChanged, False, config.thisTranslation["logCommands"]), ] data = {} for flag, configValue, action, default, tooltip in options: data[flag] = [configValue, default, tooltip, action] return data def restoreAllDefaults(self): for key, value in self.data.items(): code = "config.{0} = {1}".format(key, value[1]) exec(code) self.resetItems() self.displayMessage(config.thisTranslation["message_restart"]) def itemChanged(self, standardItem): flag = standardItem.text() if flag in self.data and not self.isUpdating: self.data[flag][-1]() def resetItems(self): self.isUpdating = True # Empty the model before reset self.dataViewModel.clear() # Reset self.data = self.getOptions() filterEntry = self.filterEntry.text().lower() rowCount = 0 for flag, value in self.data.items(): configValue, default, tooltip, *_ = value if filterEntry == "" or (filterEntry != "" and (filterEntry in flag.lower() or filterEntry in tooltip.lower())): # 1st column item = QStandardItem(flag) item.setToolTip(tooltip) item.setCheckable(True) item.setCheckState(Qt.CheckState.Checked if configValue else Qt.CheckState.Unchecked) self.dataViewModel.setItem(rowCount, 0, item) # 2nd column item = QStandardItem(str(default)) self.dataViewModel.setItem(rowCount, 1, item) # 3rd column tooltip = tooltip.replace("\n", " ") item = QStandardItem(tooltip) item.setToolTip(tooltip) self.dataViewModel.setItem(rowCount, 2, item) # add row count rowCount += 1 self.dataViewModel.setHorizontalHeaderLabels([ config.thisTranslation["flag"], config.thisTranslation["default"], config.thisTranslation["description"] ]) self.dataView.resizeColumnsToContents() self.isUpdating = False def displayMessage(self, message="", title="UniqueBible"): QMessageBox.information(self, title, message) def openWiki(self, event): wikiLink = "https://github.com/eliranwong/UniqueBible/wiki/Config-file-reference" webbrowser.open(wikiLink) def ibusChanged(self): config.ibus = not config.ibus if config.fcitx and config.ibus: config.fcitx = not config.fcitx if config.virtualKeyboard and config.ibus: config.virtualKeyboard = not config.virtualKeyboard self.displayMessage(config.thisTranslation["message_restart"]) def fcitxChanged(self): config.fcitx = not config.fcitx if config.fcitx and config.ibus: config.ibus = not config.ibus if config.fcitx and config.virtualKeyboard: config.virtualKeyboard = not config.virtualKeyboard self.displayMessage(config.thisTranslation["message_restart"]) def virtualKeyboardChanged(self): config.virtualKeyboard = not config.virtualKeyboard if config.fcitx and config.virtualKeyboard: config.fcitx = not config.fcitx if config.virtualKeyboard and config.ibus: config.ibus = not config.ibus self.displayMessage(config.thisTranslation["message_restart"]) def parseWordDocumentChanged(self): config.parseWordDocument = not config.parseWordDocument def useLangDetectOnTtsChanged(self): config.useLangDetectOnTts = not config.useLangDetectOnTts def ttsEnglishAlwaysUSChanged(self): config.ttsEnglishAlwaysUS = not config.ttsEnglishAlwaysUS if config.ttsEnglishAlwaysUK and config.ttsEnglishAlwaysUS: config.ttsEnglishAlwaysUK = not config.ttsEnglishAlwaysUK def ttsEnglishAlwaysUKChanged(self): config.ttsEnglishAlwaysUK = not config.ttsEnglishAlwaysUK if config.ttsEnglishAlwaysUK and config.ttsEnglishAlwaysUS: config.ttsEnglishAlwaysUS = not config.ttsEnglishAlwaysUS def ttsChineseAlwaysMandarinChanged(self): config.ttsChineseAlwaysMandarin = not config.ttsChineseAlwaysMandarin if config.ttsChineseAlwaysMandarin and config.ttsChineseAlwaysCantonese: config.ttsChineseAlwaysCantonese = not config.ttsChineseAlwaysCantonese def ttsChineseAlwaysCantoneseChanged(self): config.ttsChineseAlwaysCantonese = not config.ttsChineseAlwaysCantonese if config.ttsChineseAlwaysMandarin and config.ttsChineseAlwaysCantonese: config.ttsChineseAlwaysMandarin = not config.ttsChineseAlwaysMandarin def showVerseNumbersInRangeChanged(self): config.showVerseNumbersInRange = not config.showVerseNumbersInRange #def customPythonOnStartupChanged(self): # config.customPythonOnStartup = not config.customPythonOnStartup def openBibleWindowContentOnNextTabChanged(self): config.openBibleWindowContentOnNextTab = not config.openBibleWindowContentOnNextTab self.newTabException = False def showControlPanelOnStartupChanged(self): config.showControlPanelOnStartup = not config.showControlPanelOnStartup self.displayMessage(config.thisTranslation["message_restart"]) def preferControlPanelForCommandLineEntryChanged(self): config.preferControlPanelForCommandLineEntry = not config.preferControlPanelForCommandLineEntry self.displayMessage(config.thisTranslation["message_restart"]) def closeControlPanelAfterRunningCommandChanged(self): config.closeControlPanelAfterRunningCommand = not config.closeControlPanelAfterRunningCommand def restrictControlPanelWidthChanged(self): config.restrictControlPanelWidth = not config.restrictControlPanelWidth self.parent.reloadControlPanel(False) def regexCaseSensitiveChanged(self): config.regexCaseSensitive = not config.regexCaseSensitive def openStudyWindowContentOnNextTabChanged(self): config.openStudyWindowContentOnNextTab = not config.openStudyWindowContentOnNextTab self.newTabException = False def addFavouriteToMultiRefChanged(self): config.addFavouriteToMultiRef = not config.addFavouriteToMultiRef def addOHGBiToMorphologySearchChanged(self): config.addOHGBiToMorphologySearch = not config.addOHGBiToMorphologySearch def exportEmbeddedImagesChanged(self): config.exportEmbeddedImages = not config.exportEmbeddedImages def clickToOpenImageChanged(self): config.clickToOpenImage = not config.clickToOpenImage def openBibleNoteAfterEditorClosedChanged(self): config.openBibleNoteAfterEditorClosed = not config.openBibleNoteAfterEditorClosed def preferHtmlMenuChanged(self): config.preferHtmlMenu = not config.preferHtmlMenu def hideNoteEditorStyleToolbarChanged(self): config.hideNoteEditorStyleToolbar = not config.hideNoteEditorStyleToolbar def hideNoteEditorTextUtilityChanged(self): config.hideNoteEditorTextUtility = not config.hideNoteEditorTextUtility def populateTabsOnStartupChanged(self): config.populateTabsOnStartup = not config.populateTabsOnStartup def openBookInNewWindowChanged(self): config.openBookInNewWindow = not config.openBookInNewWindow def convertChapterVerseDotSeparatorChanged(self): config.convertChapterVerseDotSeparator = not config.convertChapterVerseDotSeparator def updateWithGitPullChanged(self): config.updateWithGitPull = not config.updateWithGitPull if config.updateWithGitPull and not os.path.isdir(".git"): config.updateWithGitPull = False def parseBookChapterWithoutSpaceChanged(self): config.parseBookChapterWithoutSpace = not config.parseBookChapterWithoutSpace def parseBooklessReferencesChanged(self): config.parseBooklessReferences = not config.parseBooklessReferences def openPdfViewerInNewWindowChanged(self): config.openPdfViewerInNewWindow = not config.openPdfViewerInNewWindow def searchBibleIfCommandNotFoundChanged(self): config.searchBibleIfCommandNotFound = not config.searchBibleIfCommandNotFound def regexSearchBibleIfCommandNotFoundChanged(self): config.regexSearchBibleIfCommandNotFound = not config.regexSearchBibleIfCommandNotFound if config.regexSearchBibleIfCommandNotFound and not config.searchBibleIfCommandNotFound: config.searchBibleIfCommandNotFound = True def overwriteNoteFontChanged(self): config.overwriteNoteFont = not config.overwriteNoteFont def overwriteNoteFontSizeChanged(self): config.overwriteNoteFontSize = not config.overwriteNoteFontSize def overwriteBookFontChanged(self): config.overwriteBookFont = not config.overwriteBookFont def useWebbrowserChanged(self): config.useWebbrowser = not config.useWebbrowser def removeHighlightOnExitChanged(self): config.removeHighlightOnExit = not config.removeHighlightOnExit def overwriteBookFontSizeChanged(self): config.overwriteBookFontSize = not config.overwriteBookFontSize def alwaysDisplayStaticMapsChanged(self): config.alwaysDisplayStaticMaps = not config.alwaysDisplayStaticMaps def openBibleNoteAfterSaveChanged(self): config.openBibleNoteAfterSave = not config.openBibleNoteAfterSave def addBreakAfterTheFirstToolBarChanged(self): config.addBreakAfterTheFirstToolBar = not config.addBreakAfterTheFirstToolBar self.displayMessage(config.thisTranslation["message_restart"]) def addBreakBeforeTheLastToolBarChanged(self): config.addBreakBeforeTheLastToolBar = not config.addBreakBeforeTheLastToolBar self.displayMessage(config.thisTranslation["message_restart"]) def disableModulesUpdateCheckChanged(self): config.disableModulesUpdateCheck = not config.disableModulesUpdateCheck def forceGenerateHtmlChanged(self): config.forceGenerateHtml = not config.forceGenerateHtml def parserStandarisationChanged(self): if config.parserStandarisation == "YES": config.parserStandarisation = "NO" else: config.parserStandarisation = "YES" def linuxStartFullScreenChanged(self): config.linuxStartFullScreen = not config.linuxStartFullScreen self.displayMessage(config.thisTranslation["message_restart"]) def espeakChanged(self): config.espeak = not config.espeak self.displayMessage(config.thisTranslation["message_restart"]) def enableLoggingChanged(self): config.enableLogging = not config.enableLogging self.displayMessage(config.thisTranslation["message_restart"]) def logCommandsChanged(self): config.logCommands = not config.logCommands def enableVerseHighlightingChanged(self): config.enableVerseHighlighting = not config.enableVerseHighlighting self.displayMessage(config.thisTranslation["message_restart"]) def useLiteVerseParsingChanged(self): config.useLiteVerseParsing = not config.useLiteVerseParsing def parseEnglishBooksOnlyChanged(self): config.parseEnglishBooksOnly = not config.parseEnglishBooksOnly def enableMacrosChanged(self): config.enableMacros = not config.enableMacros self.displayMessage(config.thisTranslation["message_restart"]) def enablePluginsChanged(self): config.enablePlugins = not config.enablePlugins self.parent.setMenuLayout(config.menuLayout) def clearCommandEntryChanged(self): config.clearCommandEntry = not config.clearCommandEntry def qtMaterialChanged(self): if not config.qtMaterial: self.parent.enableQtMaterial(True) else: self.parent.enableQtMaterial(False) def enableGistChanged(self): if not config.enableGist and config.isPygithubInstalled: config.enableGist = True self.displayMessage(config.thisTranslation["message_restart"]) elif config.enableGist: config.enableGist = not config.enableGist self.displayMessage(config.thisTranslation["message_restart"]) else: self.displayMessage(config.thisTranslation["message_noSupport"]) def hideBlankVerseCompareChanged(self): config.hideBlankVerseCompare = not config.hideBlankVerseCompare def enableMenuUnderlineChanged(self): config.enableMenuUnderline = not config.enableMenuUnderline if config.enableMenuUnderline: config.menuUnderline = "&" else: config.menuUnderline = "" self.parent.setMenuLayout(config.menuLayout) def includeStrictDocTypeInNoteChanged(self): config.includeStrictDocTypeInNote = not config.includeStrictDocTypeInNote def parseTextConvertNotesToBookChanged(self): config.parseTextConvertNotesToBook = not config.parseTextConvertNotesToBook def parseTextConvertHTMLToBookChanged(self): config.parseTextConvertHTMLToBook = not config.parseTextConvertHTMLToBook def displayCmdOutputChanged(self): config.displayCmdOutput = not config.displayCmdOutput def disableLoadLastOpenFilesOnStartupChanged(self): config.disableLoadLastOpenFilesOnStartup = not config.disableLoadLastOpenFilesOnStartup def disableOpenPopupWindowOnStartupChanged(self): config.disableOpenPopupWindowOnStartup = not config.disableOpenPopupWindowOnStartup def showMiniKeyboardInMiniControlChanged(self): config.showMiniKeyboardInMiniControl = not config.showMiniKeyboardInMiniControl
class SpecimenPositionListWidget(ParameterWidget): class _SpecimenPositionModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self.positions = [] def rowCount(self, *args, **kwargs): return len(self.positions) def columnCount(self, *args, **kwargs): return 5 def data(self, index, role): if not index.isValid() or not (0 <= index.row() < len(self.positions)): return None if role != Qt.DisplayRole: return None position = self.positions[index.row()] column = index.column() if column == 0: return str(position.x) if position.x is not None else '' elif column == 1: return str(position.y) if position.y is not None else '' elif column == 2: return str(position.z) if position.z is not None else '' elif column == 3: return str(position.r) if position.r is not None else '' elif column == 4: return str(position.t) if position.t is not None else '' def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'X' elif section == 1: return 'Y' elif section == 2: return 'Z' elif section == 3: return 'R' elif section == 4: return 'T' elif orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self.positions)): return False position = self.positions[index.row()] column = index.column() if column == 0: position.x = value elif column == 1: position.y = value elif column == 2: position.z = value elif column == 3: position.r = value elif column == 4: position.t = value return True def insertRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginInsertRows(parent, row, row + count - 1) for i in range(count): self.positions.insert(row + i, SpecimenPosition()) self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginRemoveRows(parent, row, row + count - 1) self.positions = self.positions[:row] + self.positions[row + count:] self.endRemoveRows() return True class _SpecimenPositionDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): column = index.column() if column == 0: return NumericalAttributeLineEdit(SpecimenPosition.x, parent) elif column == 1: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) elif column == 2: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) elif column == 3: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) elif column == 4: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) else: return QItemDelegate.createEditor(self, parent, option, index) def setEditorData(self, editor, index): text = index.model().data(index, Qt.DisplayRole) column = index.column() if column == 0: editor.setText(text) elif column == 1: editor.setText(text) elif column == 2: editor.setText(text) elif column == 3: editor.setText(text) elif column == 4: editor.setText(text) else: QItemDelegate.setEditorData(self, editor, index) def setModelData(self, editor, model, index): column = index.column() if column == 0: model.setData(index, editor.text()) elif column == 1: model.setData(index, editor.text()) elif column == 2: model.setData(index, editor.text()) elif column == 3: model.setData(index, editor.text()) elif column == 4: model.setData(index, editor.text()) else: return QItemDelegate.setModelData(self, editor, model, index) def __init__(self, parent=None): ParameterWidget.__init__(self, object, parent) def _init_ui(self): # Widgets self._table = QTableView() self._table.setModel(self._SpecimenPositionModel()) self._table.setItemDelegate(self._SpecimenPositionDelegate(self)) self._table.horizontalHeader().setStretchLastSection(True) self._toolbar = QToolBar() action_add = self._toolbar.addAction(getIcon("list-add"), "Add layer") action_remove = self._toolbar.addAction(getIcon("list-remove"), "Remove layer") # Layouts layout = ParameterWidget._init_ui(self) layout.addRow(self._table) layout.addRow(self._toolbar) # Signals action_add.triggered.connect(self._on_add) action_remove.triggered.connect(self._on_remove) return layout def _on_add(self): index = self._table.selectionModel().currentIndex() model = self._table.model() model.insertRows(index.row() + 1) def _on_remove(self): selection = self._table.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Specimen position", "Select a position") return model = self._table.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.removeRow(row) def parameter(self): positions = [] for position in self._table.model().positions: positions.append(SpecimenPosition(position.x, position.y, position.z, position.r, position.t)) return positions def setParameter(self, positions): model = self._table.model() model.positions = positions model.reset() def positions(self): return self.parameter() def setPositions(self, positions): self.setParameter(positions) def setReadOnly(self, state): ParameterWidget.setReadOnly(self, state) if state: trigger = QTableView.EditTrigger.NoEditTriggers else: trigger = QTableView.EditTrigger.AllEditTriggers self._table.setEditTriggers(trigger) self._toolbar.setEnabled(not state) def isReadOnly(self): return ParameterWidget.isReadOnly(self) and \ self._table.editTriggers() == QTableView.EditTrigger.NoEditTriggers and \ not self._toolbar.isEnabled()
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 MxDataWidget(QWidget): """ Dialog for displaying and editing DataFrame and related objects. Based on the gtabview project (ExtTableView). For more information please see: https://github.com/wavexx/gtabview/blob/master/gtabview/viewer.py Signals ------- sig_option_changed(str, object): Raised if an option is changed. Arguments are name of option and its new value. """ sig_option_changed = Signal(str, object) def __init__(self, parent=None, data=DataFrame()): QWidget.__init__(self, parent) # Destroying the C++ object right after closing the dialog box, # otherwise it may be garbage-collected in another QThread # (e.g. the editor's analysis thread in Spyder), thus leading to # a segmentation fault on UNIX or an application crash on Windows self.setAttribute(Qt.WA_DeleteOnClose) self.is_series = False self.layout = None self.setup_and_check(data) def setup_and_check(self, data, title=''): """ Setup DataFrameEditor: return False if data is not supported, True otherwise. Supported types for data are DataFrame, Series and Index. """ self._selection_rec = False self._model = None self.layout = QGridLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.setWindowIcon(ima.icon('arredit')) if title: title = to_text_string(title) + " - %s" % data.__class__.__name__ else: title = _("%s editor") % data.__class__.__name__ if isinstance(data, Series): self.is_series = True data = data.to_frame() elif isinstance(data, Index): data = DataFrame(data) self.setWindowTitle(title) # self.resize(600, 500) self.hscroll = QScrollBar(Qt.Horizontal) self.vscroll = QScrollBar(Qt.Vertical) # Create the view for the level self.create_table_level() # Create the view for the horizontal header self.create_table_header() # Create the view for the vertical index self.create_table_index() # Create the model and view of the data self.dataModel = MxDataModel(data, parent=self) # self.dataModel.dataChanged.connect(self.save_and_close_enable) self.create_data_table() self.layout.addWidget(self.hscroll, 2, 0, 1, 2) self.layout.addWidget(self.vscroll, 0, 2, 2, 1) # autosize columns on-demand self._autosized_cols = set() self._max_autosize_ms = None self.dataTable.installEventFilter(self) avg_width = self.fontMetrics().averageCharWidth() self.min_trunc = avg_width * 8 # Minimum size for columns self.max_width = avg_width * 64 # Maximum size for columns self.setLayout(self.layout) # Make the dialog act as a window # self.setWindowFlags(Qt.Window) self.setModel(self.dataModel) self.resizeColumnsToContents() return True def create_table_level(self): """Create the QTableView that will hold the level model.""" self.table_level = QTableView() self.table_level.setEditTriggers(QTableWidget.NoEditTriggers) self.table_level.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_level.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_level.setFrameStyle(QFrame.Plain) self.table_level.horizontalHeader().sectionResized.connect( self._index_resized) self.table_level.verticalHeader().sectionResized.connect( self._header_resized) # self.table_level.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.table_level, 0, 0) self.table_level.setContentsMargins(0, 0, 0, 0) self.table_level.horizontalHeader().sectionClicked.connect( self.sortByIndex) def create_table_header(self): """Create the QTableView that will hold the header model.""" self.table_header = QTableView() self.table_header.verticalHeader().hide() self.table_header.setEditTriggers(QTableWidget.NoEditTriggers) self.table_header.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_header.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_header.setHorizontalScrollMode(QTableView.ScrollPerPixel) self.table_header.setHorizontalScrollBar(self.hscroll) self.table_header.setFrameStyle(QFrame.Plain) self.table_header.horizontalHeader().sectionResized.connect( self._column_resized) # self.table_header.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.table_header, 0, 1) def create_table_index(self): """Create the QTableView that will hold the index model.""" self.table_index = QTableView() self.table_index.horizontalHeader().hide() self.table_index.setEditTriggers(QTableWidget.NoEditTriggers) self.table_index.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_index.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.table_index.setVerticalScrollMode(QTableView.ScrollPerPixel) self.table_index.setVerticalScrollBar(self.vscroll) self.table_index.setFrameStyle(QFrame.Plain) self.table_index.verticalHeader().sectionResized.connect( self._row_resized) # self.table_index.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.table_index, 1, 0) self.table_index.setContentsMargins(0, 0, 0, 0) def create_data_table(self): """Create the QTableView that will hold the data model.""" self.dataTable = MxDataTable(self, self.dataModel, self.table_header.horizontalHeader(), self.hscroll, self.vscroll) self.dataTable.verticalHeader().hide() self.dataTable.horizontalHeader().hide() self.dataTable.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.dataTable.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.dataTable.setHorizontalScrollMode(QTableView.ScrollPerPixel) self.dataTable.setVerticalScrollMode(QTableView.ScrollPerPixel) self.dataTable.setFrameStyle(QFrame.Plain) # self.dataTable.setItemDelegate(QItemDelegate()) self.layout.addWidget(self.dataTable, 1, 1) self.setFocusProxy(self.dataTable) self.dataTable.sig_sort_by_column.connect(self._sort_update) self.dataTable.sig_fetch_more_columns.connect(self._fetch_more_columns) self.dataTable.sig_fetch_more_rows.connect(self._fetch_more_rows) def sortByIndex(self, index): """Implement a Index sort.""" self.table_level.horizontalHeader().setSortIndicatorShown(True) sort_order = self.table_level.horizontalHeader().sortIndicatorOrder() self.table_index.model().sort(index, sort_order) self._sort_update() def model(self): """Get the model of the dataframe.""" return self._model def _column_resized(self, col, old_width, new_width): """Update the column width.""" self.dataTable.setColumnWidth(col, new_width) self._update_layout() def _row_resized(self, row, old_height, new_height): """Update the row height.""" self.dataTable.setRowHeight(row, new_height) self._update_layout() def _index_resized(self, col, old_width, new_width): """Resize the corresponding column of the index section selected.""" self.table_index.setColumnWidth(col, new_width) self._update_layout() def _header_resized(self, row, old_height, new_height): """Resize the corresponding row of the header section selected.""" self.table_header.setRowHeight(row, new_height) self._update_layout() def _update_layout(self): """Set the width and height of the QTableViews and hide rows.""" h_width = max(self.table_level.verticalHeader().sizeHint().width(), self.table_index.verticalHeader().sizeHint().width()) self.table_level.verticalHeader().setFixedWidth(h_width) self.table_index.verticalHeader().setFixedWidth(h_width) last_row = self._model.header_shape[0] - 1 if last_row < 0: hdr_height = self.table_level.horizontalHeader().height() else: # Check if the header shape has only one row (which display the # same info than the horizontal header). if last_row == 0: self.table_level.setRowHidden(0, True) self.table_header.setRowHidden(0, True) else: self.table_level.setRowHidden(0, False) self.table_header.setRowHidden(0, False) hdr_height = self.table_level.rowViewportPosition(last_row) + \ self.table_level.rowHeight(last_row) + \ self.table_level.horizontalHeader().height() self.table_header.setFixedHeight(hdr_height) self.table_level.setFixedHeight(hdr_height) last_col = self._model.header_shape[1] - 1 if last_col < 0: idx_width = self.table_level.verticalHeader().width() else: idx_width = self.table_level.columnViewportPosition(last_col) + \ self.table_level.columnWidth(last_col) + \ self.table_level.verticalHeader().width() self.table_index.setFixedWidth(idx_width) self.table_level.setFixedWidth(idx_width) self._resizeVisibleColumnsToContents() def _reset_model(self, table, model): """Set the model in the given table.""" old_sel_model = table.selectionModel() table.setModel(model) if old_sel_model: del old_sel_model def setAutosizeLimit(self, limit_ms): """Set maximum size for columns.""" self._max_autosize_ms = limit_ms def setModel(self, model, relayout=True): """Set the model for the data, header/index and level views.""" self._model = model # sel_model = self.dataTable.selectionModel() # sel_model.currentColumnChanged.connect( # self._resizeCurrentColumnToContents) self._reset_model(self.dataTable, model) # Asociate the models (level, vertical index and horizontal header) # with its corresponding view. self._reset_model( self.table_level, DataFrameLevelModel(model, self.palette(), self.font())) self._reset_model(self.table_header, DataFrameHeaderModel(model, 0, self.palette())) self._reset_model(self.table_index, DataFrameHeaderModel(model, 1, self.palette())) # Needs to be called after setting all table models if relayout: self._update_layout() def setCurrentIndex(self, y, x): """Set current selection.""" self.dataTable.selectionModel().setCurrentIndex( self.dataTable.model().index(y, x), QItemSelectionModel.ClearAndSelect) def _sizeHintForColumn(self, table, col, limit_ms=None): """Get the size hint for a given column in a table.""" max_row = table.model().rowCount() lm_start = time.perf_counter() lm_row = 64 if limit_ms else max_row max_width = 0 for row in range(max_row): v = table.sizeHintForIndex(table.model().index(row, col)) max_width = max(max_width, v.width()) if row > lm_row: lm_now = time.perf_counter() lm_elapsed = (lm_now - lm_start) * 1000 if lm_elapsed >= limit_ms: break lm_row = int((row / lm_elapsed) * limit_ms) return max_width def _resizeColumnToContents(self, header, data, col, limit_ms): """Resize a column by its contents.""" hdr_width = self._sizeHintForColumn(header, col, limit_ms) data_width = self._sizeHintForColumn(data, col, limit_ms) if data_width > hdr_width: width = min(self.max_width, data_width) elif hdr_width > data_width * 2: width = max(min(hdr_width, self.min_trunc), min(self.max_width, data_width)) else: width = min(self.max_width, hdr_width) header.setColumnWidth(col, width) def _resizeColumnsToContents(self, header, data, limit_ms): """Resize all the colummns to its contents.""" max_col = data.model().columnCount() if limit_ms is None: max_col_ms = None else: max_col_ms = limit_ms / max(1, max_col) for col in range(max_col): self._resizeColumnToContents(header, data, col, max_col_ms) def eventFilter(self, obj, event): """Override eventFilter to catch resize event.""" if obj == self.dataTable and event.type() == QEvent.Resize: self._resizeVisibleColumnsToContents() return False def _resizeVisibleColumnsToContents(self): """Resize the columns that are in the view.""" index_column = self.dataTable.rect().topLeft().x() start = col = self.dataTable.columnAt(index_column) width = self._model.shape[1] end = self.dataTable.columnAt(self.dataTable.rect().bottomRight().x()) end = width if end == -1 else end + 1 if self._max_autosize_ms is None: max_col_ms = None else: max_col_ms = self._max_autosize_ms / max(1, end - start) while col < end: resized = False if col not in self._autosized_cols: self._autosized_cols.add(col) resized = True self._resizeColumnToContents(self.table_header, self.dataTable, col, max_col_ms) col += 1 if resized: # As we resize columns, the boundary will change index_column = self.dataTable.rect().bottomRight().x() end = self.dataTable.columnAt(index_column) end = width if end == -1 else end + 1 if max_col_ms is not None: max_col_ms = self._max_autosize_ms / max(1, end - start) def _resizeCurrentColumnToContents(self, new_index, old_index): """Resize the current column to its contents.""" if new_index.column() not in self._autosized_cols: # Ensure the requested column is fully into view after resizing self._resizeVisibleColumnsToContents() self.dataTable.scrollTo(new_index) def resizeColumnsToContents(self): """Resize the columns to its contents.""" self._autosized_cols = set() self._resizeColumnsToContents(self.table_level, self.table_index, self._max_autosize_ms) self._update_layout() self.table_level.resizeColumnsToContents() def change_format(self): """ Ask user for display format for floats and use it. This function also checks whether the format is valid and emits `sig_option_changed`. """ format, valid = QInputDialog.getText(self, _('Format'), _("Float formatting"), QLineEdit.Normal, self.dataModel.get_format()) if valid: format = str(format) try: format % 1.1 except: msg = _("Format ({}) is incorrect").format(format) QMessageBox.critical(self, _("Error"), msg) return if not format.startswith('%'): msg = _("Format ({}) should start with '%'").format(format) QMessageBox.critical(self, _("Error"), msg) return self.dataModel.set_format(format) self.sig_option_changed.emit('dataframe_format', format) def get_value(self): """Return modified Dataframe -- this is *not* a copy""" # It is import to avoid accessing Qt C++ object as it has probably # already been destroyed, due to the Qt.WA_DeleteOnClose attribute df = self.dataModel.get_data() if self.is_series: return df.iloc[:, 0] else: return df def _update_header_size(self): """Update the column width of the header.""" column_count = self.table_header.model().columnCount() for index in range(0, column_count): if index < column_count: column_width = self.dataTable.columnWidth(index) self.table_header.setColumnWidth(index, column_width) else: break def _sort_update(self): """ Update the model for all the QTableView objects. Uses the model of the dataTable as the base. """ self.setModel(self.dataTable.model()) def _fetch_more_columns(self): """Fetch more data for the header (columns).""" self.table_header.model().fetch_more() def _fetch_more_rows(self): """Fetch more data for the index (rows).""" self.table_index.model().fetch_more() def resize_to_contents(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.dataTable.resizeColumnsToContents() self.dataModel.fetch_more(columns=True) self.dataTable.resizeColumnsToContents() self._update_header_size() QApplication.restoreOverrideCursor() # --- mx specific --- def process_remote_view(self, data): if data is None: data = DataFrame() # Empty DataFrame self.setModel(MxDataModel(data, parent=self))
class DownloadBibleMp3Dialog(QDialog): def __init__(self, parent): super().__init__() self.bibles = { "BBE (British accent)": ("BBE", "otseng/UniqueBible_MP3_BBE_british", "british"), "KJV (American accent)": ("KJV", "otseng/UniqueBible_MP3_KJV", "default"), "KJV (American soft music)": ("KJV", "otseng/UniqueBible_MP3_KJV_soft_music", "soft-music"), "NHEB (Indian accent)": ("NHEB", "otseng/UniqueBible_MP3_NHEB_indian", "indian"), "WEB (American accent)": ("WEB", "otseng/UniqueBible_MP3_WEB", "default"), "CUV (Chinese)": ("CUV", "otseng/UniqueBible_MP3_CUV", "default"), "HHBD (Hindi)": ("HHBD", "otseng/UniqueBible_MP3_HHBD", "default"), "RVA (Spanish)": ("RVA", "otseng/UniqueBible_MP3_RVA", "default"), "TR (Modern Greek)": ("TR", "otseng/UniqueBible_MP3_TR", "modern"), } self.parent = parent self.setWindowTitle(config.thisTranslation["gitHubBibleMp3Files"]) self.setMinimumSize(150, 450) self.selectedRendition = None self.selectedText = None self.selectedRepo = None self.selectedDirectory = None self.settingBibles = False self.thread = None self.setupUI() def setupUI(self): mainLayout = QVBoxLayout() title = QLabel(config.thisTranslation["gitHubBibleMp3Files"]) mainLayout.addWidget(title) self.versionsLayout = QVBoxLayout() self.renditionsList = QListWidget() self.renditionsList.itemClicked.connect(self.selectItem) for rendition in self.bibles.keys(): self.renditionsList.addItem(rendition) self.renditionsList.setMaximumHeight(100) self.versionsLayout.addWidget(self.renditionsList) mainLayout.addLayout(self.versionsLayout) self.downloadTable = QTableView() self.downloadTable.setEnabled(False) self.downloadTable.setFocusPolicy(Qt.StrongFocus) self.downloadTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.downloadTable.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.downloadTable) self.downloadTable.setModel(self.dataViewModel) mainLayout.addWidget(self.downloadTable) buttonsLayout = QHBoxLayout() selectAllButton = QPushButton(config.thisTranslation["selectAll"]) selectAllButton.setFocusPolicy(Qt.StrongFocus) selectAllButton.clicked.connect(self.selectAll) buttonsLayout.addWidget(selectAllButton) selectNoneButton = QPushButton(config.thisTranslation["selectNone"]) selectNoneButton.setFocusPolicy(Qt.StrongFocus) selectNoneButton.clicked.connect(self.selectNone) buttonsLayout.addWidget(selectNoneButton) otButton = QPushButton("1-39") otButton.setFocusPolicy(Qt.StrongFocus) otButton.clicked.connect(self.selectOT) buttonsLayout.addWidget(otButton) ntButton = QPushButton("40-66") ntButton.setFocusPolicy(Qt.StrongFocus) ntButton.clicked.connect(self.selectNT) buttonsLayout.addWidget(ntButton) # buttonsLayout.addStretch() mainLayout.addLayout(buttonsLayout) self.downloadButton = QPushButton(config.thisTranslation["download"]) self.downloadButton.setFocusPolicy(Qt.StrongFocus) self.downloadButton.setAutoDefault(True) self.downloadButton.setFocus() self.downloadButton.clicked.connect(self.download) mainLayout.addWidget(self.downloadButton) self.status = QLabel("") mainLayout.addWidget(self.status) buttonLayout = QHBoxLayout() self.closeButton = QPushButton(config.thisTranslation["close"]) self.closeButton.setFocusPolicy(Qt.StrongFocus) self.closeButton.clicked.connect(self.closeDialog) buttonLayout.addWidget(self.closeButton) mainLayout.addLayout(buttonLayout) self.setLayout(mainLayout) self.renditionsList.item(0).setSelected(True) bible = self.renditionsList.item(0).text() self.selectRendition(bible) self.downloadButton.setDefault(True) QTimer.singleShot(0, self.downloadButton.setFocus) def selectItem(self, item): self.selectRendition(item.text()) def selectRendition(self, rendition): from util.GithubUtil import GithubUtil self.selectedRendition = rendition self.downloadTable.setEnabled(True) self.selectedText, self.selectedRepo, self.selectedDirectory = self.bibles[ self.selectedRendition] self.github = GithubUtil(self.selectedRepo) self.repoData = self.github.getRepoData() self.settingBibles = True self.dataViewModel.clear() rowCount = 0 for file in self.repoData.keys(): if len(str(file)) > 3: engFullBookName = file[3:] else: engFullBookName = BibleBooks().eng[str(int(file))][1] item = QStandardItem(file[:3].strip()) folder = os.path.join("audio", "bibles", self.selectedText, self.selectedDirectory, file) folderWithName = os.path.join("audio", "bibles", self.selectedText, self.selectedDirectory, file + " " + engFullBookName) if os.path.exists(folder) or os.path.exists(folderWithName): item.setCheckable(False) item.setCheckState(Qt.Unchecked) item.setEnabled(False) else: item.setCheckable(True) item.setCheckState(Qt.Checked) item.setEnabled(True) self.dataViewModel.setItem(rowCount, 0, item) item = QStandardItem(engFullBookName) self.dataViewModel.setItem(rowCount, 1, item) if os.path.exists(folder) or os.path.exists(folderWithName): item = QStandardItem("Installed") self.dataViewModel.setItem(rowCount, 2, item) else: item = QStandardItem("") self.dataViewModel.setItem(rowCount, 2, item) rowCount += 1 self.dataViewModel.setHorizontalHeaderLabels([ config.thisTranslation["menu_book"], config.thisTranslation["name"], "" ]) self.downloadTable.setColumnWidth(0, 90) self.downloadTable.setColumnWidth(1, 125) self.downloadTable.setColumnWidth(2, 125) # self.downloadTable.resizeColumnsToContents() self.settingBibles = False def selectAll(self): for index in range(self.dataViewModel.rowCount()): item = self.dataViewModel.item(index) if item.isEnabled(): item.setCheckState(Qt.Checked) def selectNone(self): for index in range(self.dataViewModel.rowCount()): item = self.dataViewModel.item(index) item.setCheckState(Qt.Unchecked) def selectOT(self): for index in range(self.dataViewModel.rowCount()): item = self.dataViewModel.item(index) bookNum = int(item.text()) if bookNum <= 39: if item.isEnabled(): item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Unchecked) def selectNT(self): for index in range(self.dataViewModel.rowCount()): item = self.dataViewModel.item(index) bookNum = int(item.text()) if bookNum >= 40: if item.isEnabled(): item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Unchecked) def download(self): self.downloadButton.setEnabled(False) self.setStatus(config.thisTranslation["message_installing"]) self.closeButton.setEnabled(False) folder = os.path.join("audio", "bibles") if not os.path.exists(folder): os.mkdir(folder) folder = os.path.join("audio", "bibles", self.selectedText) if not os.path.exists(folder): os.mkdir(folder) folder = os.path.join("audio", "bibles", self.selectedText, self.selectedDirectory) if not os.path.exists(folder): os.mkdir(folder) self.thread = QThread() self.worker = DownloadFromGitHub(self.github, self.repoData, self.dataViewModel, self.selectedText, self.selectedDirectory) self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.run) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.worker.deleteLater) self.thread.finished.connect(self.worker.deleteLater) self.worker.finished.connect(self.finishedDownloading) self.worker.progress.connect(self.setStatus) self.thread.start() def finishedDownloading(self, count): self.selectRendition(self.selectedRendition) self.setStatus("") self.downloadButton.setEnabled(True) self.closeButton.setEnabled(True) if count > 0: self.parent.displayMessage( config.thisTranslation["message_installed"]) def setStatus(self, message): self.status.setText(message) QApplication.processEvents() def closeDialog(self): if self.thread: if self.thread.isRunning(): self.thread.quit() self.close()
class BasePlotCurveEditorDialog(QDialog): """QDialog that is used in Qt Designer to edit the properties of the curves in a waveform plot. This dialog is shown when you double-click the plot, or when you right click it and choose 'edit curves'. This thing is mostly just a wrapper for a table view, with a couple buttons to add and remove curves, and a button to save the changes.""" TABLE_MODEL_CLASS = BasePlotCurvesModel def __init__(self, plot, parent=None): super(BasePlotCurveEditorDialog, self).__init__(parent) self.plot = plot self.setup_ui() self.table_model = self.TABLE_MODEL_CLASS(self.plot) self.table_view.setModel(self.table_model) self.table_model.plot = plot # self.table_view.resizeColumnsToContents() self.add_button.clicked.connect(self.addCurve) self.remove_button.clicked.connect(self.removeSelectedCurve) self.remove_button.setEnabled(False) self.table_view.selectionModel().selectionChanged.connect( self.handleSelectionChange) self.table_view.doubleClicked.connect(self.handleDoubleClick) self.resize(800, 300) def setup_ui(self): self.vertical_layout = QVBoxLayout(self) self.table_view = QTableView(self) self.table_view.setEditTriggers(QAbstractItemView.DoubleClicked) self.table_view.setProperty("showDropIndicator", False) self.table_view.setDragDropOverwriteMode(False) self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.table_view.setSortingEnabled(False) self.table_view.horizontalHeader().setStretchLastSection(True) self.table_view.verticalHeader().setVisible(False) self.table_view.setColumnWidth(0, 160) self.table_view.setColumnWidth(1, 160) self.table_view.setColumnWidth(2, 160) self.vertical_layout.addWidget(self.table_view) self.add_remove_layout = QHBoxLayout() spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.add_remove_layout.addItem(spacer) self.add_button = QPushButton("Add Curve", self) self.add_remove_layout.addWidget(self.add_button) self.remove_button = QPushButton("Remove Curve", self) self.add_remove_layout.addWidget(self.remove_button) self.vertical_layout.addLayout(self.add_remove_layout) self.button_box = QDialogButtonBox(self) self.button_box.setOrientation(Qt.Horizontal) self.button_box.addButton("Done", QDialogButtonBox.AcceptRole) self.vertical_layout.addWidget(self.button_box) self.button_box.accepted.connect(self.saveChanges) self.button_box.rejected.connect(self.reject) self.setWindowTitle("Waveform Curve Editor") def setup_delegate_columns(self, index=2): symbol_delegate = SymbolColumnDelegate(self) self.table_view.setItemDelegateForColumn(index+3, symbol_delegate) line_delegate = LineColumnDelegate(self) self.table_view.setItemDelegateForColumn(index+1, line_delegate) color_delegate = ColorColumnDelegate(self) self.table_view.setItemDelegateForColumn(index, color_delegate) @Slot() def addCurve(self): self.table_model.append() @Slot() def removeSelectedCurve(self): self.table_model.removeAtIndex(self.table_view.currentIndex()) @Slot(QItemSelection, QItemSelection) def handleSelectionChange(self, selected, deselected): self.remove_button.setEnabled( self.table_view.selectionModel().hasSelection()) @Slot(QModelIndex) def handleDoubleClick(self, index): if self.table_model.needsColorDialog(index): # The table model returns a QBrush for BackgroundRole, not a QColor init_color = self.table_model.data(index, Qt.BackgroundRole).color() color = QColorDialog.getColor(init_color, self) if color.isValid(): self.table_model.setData(index, color, role=Qt.EditRole) @Slot() def saveChanges(self): formWindow = QDesignerFormWindowInterface.findFormWindow(self.plot) if formWindow: formWindow.cursor().setProperty("curves", self.plot.curves) self.accept()
class LiveFilterDialog(QDialog): JS_HIDE = """ count = 0; searchResultCount = document.getElementById("searchResultCount"); divs = document.querySelectorAll("div"); for (var i = 0, len = divs.length; i < len; i++) {{ div = divs[i]; div.hidden = {0}; count++; }}; if (searchResultCount) {{ searchResultCount.innerHTML = count; }} """ JS_SHOW = """ wordSets = [{0}]; count = 0; searchResultCount = document.getElementById("searchResultCount"); divs = document.querySelectorAll("div"); for (var i=0, len=divs.length; i < len; i++) {{ div = divs[i]; var found = true; for (var j=0, len2=wordSets.length; j < len2; j++) {{ wordSet = wordSets[j]; var regex; if (wordSet.startsWith("'")) {{ wordSet = wordSet.replace("'", ""); wordSet = wordSet.replace("'", ""); regex = new RegExp(wordSet); }} else {{ regex = new RegExp(wordSet, "i"); }} found &= regex.test(div.innerHTML); }} if (found) {{ div.hidden = false; count++; }} }}; if (searchResultCount) {{ searchResultCount.innerHTML = count; }} """ def __init__(self, parent): super().__init__() self.parent = parent self.setWindowTitle(config.thisTranslation["liveFilter"]) self.setMinimumSize(400, 400) self.selectedFilter = None self.selectedPattern = None self.settingBibles = False self.db = LiveFilterSqlite() self.filters = None self.saveReadFormattedBibles = config.readFormattedBibles if config.readFormattedBibles: self.parent.disableBiblesInParagraphs() self.setupUI() def setupUI(self): mainLayout = QVBoxLayout() title = QLabel(config.thisTranslation["liveFilter"]) mainLayout.addWidget(title) self.filtersTable = QTableView() self.filtersTable.setEnabled(True) self.filtersTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.filtersTable.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.filtersTable) self.filtersTable.setModel(self.dataViewModel) self.dataViewModel.itemChanged.connect(self.filterSelectionChanged) self.selectionModel = self.filtersTable.selectionModel() self.selectionModel.selectionChanged.connect(self.handleSelection) mainLayout.addWidget(self.filtersTable) self.reloadFilters() buttonsLayout = QHBoxLayout() addButton = QPushButton(config.thisTranslation["add"]) addButton.clicked.connect(self.addNewFilter) buttonsLayout.addWidget(addButton) removeButton = QPushButton(config.thisTranslation["remove"]) removeButton.clicked.connect(self.removeFilter) buttonsLayout.addWidget(removeButton) editButton = QPushButton(config.thisTranslation["edit"]) editButton.clicked.connect(self.editFilter) buttonsLayout.addWidget(editButton) importButton = QPushButton(config.thisTranslation["import"]) importButton.clicked.connect(self.importFile) buttonsLayout.addWidget(importButton) buttonsLayout.addStretch() mainLayout.addLayout(buttonsLayout) buttons = QDialogButtonBox.Ok self.buttonBox = QDialogButtonBox(buttons) self.buttonBox.accepted.connect(self.accept) self.buttonBox.accepted.connect(self.close) self.buttonBox.rejected.connect(self.reject) mainLayout.addWidget(self.buttonBox) self.setLayout(mainLayout) def close(self): pass def reloadFilters(self): self.filters = self.db.getAll() self.dataViewModel.clear() rowCount = 0 for bible, description in self.filters: item = QStandardItem(bible) item.setToolTip(bible) item.setCheckable(True) self.dataViewModel.setItem(rowCount, 0, item) item = QStandardItem(description) self.dataViewModel.setItem(rowCount, 1, item) rowCount += 1 self.dataViewModel.setHorizontalHeaderLabels([ config.thisTranslation["filter2"], config.thisTranslation["pattern"] ]) self.filtersTable.resizeColumnsToContents() def handleSelection(self, selected, deselected): for item in selected: row = item.indexes()[0].row() filter = self.dataViewModel.item(row, 0) self.selectedFilter = filter.text() pattern = self.dataViewModel.item(row, 1) self.selectedPattern = pattern.text() def filterSelectionChanged(self, item): try: numChecked = 0 for index in range(self.dataViewModel.rowCount()): item = self.dataViewModel.item(index) if item.checkState() == Qt.Checked: numChecked += 1 if numChecked == 0: config.mainWindow.studyPage.runJavaScript( self.JS_HIDE.format("false")) else: sets = [] config.mainWindow.studyPage.runJavaScript( self.JS_HIDE.format("true")) for index in range(self.dataViewModel.rowCount()): item = self.dataViewModel.item(index) if item.checkState() == Qt.Checked: sets.append('"{0}"'.format(self.filters[index][1])) wordSets = ",".join(sets) js = self.JS_SHOW.format(wordSets) config.mainWindow.studyPage.runJavaScript(js) except Exception as e: print(str(e)) def addNewFilter(self): fields = [(config.thisTranslation["filter2"], ""), (config.thisTranslation["pattern"], "")] dialog = MultiLineInputDialog("New Filter", fields) if dialog.exec(): data = dialog.getInputs() self.db.insert(data[0], data[1]) self.reloadFilters() def removeFilter(self): reply = QMessageBox.question( self, "Delete", 'Delete {0} {1}'.format(self.selectedFilter, config.thisTranslation["filter2"]), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.db.delete(self.selectedFilter) self.reloadFilters() def editFilter(self): fields = [(config.thisTranslation["filter2"], self.selectedFilter), (config.thisTranslation["pattern"], self.selectedPattern)] dialog = MultiLineInputDialog("Edit Filter", fields) if dialog.exec(): data = dialog.getInputs() self.db.delete(self.selectedFilter) self.db.insert(data[0], data[1]) self.reloadFilters() def importFile(self): options = QFileDialog.Options() filename, filtr = QFileDialog.getOpenFileName( self, config.thisTranslation["import"], config.thisTranslation["liveFilter"], "File (*.*)", "", options) if filename: try: with open(filename, errors='ignore') as f: for line in f: data = line.split(":::") filter = data[0].strip() pattern = data[1].strip() if self.db.checkFilterExists(filter): self.db.delete(filter) self.db.insert(filter, pattern) except Exception as e: print(e) self.reloadFilters()
class MainWidget(QWidget): bot_double_clicked = Signal(Bot) task_button_clicked = Signal() payload_button_clicked = Signal() bot_clicked = Signal(int) load_finished = Signal() def __init__(self, parent): super(MainWidget, self).__init__(parent) self.main_layout = QHBoxLayout(self) self.main_layout.setSpacing(11) self.setLayout(self.main_layout) def setupUi(self, config): self.map = GoogleMapsView(self, config.get("gmaps_key")) self.map.getHandler().markerDoubleClicked.connect( lambda bot_id, lat, lng: self.bot_double_clicked.emit( self.table_model.getDeviceById(int(bot_id)))) self.map.getHandler().markerClicked.connect(self.bot_clicked.emit) self.map.setObjectName("mapWidget") self.map.enableMarkersDragging(False) self.map.loadFinished.connect(self.load_finished.emit) self.bots_table = QTableView(self) self.bots_table.doubleClicked.connect(self.on_botTable_doubleClicked) self.bots_table.setObjectName("bots_table") self.bots_table.setAutoFillBackground(True) self.bots_table.setFrameShape(QFrame.StyledPanel) self.bots_table.setFrameShadow(QFrame.Sunken) self.bots_table.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.bots_table.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.bots_table.setSizeAdjustPolicy( QAbstractScrollArea.AdjustToContents) self.bots_table.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel) self.bots_table.setHorizontalScrollMode( QAbstractItemView.ScrollPerPixel) self.bots_table.setGridStyle(Qt.SolidLine) self.bots_table.horizontalHeader().setStretchLastSection(True) self.bots_table.setContextMenuPolicy(Qt.CustomContextMenu) hheader = self.bots_table.horizontalHeader() hheader.setDefaultSectionSize(150) hheader.setMinimumSectionSize(150) hheader.setMouseTracking(True) vheader = self.bots_table.verticalHeader() vheader.setCascadingSectionResizes(True) vheader.setDefaultSectionSize(35) vheader.setSortIndicatorShown(False) vheader.setStretchLastSection(False) vheader.setVisible(False) self.bots_table.setEditTriggers(QTableWidget.NoEditTriggers) self.bots_table.setSelectionMode(QAbstractItemView.ExtendedSelection) self.bots_table.setSelectionBehavior(QAbstractItemView.SelectRows) self.table_model = BotsTableModel(self) self.table_model.setObjectName("table_model") self.table_model.removed.connect(self.map.deleteMarker) self.bots_table.setModel(self.table_model) for i in range(self.table_model.columnCount()): self.bots_table.horizontalHeader().setSectionResizeMode( i, QHeaderView.Stretch) self.splitter = QSplitter(self) self.splitter.addWidget(self.map) self.splitter.addWidget(self.bots_table) self.splitter.setOrientation(Qt.Vertical) self.main_layout.addWidget(self.splitter) sizepolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) sizepolicy.setHorizontalStretch(0) sizepolicy.setVerticalStretch(0) self.buttons_widget = QWidget(self) self.buttons_widget.setContentsMargins(0, 0, 0, 0) self.buttons_widgetLayout = QVBoxLayout(self.buttons_widget) self.buttons_widgetLayout.setContentsMargins(0, 0, 0, 0) self.buttons_widgetLayout.setAlignment(Qt.AlignTop) self.buttons_widget.setLayout(self.buttons_widgetLayout) self.payload_button = MainWindowButton(self.buttons_widget) self.payload_button.setToolTip("Create payload") self.payload_button.clicked.connect(self.payload_button_clicked.emit) self.payload_button.setIcon( QIcon( os.path.join(os.getcwd(), "resources/icons/3d-cube-sphere.svg"))) self.task_button = MainWindowButton(self.buttons_widget) self.task_button.setToolTip("Create tasks") self.task_button.clicked.connect(self.task_button_clicked.emit) self.task_button.setIcon( QIcon(os.path.join(os.getcwd(), "resources/icons/list-check.svg"))) self.disconnect_button = MainWindowButton(self.buttons_widget) self.disconnect_button.setToolTip("Kick") self.disconnect_button.setIcon( QIcon(os.path.join(os.getcwd(), "resources/icons/wifi-off.svg"))) self.terminate_button = MainWindowButton(self.buttons_widget) self.terminate_button.setToolTip("Terminate") self.terminate_button.setIcon( QIcon(os.path.join(os.getcwd(), "resources/icons/user-off.svg"))) self.close_button = MainWindowButton(self.buttons_widget) self.close_button.setToolTip("Close") self.close_button.clicked.connect(self.window().closeClicked.emit) self.close_button.setIcon( QIcon(os.path.join(os.getcwd(), "resources/icons/x.svg"))) self.buttons_widgetLayout.addWidget(self.payload_button) self.buttons_widgetLayout.addWidget(self.task_button) self.buttons_widgetLayout.addWidget(self.terminate_button) self.buttons_widgetLayout.addWidget(self.disconnect_button) self.buttons_widgetLayout.addStretch(1) self.buttons_widgetLayout.addWidget(self.close_button) self.main_layout.addWidget(self.buttons_widget) @Slot(QModelIndex) def on_botTable_doubleClicked(self, index: QModelIndex): self.bot_double_clicked.emit( self.table_model.getDeviceById( self.table_model.index(index.row(), 0).data())) def add_bot(self, bot_id, ip, port, kwargs={}): bot = Bot(None, bot_id, ip, port, **kwargs) bot.update_map.connect(lambda marker_id, loc: self.map.addMarker( marker_id, loc[0], loc[1])) self.table_model.appendDevice(bot) def update_bot(self, bot_id, kwargs): self.table_model.updateDevice(bot_id, kwargs) def remove_bot(self, bot_id): self.table_model.removeDevice(bot_id) @Slot(Log) def on_bot_log(self, log): bot = self.table_model.getDeviceById(log.device_id) if bot: bot.logs.append(log) bot.updated.emit() @Slot(int) def get_bot_by_id(self, bot_id): return self.table_model.getDeviceById(bot_id)
class CompositionElementalWidget(_CompositionWidget): class _CompositionModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self.composition = OrderedDict() def rowCount(self, *args, **kwargs): return len(self.composition) def columnCount(self, *args, **kwargs): return 2 def data(self, index, role): if not index.isValid() or \ not (0 <= index.row() < len(self.composition)): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role != Qt.DisplayRole: return None z, fraction = list(self.composition.items())[index.row()] column = index.column() if column == 0: if z is None: return 'none' else: return str(get_symbol(z)) elif column == 1: return str(fraction) def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'Element' elif section == 1: return 'Fraction' elif orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self.composition)): return False z = list(self.composition.keys())[index.row()] column = index.column() if column == 0: if value in self.composition: return False fraction = self.composition.pop(z) self.composition[value] = fraction elif column == 1: self.composition[z] = float(value) self.dataChanged.emit(index, index) return True def insertRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginInsertRows(parent, row, row + count - 1) if None in self.composition: return False self.composition[None] = 0.0 self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginRemoveRows(parent, row, count + row - 1) keys = list(self.composition.keys()) for key in keys[:row] + keys[row + count:]: self.composition.pop(key) self.endRemoveRows() return True class _CompositionDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): column = index.column() if column == 0: editor = PeriodicTableDialog(parent) editor.setMultipleSelection(False) editor.setRequiresSelection(True) return editor elif column == 1: editor = QLineEdit(parent) editor.setValidator(QDoubleValidator()) return editor else: return QItemDelegate.createEditor(self, parent, option, index) def setEditorData(self, editor, index): text = index.model().data(index, Qt.DisplayRole) column = index.column() if column == 0: if text != 'none': editor.setSelection(text) elif column == 1: editor.setText(text) else: QItemDelegate.setEditorData(self, editor, index) def setModelData(self, editor, model, index): column = index.column() if column == 0: model.setData(index, editor.selection()) elif column == 1: model.setData(index, editor.text()) else: return QItemDelegate.setModelData(self, editor, model, index) def __init__(self, parent=None): _CompositionWidget.__init__(self, CompositionElemental, parent) def _init_ui(self): # Widgets model = self._CompositionModel() self._table = QTableView() self._table.setModel(model) self._table.setItemDelegate(self._CompositionDelegate(self)) self._table.horizontalHeader().setStretchLastSection(True) self._toolbar = QToolBar() action_add = self._toolbar.addAction(getIcon("list-add"), "Add layer") action_remove = self._toolbar.addAction(getIcon("list-remove"), "Remove layer") # Layouts layout = _CompositionWidget._init_ui(self) layout.addRow(self._table) layout.addRow(self._toolbar) # Signals action_add.triggered.connect(self._on_add) action_remove.triggered.connect(self._on_remove) model.dataChanged.connect(self.edited) model.rowsInserted.connect(self.edited) model.rowsRemoved.connect(self.edited) return layout def _on_add(self): index = self._table.selectionModel().currentIndex() model = self._table.model() model.insertRows(index.row() + 1) def _on_remove(self): selection = self._table.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Window layer", "Select a layer") return model = self._table.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.removeRow(row) def _create_parameter(self): return self.CLASS('wt%') def parameter(self, parameter=None): parameter = _CompositionWidget.parameter(self, parameter) parameter.update(self._table.model().composition) return parameter def setParameter(self, condition): _CompositionWidget.setParameter(self, condition) self._table.model().composition.update(condition) self._table.model().reset() def setReadOnly(self, state): _CompositionWidget.setReadOnly(self, state) if state: trigger = QTableView.EditTrigger.NoEditTriggers else: trigger = QTableView.EditTrigger.AllEditTriggers self._table.setEditTriggers(trigger) self._toolbar.setEnabled(not state) def isReadOnly(self): return _CompositionWidget.isReadOnly(self) and \ self._table.editTriggers() == QTableView.EditTrigger.NoEditTriggers and \ not self._toolbar.isEnabled()
class BasePlotCurveEditorDialog(QDialog): """QDialog that is used in Qt Designer to edit the properties of the curves in a waveform plot. This dialog is shown when you double-click the plot, or when you right click it and choose 'edit curves'. This thing is mostly just a wrapper for a table view, with a couple buttons to add and remove curves, and a button to save the changes.""" TABLE_MODEL_CLASS = BasePlotCurvesModel def __init__(self, plot, parent=None): super(BasePlotCurveEditorDialog, self).__init__(parent) self.plot = plot self.setup_ui() self.table_model = self.TABLE_MODEL_CLASS(self.plot) self.table_view.setModel(self.table_model) self.table_model.plot = plot # self.table_view.resizeColumnsToContents() self.add_button.clicked.connect(self.addCurve) self.remove_button.clicked.connect(self.removeSelectedCurve) self.remove_button.setEnabled(False) self.table_view.selectionModel().selectionChanged.connect( self.handleSelectionChange) self.table_view.doubleClicked.connect(self.handleDoubleClick) self.resize(800, 300) def setup_ui(self): self.vertical_layout = QVBoxLayout(self) self.table_view = QTableView(self) self.table_view.setEditTriggers(QAbstractItemView.DoubleClicked) self.table_view.setProperty("showDropIndicator", False) self.table_view.setDragDropOverwriteMode(False) self.table_view.setSelectionMode(QAbstractItemView.SingleSelection) self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.table_view.setSortingEnabled(False) self.table_view.horizontalHeader().setStretchLastSection(True) self.table_view.verticalHeader().setVisible(False) self.table_view.setColumnWidth(0, 160) self.table_view.setColumnWidth(1, 160) self.table_view.setColumnWidth(2, 160) self.vertical_layout.addWidget(self.table_view) self.add_remove_layout = QHBoxLayout() spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.add_remove_layout.addItem(spacer) self.add_button = QPushButton("Add Curve", self) self.add_remove_layout.addWidget(self.add_button) self.remove_button = QPushButton("Remove Curve", self) self.add_remove_layout.addWidget(self.remove_button) self.vertical_layout.addLayout(self.add_remove_layout) self.button_box = QDialogButtonBox(self) self.button_box.setOrientation(Qt.Horizontal) self.button_box.addButton("Done", QDialogButtonBox.AcceptRole) self.vertical_layout.addWidget(self.button_box) self.button_box.accepted.connect(self.saveChanges) self.button_box.rejected.connect(self.reject) self.setWindowTitle("Waveform Curve Editor") def setup_delegate_columns(self, index=2): symbol_delegate = SymbolColumnDelegate(self) self.table_view.setItemDelegateForColumn(index + 3, symbol_delegate) line_delegate = LineColumnDelegate(self) self.table_view.setItemDelegateForColumn(index + 1, line_delegate) color_delegate = ColorColumnDelegate(self) self.table_view.setItemDelegateForColumn(index, color_delegate) @Slot() def addCurve(self): self.table_model.append() @Slot() def removeSelectedCurve(self): self.table_model.removeAtIndex(self.table_view.currentIndex()) @Slot(QItemSelection, QItemSelection) def handleSelectionChange(self, selected, deselected): self.remove_button.setEnabled( self.table_view.selectionModel().hasSelection()) @Slot(QModelIndex) def handleDoubleClick(self, index): if self.table_model.needsColorDialog(index): # The table model returns a QBrush for BackgroundRole, not a QColor init_color = self.table_model.data(index, Qt.BackgroundRole).color() color = QColorDialog.getColor(init_color, self) if color.isValid(): self.table_model.setData(index, color, role=Qt.EditRole) @Slot() def saveChanges(self): formWindow = QDesignerFormWindowInterface.findFormWindow(self.plot) if formWindow: formWindow.cursor().setProperty("curves", self.plot.curves) self.accept()
class BibleCollectionDialog(QDialog): def __init__(self, parent): super().__init__() self.setWindowTitle(config.thisTranslation["bibleCollections"]) self.setMinimumSize(680, 500) self.selectedCollection = None self.settingBibles = False self.bibles = self.getBibles() self.setupUI() self.parent = parent def setupUI(self): mainLayout = QVBoxLayout() title = QLabel(config.thisTranslation["bibleCollections"]) mainLayout.addWidget(title) self.collectionsLayout = QVBoxLayout() self.collectionsList = QListWidget() self.collectionsList.setMaximumHeight(90) self.collectionsLayout.addWidget(self.collectionsList) mainLayout.addLayout(self.collectionsLayout) self.showListOfCollections() buttonsLayout = QHBoxLayout() addButton = QPushButton(config.thisTranslation["add"]) addButton.clicked.connect(self.addNewCollection) buttonsLayout.addWidget(addButton) removeButton = QPushButton(config.thisTranslation["remove"]) removeButton.clicked.connect(self.removeCollection) buttonsLayout.addWidget(removeButton) renameButton = QPushButton(config.thisTranslation["rename"]) renameButton.clicked.connect(self.renameCollection) buttonsLayout.addWidget(renameButton) buttonsLayout.addStretch() mainLayout.addLayout(buttonsLayout) self.biblesTable = QTableView() self.biblesTable.setEnabled(False) self.biblesTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.biblesTable.setSortingEnabled(True) self.dataViewModel = QStandardItemModel(self.biblesTable) self.biblesTable.setModel(self.dataViewModel) self.loadBibleSelection() self.dataViewModel.itemChanged.connect(self.bibleSelectionChanged) mainLayout.addWidget(self.biblesTable) buttonLayout = QHBoxLayout() button = QPushButton(config.thisTranslation["close"]) button.clicked.connect(self.reloadControlPanel) button.clicked.connect(self.close) buttonLayout.addWidget(button) mainLayout.addLayout(buttonLayout) self.setLayout(mainLayout) def showListOfCollections(self): self.collectionsList.clear() if len(config.bibleCollections) > 0: for collection in sorted(config.bibleCollections.keys()): showBibleSelection = QRadioButton() showBibleSelection.setChecked(False) self.collectionsList.itemClicked.connect(self.selectCollection) self.collectionsList.addItem(collection) else: self.collectionsList.addItem("[No collection defined]") def addNewCollection(self): name, ok = QInputDialog.getText(self, 'Collection', 'Collection name:') if ok and len(name) > 0 and name != "All": config.bibleCollections[name] = {} self.showListOfCollections() self.biblesTable.setEnabled(False) def removeCollection(self): config.bibleCollections.pop(self.selectedCollection, None) self.showListOfCollections() self.biblesTable.setEnabled(False) def renameCollection(self): name, ok = QInputDialog.getText(self, 'Collection', 'Collection name:', text=self.selectedCollection) if ok and len(name) > 0 and name != "All": biblesInCollection = config.bibleCollections[ self.selectedCollection] config.bibleCollections.pop(self.selectedCollection, None) self.selectedCollection = name config.bibleCollections[name] = biblesInCollection self.showListOfCollections() self.biblesTable.setEnabled(False) def getBibles(self): from db.BiblesSqlite import BiblesSqlite from db.BiblesSqlite import Bible bibles = BiblesSqlite().getBibleList() bibleInfo = [] for bible in bibles: description = Bible(bible).bibleInfo() bibleInfo.append((bible, description)) return bibleInfo def selectCollection(self, item): self.selectedCollection = item.text() self.biblesTable.setEnabled(True) self.loadBibleSelection() def bibleSelectionChanged(self, item): if not self.settingBibles: if self.selectedCollection is not None: text = item.text() biblesInCollection = config.bibleCollections[ self.selectedCollection] if len(biblesInCollection) == 0: biblesInCollection = [] if text in biblesInCollection: biblesInCollection.remove(text) else: biblesInCollection.append(text) config.bibleCollections[ self.selectedCollection] = biblesInCollection def loadBibleSelection(self): self.settingBibles = True self.dataViewModel.clear() biblesInCollection = [] if self.selectedCollection is not None: biblesInCollection = config.bibleCollections[ self.selectedCollection] rowCount = 0 for bible, description in self.bibles: item = QStandardItem(bible) item.setToolTip(bible) item.setCheckable(True) if bible in biblesInCollection: item.setCheckState(Qt.Checked) self.dataViewModel.setItem(rowCount, 0, item) item = QStandardItem(description) self.dataViewModel.setItem(rowCount, 1, item) rowCount += 1 self.dataViewModel.setHorizontalHeaderLabels([ config.thisTranslation["bible"], config.thisTranslation["description"] ]) self.biblesTable.resizeColumnsToContents() self.settingBibles = False def reloadControlPanel(self): self.parent.reloadControlPanel(False)
class WindowWidget(ParameterWidget): class _WindowModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self.layers = [] def rowCount(self, *args, **kwargs): return len(self.layers) def columnCount(self, *args, **kwargs): return 2 def data(self, index, role): if not index.isValid() or not (0 <= index.row() < len(self.layers)): return None if role != Qt.DisplayRole: return None layer = self.layers[index.row()] column = index.column() if column == 0: return layer.material elif column == 1: return '%s' % layer.thickness def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'Material' elif section == 1: return 'Thickness' elif orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self.layers)): return False layer = self.layers[index.row()] column = index.column() if column == 0: layer.material = value elif column == 1: layer.thickness = value self.dataChanged.emit(index, index) return True def insertRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginInsertRows(parent, row, row + count - 1) for i in range(count): self.layers.insert(row + i, WindowLayer("unknown", 0.0)) self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginRemoveRows(parent, row, row + count - 1) self.layers = self.layers[:row] + self.layers[row + count:] self.endRemoveRows() return True class _WindowDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): column = index.column() if column == 0: return TextAttributeLineEdit(WindowLayer.material, parent) elif column == 1: return NumericalAttributeLineEdit(WindowLayer.thickness, parent) else: return QItemDelegate.createEditor(self, parent, option, index) def setEditorData(self, editor, index): text = index.model().data(index, Qt.DisplayRole) column = index.column() if column == 0: editor.setText(text) elif column == 1: editor.setText(text) else: QItemDelegate.setEditorData(self, editor, index) def setModelData(self, editor, model, index): column = index.column() if column == 0: model.setData(index, editor.text()) elif column == 1: model.setData(index, editor.text()) else: return QItemDelegate.setModelData(self, editor, model, index) def __init__(self, parent=None): ParameterWidget.__init__(self, Window, parent) def _init_ui(self): # Widgets model = self._WindowModel() self._table = QTableView() self._table.setModel(model) self._table.setItemDelegate(self._WindowDelegate(self)) self._table.horizontalHeader().setStretchLastSection(True) self._toolbar = QToolBar() action_add = self._toolbar.addAction(getIcon("list-add"), "Add layer") action_remove = self._toolbar.addAction(getIcon("list-remove"), "Remove layer") # Layouts layout = ParameterWidget._init_ui(self) layout.addRow(self._table) layout.addRow(self._toolbar) # Signals action_add.triggered.connect(self._on_add) action_remove.triggered.connect(self._on_remove) model.dataChanged.connect(self.edited) model.rowsInserted.connect(self.edited) model.rowsRemoved.connect(self.edited) return layout def _on_add(self): index = self._table.selectionModel().currentIndex() model = self._table.model() model.insertRows(index.row() + 1) def _on_remove(self): selection = self._table.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Window layer", "Select a layer") return model = self._table.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.removeRow(row) def parameter(self, parameter=None): parameter = ParameterWidget.parameter(self, parameter) parameter.layers.clear() for layer in self._table.model().layers: parameter.append_layer(layer.material, layer.thickness) # copy return parameter def setParameter(self, window): model = self._table.model() model.layers = window.layers model.reset() def window(self): return self.parameter() def setWindow(self, window): self.setParameter(window) def setReadOnly(self, state): ParameterWidget.setReadOnly(self, state) if state: trigger = QTableView.EditTrigger.NoEditTriggers else: trigger = QTableView.EditTrigger.AllEditTriggers self._table.setEditTriggers(trigger) self._toolbar.setEnabled(not state) def isReadOnly(self): return ParameterWidget.isReadOnly(self) and \ self._table.editTriggers() == QTableView.EditTrigger.NoEditTriggers and \ not self._toolbar.isEnabled()