class MetricsWindowTab(QWidget): def __init__(self, parent=None): super().__init__(parent) self.name = self.tr("Metrics Window") self.inputTextLabel = QLabel(self.tr("Default text:"), self) self.inputTextList = QListWidget(self) self.inputTextList.setDragDropMode(QAbstractItemView.InternalMove) self.addItemButton = QPushButton(self) self.addItemButton.setIcon(icons.i_plus()) self.addItemButton.clicked.connect(self.addItem) self.removeItemButton = QPushButton(self) self.removeItemButton.setIcon(icons.i_minus()) self.removeItemButton.clicked.connect(self.removeItem) buttonsLayout = QHBoxLayout() buttonsLayout.addWidget(self.addItemButton) buttonsLayout.addWidget(self.removeItemButton) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) buttonsLayout.addWidget(spacer) layout = QVBoxLayout(self) layout.addWidget(self.inputTextLabel) layout.addWidget(self.inputTextList) layout.addLayout(buttonsLayout) self.setLayout(layout) self.readSettings() def addItem(self): item = QListWidgetItem(self.inputTextList) item.setFlags(item.flags() | Qt.ItemIsEditable) self.inputTextList.setCurrentItem(item) self.inputTextList.editItem(item) self.removeItemButton.setEnabled(True) def removeItem(self): i = self.inputTextList.currentRow() self.inputTextList.takeItem(i) if not self.inputTextList.count(): self.removeItemButton.setEnabled(False) def readSettings(self): self.inputTextList.clear() entries = settings.metricsWindowComboBoxItems() for entry in entries: item = QListWidgetItem(entry, self.inputTextList) item.setFlags(item.flags() | Qt.ItemIsEditable) if not len(entries): self.removeItemButton.setEnabled(False) def writeSettings(self): entries = [] for i in range(self.inputTextList.count()): item = self.inputTextList.item(i) entries.append(item.text()) settings.setMetricsWindowComboBoxItems(entries)
class MetricsWindowTab(QWidget): def __init__(self, parent=None): super().__init__(parent) self.name = self.tr("Metrics Window") self.inputTextLabel = QLabel(self.tr("Default text:"), self) self.inputTextList = QListWidget(self) self.inputTextList.setDragDropMode(QAbstractItemView.InternalMove) self.addItemButton = QPushButton("+", self) self.addItemButton.clicked.connect(self.addItem) self.removeItemButton = QPushButton("−", self) self.removeItemButton.clicked.connect(self.removeItem) layout = QGridLayout(self) l = 0 layout.addWidget(self.inputTextLabel, l, 0, 1, 3) l += 1 layout.addWidget(self.inputTextList, l, 0, 1, 3) l += 1 layout.addWidget(self.addItemButton, l, 0) layout.addWidget(self.removeItemButton, l, 1) self.setLayout(layout) self.readSettings() def addItem(self): item = QListWidgetItem(self.inputTextList) item.setFlags(item.flags() | Qt.ItemIsEditable) self.inputTextList.setCurrentItem(item) self.inputTextList.editItem(item) self.removeItemButton.setEnabled(True) def removeItem(self): i = self.inputTextList.currentRow() self.inputTextList.takeItem(i) if not self.inputTextList.count(): self.removeItemButton.setEnabled(False) def readSettings(self): self.inputTextList.clear() entries = settings.metricsWindowComboBoxItems() for entry in entries: item = QListWidgetItem(entry, self.inputTextList) item.setFlags(item.flags() | Qt.ItemIsEditable) if not len(entries): self.removeItemButton.setEnabled(False) def writeSettings(self): entries = [] for i in range(self.inputTextList.count()): item = self.inputTextList.item(i) entries.append(item.text()) settings.setMetricsWindowComboBoxItems(entries)
class GlyphSetTab(QWidget): def __init__(self, parent=None): super().__init__(parent) self.name = self.tr("Glyph sets") self.defaultGlyphSetBox = QCheckBox( self.tr("Default glyph set:"), self) self.defaultGlyphSetDrop = QComboBox(self) self.defaultGlyphSetBox.toggled.connect(self.toggleGlyphSetDrop) self.glyphSetList = QListWidget(self) self.glyphSetList.setSortingEnabled(True) self.glyphSetContents = QPlainTextEdit(self) self.glyphSetList.currentItemChanged.connect( self.updateGlyphSetContents) self.glyphSetList.itemChanged.connect(self.renameGlyphSet) self._cachedName = None splitter = QSplitter(self) splitter.addWidget(self.glyphSetList) splitter.addWidget(self.glyphSetContents) self.addGlyphSetButton = QPushButton(self) self.addGlyphSetButton.setIcon(icons.i_plus()) self.addGlyphSetButton.clicked.connect(lambda: self.addGlyphSet()) self.removeGlyphSetButton = QPushButton(self) self.removeGlyphSetButton.setIcon(icons.i_minus()) self.removeGlyphSetButton.clicked.connect(self.removeGlyphSet) self.importButton = QPushButton(self.tr("Import"), self) importMenu = QMenu(self) importMenu.addAction( self.tr("Import from Current Font"), self.importFromCurrentFont) self.importButton.setMenu(importMenu) self.glyphListBox = QCheckBox(self.tr("Glyph list path:"), self) self.glyphListEdit = QLineEdit(self) self.glyphListEdit.setReadOnly(True) self.glyphListButton = QPushButton(self.tr("Browse…"), self) self.glyphListButton.clicked.connect(self.getGlyphList) self.glyphListBox.toggled.connect(self.glyphListEdit.setEnabled) self.glyphListBox.toggled.connect(self.glyphListButton.setEnabled) buttonsLayout = QHBoxLayout() buttonsLayout.addWidget(self.addGlyphSetButton) buttonsLayout.addWidget(self.removeGlyphSetButton) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) buttonsLayout.addWidget(spacer) buttonsLayout.addWidget(self.importButton) firstLayout = QGridLayout() l = 0 firstLayout.addWidget(self.defaultGlyphSetBox, l, 0, 1, 2) firstLayout.addWidget(self.defaultGlyphSetDrop, l, 3, 1, 3) l += 1 firstLayout.addWidget(splitter, l, 0, 1, 6) l += 1 firstLayout.addLayout(buttonsLayout, l, 0, 1, 3) secondLayout = QHBoxLayout() secondLayout.addWidget(self.glyphListBox) secondLayout.addWidget(self.glyphListEdit) secondLayout.addWidget(self.glyphListButton) mainLayout = QVBoxLayout(self) mainLayout.addLayout(firstLayout) mainLayout.addLayout(secondLayout) self.setLayout(mainLayout) self.readSettings() def addGlyphSet(self, glyphNames=[], glyphSetName=None): if glyphSetName is None: glyphSetName = self.tr("New glyph set") if glyphSetName in self.glyphSets: index = 1 while "%s %d" % (glyphSetName, index) in self.glyphSets: index += 1 glyphSetName = "%s %d" % (glyphSetName, index) self.glyphSets[glyphSetName] = glyphNames item = QListWidgetItem(glyphSetName, self.glyphSetList) item.setFlags(item.flags() | Qt.ItemIsEditable) self.glyphSetList.setCurrentItem(item) self.glyphSetList.editItem(item) self.removeGlyphSetButton.setEnabled(True) def removeGlyphSet(self): i = self.glyphSetList.currentRow() text = self.glyphSetList.takeItem(i).text() del self.glyphSets[text] if self.glyphSetList.count() < 2: self.removeGlyphSetButton.setEnabled(False) def renameGlyphSet(self): newKey = self.glyphSetList.currentItem() if newKey is None: return newKey = newKey.text() self.glyphSets[newKey] = self.glyphSets[self._cachedName] del self.glyphSets[self._cachedName] def importFromCurrentFont(self): font = QApplication.instance().currentFont() name = "%s %s" % (font.info.familyName, font.info.styleName) self.addGlyphSet(font.glyphOrder, name) def toggleGlyphSetDrop(self): sender = self.sender() self.defaultGlyphSetDrop.setEnabled(sender.isChecked()) def updateGlyphSetContents(self, current, previous): # store content of the textEdit in the glyphSet dict if previous is not None: glyphNames = self.glyphSetContents.toPlainText().split() self.glyphSets[previous.text()] = glyphNames # now update the text edit to current glyphSet glyphSetName = current.text() text = " ".join(self.glyphSets[glyphSetName]) self.glyphSetContents.setPlainText(text) # cache current name for renames self._cachedName = glyphSetName def getGlyphList(self): fileFormats = ( self.tr("Text file {}").format("(*.txt)"), self.tr("All files {}").format("(*.*)"), ) path, _ = QFileDialog.getOpenFileName( self, self.tr("Open File"), '', ";;".join(fileFormats) ) if path: self.glyphListEdit.setText(path) def readSettings(self): defaultGlyphSet = settings.defaultGlyphSet() self.defaultGlyphSetBox.setChecked(len(defaultGlyphSet)) self.glyphSets = settings.readGlyphSets() self.defaultGlyphSetDrop.clear() self.defaultGlyphSetDrop.addItems(self.glyphSets.keys()) self.glyphSetList.clear() glyphSetNames = self.glyphSets.keys() # Normally we should be enforcing this rather decently in the interface # already if glyphSetNames: for glyphSetName in glyphSetNames: item = QListWidgetItem(glyphSetName, self.glyphSetList) item.setFlags(item.flags() | Qt.ItemIsEditable) self.glyphSetList.setCurrentRow(0) self.removeGlyphSetButton.setEnabled(len(self.glyphSets) > 1) glyphListPath = settings.glyphListPath() self.glyphListBox.setChecked(bool(glyphListPath)) self.glyphListEdit.setEnabled(bool(glyphListPath)) self.glyphListEdit.setText(glyphListPath) self.glyphListButton.setEnabled(bool(glyphListPath)) def writeSettings(self): # store content of the textEdit in the glyphSet dict glyphNames = self.glyphSetContents.toPlainText().split() currentGlyphSet = self.glyphSetList.currentItem().text() self.glyphSets[currentGlyphSet] = glyphNames settings.writeGlyphSets(self.glyphSets) if not self.defaultGlyphSetBox.isChecked(): settings.setDefaultGlyphSet(None) else: defaultGlyphSet = self.defaultGlyphSetDrop.currentText() settings.setDefaultGlyphSet(defaultGlyphSet) if not self.glyphListBox.isChecked(): settings.setGlyphListPath(None) else: glyphListPath = self.glyphListEdit.text() if glyphListPath: settings.setGlyphListPath(glyphListPath) QApplication.instance().loadGlyphList()
class GlyphSetTab(QWidget): def __init__(self, parent=None): super().__init__(parent) self.name = self.tr("Glyph sets") self.defaultGlyphSetBox = QCheckBox( self.tr("Default glyph set:"), self) self.defaultGlyphSetDrop = QComboBox(self) self.defaultGlyphSetBox.toggled.connect(self.toggleGlyphSetDrop) self.glyphSetList = QListWidget(self) self.glyphSetList.setSortingEnabled(True) self.glyphSetContents = QPlainTextEdit(self) self.glyphSetList.currentItemChanged.connect( self.updateGlyphSetContents) self.glyphSetList.itemChanged.connect(self.renameGlyphSet) self._cachedName = None splitter = QSplitter(self) splitter.addWidget(self.glyphSetList) splitter.addWidget(self.glyphSetContents) self.addGlyphSetButton = QPushButton("+", self) self.addGlyphSetButton.clicked.connect(lambda: self.addGlyphSet()) self.removeGlyphSetButton = QPushButton("−", self) self.removeGlyphSetButton.clicked.connect(self.removeGlyphSet) self.importButton = QPushButton(self.tr("Import"), self) importMenu = QMenu(self) importMenu.addAction( self.tr("Import from current font"), self.importFromCurrentFont) self.importButton.setMenu(importMenu) self.glyphListBox = QCheckBox(self.tr("Glyph list path:"), self) self.glyphListEdit = QLineEdit(self) self.glyphListEdit.setReadOnly(True) self.glyphListButton = QPushButton(self.tr("Browse…"), self) self.glyphListButton.clicked.connect(self.getGlyphList) # TODO: find correct solution for this and maybe make a widget w # setSizesToText() # http://stackoverflow.com/a/19502467/2037879 textWidth = self.glyphListButton.fontMetrics().boundingRect( self.glyphListButton.text()).width() + 16 self.glyphListButton.setMaximumWidth(textWidth) self.glyphListBox.toggled.connect(self.glyphListEdit.setEnabled) self.glyphListBox.toggled.connect(self.glyphListButton.setEnabled) firstLayout = QGridLayout() l = 0 firstLayout.addWidget(self.defaultGlyphSetBox, l, 0, 1, 2) firstLayout.addWidget(self.defaultGlyphSetDrop, l, 3, 1, 3) l += 1 firstLayout.addWidget(splitter, l, 0, 1, 6) l += 1 firstLayout.addWidget(self.addGlyphSetButton, l, 0) firstLayout.addWidget(self.removeGlyphSetButton, l, 1) firstLayout.addWidget(self.importButton, l, 2) secondLayout = QHBoxLayout() secondLayout.addWidget(self.glyphListBox, 0) secondLayout.addWidget(self.glyphListEdit, 1) secondLayout.addWidget(self.glyphListButton, 2) mainLayout = QVBoxLayout(self) mainLayout.addLayout(firstLayout) mainLayout.addLayout(secondLayout) self.setLayout(mainLayout) self.readSettings() def addGlyphSet(self, glyphNames=[], glyphSetName=None): if glyphSetName is None: glyphSetName = self.tr("New glyph set") if glyphSetName in self.glyphSets: index = 1 while "%s %d" % (glyphSetName, index) in self.glyphSets: index += 1 glyphSetName = "%s %d" % (glyphSetName, index) self.glyphSets[glyphSetName] = glyphNames item = QListWidgetItem(glyphSetName, self.glyphSetList) item.setFlags(item.flags() | Qt.ItemIsEditable) self.glyphSetList.setCurrentItem(item) self.glyphSetList.editItem(item) self.removeGlyphSetButton.setEnabled(True) def removeGlyphSet(self): i = self.glyphSetList.currentRow() text = self.glyphSetList.takeItem(i).text() del self.glyphSets[text] if self.glyphSetList.count() < 2: self.removeGlyphSetButton.setEnabled(False) def renameGlyphSet(self): newKey = self.glyphSetList.currentItem() if newKey is None: return newKey = newKey.text() self.glyphSets[newKey] = self.glyphSets[self._cachedName] del self.glyphSets[self._cachedName] def importFromCurrentFont(self): font = QApplication.instance().currentFont() name = "%s %s" % (font.info.familyName, font.info.styleName) self.addGlyphSet(font.glyphOrder, name) def toggleGlyphSetDrop(self): sender = self.sender() self.defaultGlyphSetDrop.setEnabled(sender.isChecked()) def updateGlyphSetContents(self, current, previous): # store content of the textEdit in the glyphSet dict if previous is not None: glyphNames = self.glyphSetContents.toPlainText().split() self.glyphSets[previous.text()] = glyphNames # now update the text edit to current glyphSet glyphSetName = current.text() text = " ".join(self.glyphSets[glyphSetName]) self.glyphSetContents.setPlainText(text) # cache current name for renames self._cachedName = glyphSetName def getGlyphList(self): fileFormats = ( self.tr("Text file {}").format("(*.txt)"), self.tr("All files {}").format("(*.*)"), ) path, _ = QFileDialog.getOpenFileName( self, self.tr("Open File"), '', ";;".join(fileFormats) ) if path: self.glyphListEdit.setText(path) def readSettings(self): defaultGlyphSet = settings.defaultGlyphSet() self.defaultGlyphSetBox.setChecked(len(defaultGlyphSet)) self.glyphSets = settings.readGlyphSets() self.defaultGlyphSetDrop.clear() self.defaultGlyphSetDrop.addItems(self.glyphSets.keys()) self.glyphSetList.clear() glyphSetNames = self.glyphSets.keys() # Normally we should be enforcing this rather decently in the interface # already if glyphSetNames: for glyphSetName in glyphSetNames: item = QListWidgetItem(glyphSetName, self.glyphSetList) item.setFlags(item.flags() | Qt.ItemIsEditable) self.glyphSetList.setCurrentRow(0) self.removeGlyphSetButton.setEnabled(len(self.glyphSets) > 1) glyphListPath = settings.glyphListPath() self.glyphListBox.setChecked(bool(glyphListPath)) self.glyphListEdit.setEnabled(bool(glyphListPath)) self.glyphListEdit.setText(glyphListPath) self.glyphListButton.setEnabled(bool(glyphListPath)) def writeSettings(self): # store content of the textEdit in the glyphSet dict glyphNames = self.glyphSetContents.toPlainText().split() currentGlyphSet = self.glyphSetList.currentItem().text() self.glyphSets[currentGlyphSet] = glyphNames settings.writeGlyphSets(self.glyphSets) if not self.defaultGlyphSetBox.isChecked(): settings.setDefaultGlyphSet(None) else: defaultGlyphSet = self.defaultGlyphSetDrop.currentText() settings.setDefaultGlyphSet(defaultGlyphSet) if not self.glyphListBox.isChecked(): settings.setGlyphListPath(None) else: glyphListPath = self.glyphListEdit.text() if glyphListPath: settings.setGlyphListPath(glyphListPath) QApplication.instance().loadGlyphList()
class ListEntry(BaseParamWidget): def __init__(self, layout_dir: str, title: str, initial_value: List[List[str]], new_id_func: Callable): super().__init__(layout_dir) self.choices = [] self.text_list = [] lab_title = QLabel(text=title) layout = QHBoxLayout() self.central_layout.addWidget(lab_title) self.on_check_callback = None self.list_widget = QListWidget() self.set_value(initial_value) layout_tools = QVBoxLayout() self.button_add_item = QPushButton('+') self.button_delete_item = QPushButton('-') self.button_delete_item.clicked.connect(self.delete_row) self.button_add_item.clicked.connect(self.add_row) self.button_add_item.setMaximumWidth(20) self.button_delete_item.setMaximumWidth(20) layout_tools.addWidget(self.button_add_item) layout_tools.addWidget(self.button_delete_item) layout.addLayout(layout_tools) layout.addWidget(self.list_widget) self.central_layout.addLayout(layout) self.data = initial_value self.new_id_func = new_id_func def new_id(self): if callable(self.new_id_func): return self.new_id_func() else: return None def add_row(self): self.data[0].append(self.new_id()) self.data[1].append('Unnamed') self.set_value(self.data) def delete_row(self): # item = self.list_widget.currentItem() index = self.list_widget.currentIndex().row() self.data[0].pop(index) self.data[1].pop(index) self.set_value(self.data) def on_listwidget_double_cicked(self): print('edit') item = self.list_widget.currentItem() self.list_widget.editItem(item) def get_value(self): text = [] for i in range(self.list_widget.count()): text.append(self.list_widget.item(i).text()) self.data[1] = text return self.data def set_value(self, data: List[List[str]]): self.list_widget.clear() self.list_widget.addItems(data[1]) self.data = data for index in range(self.list_widget.count()): item = self.list_widget.item(index) item.setFlags(item.flags() | Qt.ItemIsEditable)
class Example(QWidget): def __init__(self): super().__init__() self.filenames = json_files() if type(self.filenames) is list: self.curr_file = self.filenames[0] else: self.curr_file = self.filenames self.initUI() def initUI(self): self.num = -1 # index for search bar query self.show_save = False # bool for showing unsaved changes dialog self.temp_file = ".temp.csv" self.refresh_file = False # failsafe 1 for itemChanged trigger self.list_1 = QListWidget() lister(file=self.curr_file, target=self.list_1, index=0, mode=0) self.list_1.clicked.connect(self.clear_selection) self.list_1.installEventFilter(self) self.list_items = self.list_1.count( ) # failsafe 2 for itemChanged trigger self.list_1.itemChanged.connect(self.edit_next_item) self.list_1.verticalScrollBar().valueChanged.connect(self.sync_scroll) self.list_2 = QListWidget() lister(file=self.curr_file, target=self.list_2, index=1, mode=0) self.list_2.clicked.connect(self.clear_selection) self.list_3 = QListWidget() lister(file=self.curr_file, target=self.list_3, index=2, mode=0) self.list_3.clicked.connect(self.clear_selection) self.all_lists = [self.list_1, self.list_2, self.list_3] self.menubar = QMenuBar() self.menubar.setNativeMenuBar(False) exit_event = QAction('Exit', self) exit_event.setShortcut('Ctrl+W') exit_event.triggered.connect(app.quit) showAct = QAction('Show extras', self, checkable=True) showAct.setChecked(False) showAct.setShortcut('Ctrl+E') showAct.triggered.connect(self.hide_notes) addAct = QAction('Fields', self) addAct.setShortcut('Ctrl+N') addAct.triggered.connect(self.add_item) fileOpen = QAction('Open file', self) fileOpen.triggered.connect(self.fileDialog) fileOpen.setShortcut('Ctrl+O') fileSave = QAction('Save file', self) fileSave.triggered.connect(self.save) fileSave.triggered.connect(self.refresh_recents) fileSave.setShortcut('Ctrl+S') self.fileRecents = QMenu('Recent file', self) self.refresh_recents() self.toggle_theme = QAction('Toggle theme', self, checkable=True) self.toggle_theme.setChecked(json_theme()) self.toggle_theme.triggered.connect(self.theme) self.toggle_theme.setShortcut('Ctrl+T') self.col_sort_index = QMenu('Sorting column index', self) self.col_sort_index.addAction(QAction(str(0), self)) self.col_sort_index.addAction(QAction(str(1), self)) self.col_sort_index.addAction(QAction(str(2), self)) self.col_sort_index.triggered.connect(self.sort_col_choice) self.col_search_index = QMenu('Searching column index', self) self.col_search_index.addAction(QAction(str(0), self)) self.col_search_index.addAction(QAction(str(1), self)) self.col_search_index.addAction(QAction(str(2), self)) self.col_search_index.triggered.connect(self.search_col_choice) self.sort = QAction('Sort entries', self, checkable=True) self.curr_col = 0 self.search_col = 0 self.sort.triggered.connect(self.refresh_list) self.sort.setShortcut('Ctrl+R') self.addFields = self.menubar.addMenu('Add') self.addFields.addAction(addAct) self.optionMenu = self.menubar.addMenu('Options') self.optionMenu.addAction(exit_event) self.optionMenu.addAction(showAct) self.optionMenu.addAction(self.toggle_theme) self.optionMenu.addMenu(self.col_sort_index) self.optionMenu.addMenu(self.col_search_index) self.optionMenu.addAction(self.sort) self.fileMenu = self.menubar.addMenu('File') self.fileMenu.addAction(fileOpen) self.fileMenu.addAction(fileSave) self.fileMenu.addMenu(self.fileRecents) self.search_bar = QLineEdit() self.search_bar.setPlaceholderText('Search vocab') self.search_bar.setClearButtonEnabled(True) self.search_bar.setMaxLength(10) self.search_bar.returnPressed.connect(self.search_item) self.status_bar = QStatusBar() status(self.status_bar, self.list_1) grid = QGridLayout() grid.setSpacing(10) grid.addWidget(self.menubar, 0, 0) grid.addWidget(self.list_1, 1, 0) grid.addWidget(self.list_2, 1, 1) grid.addWidget(self.list_3, 1, 2) grid.addWidget(self.search_bar, 0, 1) grid.addWidget(self.status_bar) self.theme() self.setLayout(grid) self.setGeometry(*json_window_size()) self.setWindowTitle(f'{split_name(self.curr_file)}') self.show() self.list_1.scrollToBottom() self.list_2.verticalScrollBar().setHidden(True) self.list_3.verticalScrollBar().setHidden(True) self.list_3.setHidden(True) def sync_scroll(self): scroll_location = self.list_1.verticalScrollBar().value() self.list_2.verticalScrollBar().setValue(scroll_location) self.list_3.verticalScrollBar().setValue(scroll_location) def edit_next_item(self, event): """When an item is added and edited on the first col, starts editing its counterpart on the next col""" if self.list_items == self.list_1.count( ) - 2 or self.list_items != self.list_1.count( ) and self.refresh_file == False: item = self.list_2.item(self.list_2.count() - 1) self.list_2.editItem(item) self.list_items = self.list_1.count() def closeEvent(self, event): """Triggered upon program exit, shows a dialog for unsaved changes using a bool""" if self.show_save == True: reply = QMessageBox.question( self, 'Message', "You may have unsaved changes, are you sure you want to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: try: remove(self.temp_file) except: pass event.accept() else: event.ignore() else: pass def sort_col_choice(self, action): self.curr_col = int(action.text()) def search_col_choice(self, action): self.search_col = int(action.text()) def refresh_list(self): """Refreshes the contents of the lists, when sorting is used""" self.save( mode=1 ) # saves a temp copy, with changes, but irreversable sorting introduced clear_lists(self.all_lists) if self.sort.isChecked() == True: mode = 2 else: mode = 0 try: lister(file=self.temp_file, target=self.list_1, index=0, mode=mode, column=self.curr_col) lister(file=self.temp_file, target=self.list_2, index=1, mode=mode, column=self.curr_col) lister(file=self.temp_file, target=self.list_3, index=2, mode=mode, column=self.curr_col) except: lister(file=self.curr_file, target=self.list_1, index=0, mode=mode, column=self.curr_col) lister(file=self.curr_file, target=self.list_2, index=1, mode=mode, column=self.curr_col) lister(file=self.curr_file, target=self.list_3, index=2, mode=mode, column=self.curr_col) def refresh_recents(self): try: file_1 = QAction(self.curr_file, self) self.fileRecents.addAction(file_1) file_1.triggered.connect(self.clickedFileAct) if type(self.filenames) is list: if self.filenames[1] != None: file_2 = QAction(self.filenames[1], self) self.fileRecents.addAction(file_2) file_2.triggered.connect(self.clickedFileAct) if self.filenames[2] != None: file_3 = QAction(self.filenames[2], self) self.fileRecents.addAction(file_3) file_3.triggered.connect(self.clickedFileAct) except: pass def clickedFileAct(self): self.refresh_file = True file = self.sender().text() self.curr_file = file self.setWindowTitle(f'{split_name(self.curr_file)}') clear_lists(self.all_lists) lister(file=self.curr_file, target=self.list_1, index=0) lister(file=self.curr_file, target=self.list_2, index=1) lister(file=self.curr_file, target=self.list_3, index=2) status(self.status_bar, self.list_1) self.theme() self.list_1.scrollToBottom() self.list_3.setHidden(True) self.refresh_file = False def eventFilter(self, source, event): """Item (row) deletion""" if (event.type() == QEvent.ContextMenu and source is self.list_1): menu = QMenu() menu.addAction("Delete row") if menu.exec_(event.globalPos()): item = source.itemAt(event.pos()) try: model = self.list_1.indexFromItem(item) row = model.row() self.show_save = True self.list_1.takeItem(row) self.list_2.takeItem(row) self.list_3.takeItem(row) status(self.status_bar, self.list_1, f'Deleted row number: {row+1}.') self.clearSelection() except: pass return True return super(Example, self).eventFilter(source, event) def hide_notes(self): """Toggles showing the note column and stretches the window for clearer reading of it""" self.list_3.setHidden(not self.list_3.isHidden()) def theme(self): """Sets the theme for the window and its widgets""" palette = QPalette() # dark theme if self.toggle_theme.isChecked() == True: palette.setColor(QPalette.Window, QColor(0, 0, 0)) dark = "background-color: rgb(0, 0, 0); color: rgb(255, 255, 255);" self.menubar.setStyleSheet(dark) self.addFields.setStyleSheet(dark) self.optionMenu.setStyleSheet(dark) self.fileMenu.setStyleSheet(dark) self.search_bar.setStyleSheet( "background-color: rgb(0, 0, 0); color: rgb(255, 255, 255)" ) # border: 0px; for transparency self.status_bar.setStyleSheet(dark) style_items(self.all_lists, dark_theme=True) # light theme elif self.toggle_theme.isChecked() == False: palette.setColor(QPalette.Window, QColor(255, 255, 255)) light = "background-color: rgb(255, 255, 255); color: rgb(0, 0, 0)" self.menubar.setStyleSheet(light) self.addFields.setStyleSheet(light) self.optionMenu.setStyleSheet(light) self.fileMenu.setStyleSheet(light) self.search_bar.setStyleSheet(light) self.status_bar.setStyleSheet(light) style_items(self.all_lists, dark_theme=False) self.setPalette(palette) self.theme_bool = self.toggle_theme.isChecked( ) # used in the save func def search_item(self): """Takes input from the search bar and matches with an item, gets index and scrolls to it, more reusults being qued with the num class var """ query = self.search_bar.text() search = self.all_lists[self.search_col].findItems( query, Qt.MatchContains) status(self.status_bar, self.list_1, f'Found {len(search)} results.') self.clear_selection() # testing search in all column # search_list =[] # for x in range(3): # search_list.append(self.all_lists[x].findItems(query, Qt.MatchContains)) # parent_list = [] # for x in range(3): # for y in range(len(search_list[x])): # parent_list.append(self.all_lists[x]) # replace with x # import itertools # merged = list(itertools.chain.from_iterable(search_list)) # search_dict = dict(zip(parent_list, merged)) # print(search_dict) # print() # print(len(merged)) # print(len(parent_list)) self.num += 1 for i in search: try: model_index = self.all_lists[self.search_col].indexFromItem( search[self.num]) except: self.num = 0 model_index = self.all_lists[self.search_col].indexFromItem( search[self.num]) item_index = model_index.row() self.all_lists[self.search_col].item(item_index).setSelected(True) self.list_1.scrollToItem(self.list_1.item(item_index), QAbstractItemView.PositionAtCenter) def add_item(self): self.show_save = True for x in range(3): if x == 0: lister(file=self.curr_file, target=self.list_1, index=x, mode=1) elif x == 1: lister(file=self.curr_file, target=self.list_2, index=x, mode=1) elif x == 2: lister(file=self.curr_file, target=self.list_3, index=x, mode=1) item = self.list_1.item(self.list_1.count() - 1) self.list_1.editItem(item) status(self.status_bar, self.list_1) self.list_1.scrollToBottom() self.list_2.scrollToBottom() self.list_3.scrollToBottom() def clear_selection(self): """Clears all item slections for aesthetical purposes, but only single clicks""" self.list_1.clearSelection() self.list_2.clearSelection() self.list_3.clearSelection() def fileDialog(self): fname = QFileDialog() path = fname.getOpenFileName(self, 'Open file', getcwd(), filter='csv (*.csv);;') if path[0] == '': # failsafe for canceling the dialog return self.curr_file self.curr_file = path[0] self.setWindowTitle(f'{split_name(self.curr_file)}') clear_lists(self.all_lists) lister(file=self.curr_file, target=self.list_1, index=0) lister(file=self.curr_file, target=self.list_2, index=1) lister(file=self.curr_file, target=self.list_3, index=2) status(self.status_bar, self.list_1) self.theme() def save(self, mode=0): self.show_save = False list1_items = items_text(self.list_1) list2_items = items_text(self.list_2) list3_items = items_text(self.list_3) total_dicts = [] for (a, b, c) in zip(list1_items, list2_items, list3_items): # each letter is a column dictionary = {'word_1': a, 'word_2': b, 'notes': c} total_dicts.append(dictionary) if mode == 0: writer(file=self.curr_file, data=total_dicts) status(self.status_bar, self.list_1, ('Saved current changes.')) try: json_template(theme=self.theme_bool, files=[self.curr_file, None, None], window_size=self.geometry().getRect() ) # current size values of the window except: json_template( ) # bug cannot be avoided, even though used setChecked at the beggining elif mode == 1: self.show_save = True writer(file=self.temp_file, data=total_dicts) # avoids stacking and refreshes recent file actions actions = self.fileRecents.actions() for action in actions: self.fileRecents.removeAction(action)
class GridControl(QGroupBox): """ Widget for controlling grids in Main Window. """ """Signals""" sig_place_grid = pyqtSignal() sig_crop_region = pyqtSignal(list, float, str) sig_generate_rotated_image = pyqtSignal(float) def __init__(self, parent, title, num_rows, num_cols): super().__init__(parent=parent, title=title) # self.gc_box = QGroupBox(parent=self, title='Grid control') self.layout = QGridLayout() self.label_checkbox = QCheckBox('Toggle labels', parent=self) self.col_label = QLabel('Columns:', parent=self) self.row_label = QLabel('Rows: ', parent=self) self.col_spinbox = QSpinBox(parent=self, value=num_cols, maximum=26, minimum=1) self.row_spinbox = QSpinBox(parent=self, value=num_rows, maximum=40, minimum=1) self.btn_like_grid = QPushButton('I like this grid!', parent=self) self.grid_list = QListWidget(parent=self) self.btn_crop_grid = QPushButton('Crop', parent=self) self.btn_del_grid = QPushButton('Delete', parent=self) self.parent = self.parentWidget() self.configure_gui() self.configure_signals() def configure_gui(self): """ Configure grid control """ """Layout""" self.layout.addWidget(self.row_label, 0, 0) self.layout.addWidget(self.col_label, 0, 1) self.layout.addWidget(self.col_spinbox, 1, 1) self.layout.addWidget(self.row_spinbox, 1, 0) self.layout.addWidget(self.label_checkbox, 2, 0, 1, 2) self.layout.addWidget(self.btn_like_grid, 3, 0, 1, 2) self.layout.addWidget(self.grid_list, 4, 0, 1, 2) self.layout.addWidget(self.btn_crop_grid, 5, 0) self.layout.addWidget(self.btn_del_grid, 5, 1) self.setLayout(self.layout) self.setGeometry(0, 0, 150, 400) self.move(520, 90) """Checkbox""" self.label_checkbox.resize(self.label_checkbox.sizeHint()) self.label_checkbox.setCheckState(Qt.Unchecked) self.label_checkbox.stateChanged.connect(self.label_checkbox_checked) self.label_checkbox_checked() # update grid to default settings """Row/column spinboxes""" self.row_spinbox.valueChanged.connect(self.parent.set_num_rows) self.col_spinbox.valueChanged.connect(self.parent.set_num_cols) """I like this grid! button""" self.btn_like_grid.resize(self.btn_like_grid.sizeHint()) self.btn_like_grid.setEnabled(False) self.btn_like_grid.clicked.connect(self.like_grid_button_clicked) """Grid list""" self.grid_list.setSelectionMode(QAbstractItemView.SingleSelection) self.grid_list.itemSelectionChanged.connect(self.change_selected_grid) self.grid_list.itemDoubleClicked.connect(self.change_grid_name) """Crop and del button""" self.btn_crop_grid.setEnabled(False) self.btn_crop_grid.clicked.connect(self.crop_grid_button_clicked) self.btn_del_grid.setEnabled(False) self.btn_del_grid.clicked.connect(self.del_grid_button_clicked) def configure_signals(self): self.sig_place_grid.connect(self.parent.place_grid) self.sig_generate_rotated_image.connect(self.parent.rotate_image) @pyqtSlot() def change_selected_grid(self): """ Call when selected grid in `GridList` changes. `Crop` and `Del` are enabled, and selected grid changes def_color. Returns ------- """ grid_list = [] for i in range(self.grid_list.count()): grid_list.append(self.grid_list.item(i)) grid_list_selected = self.grid_list.selectedItems() for grid_item in grid_list: grid_item.grid.set_color_and_thickness(color=Qt.blue) for grid_item in grid_list_selected: grid_item.grid.set_color_and_thickness(color=Qt.red, thickness=3.) if len(grid_list_selected) > 0: self.btn_crop_grid.setEnabled(True) self.btn_del_grid.setEnabled(True) else: self.btn_crop_grid.setEnabled(False) self.btn_del_grid.setEnabled(False) @pyqtSlot() def change_grid_name(self): item = self.grid_list.selectedItems()[0] self.grid_list.editItem(item) @pyqtSlot() def crop_grid_button_clicked(self): """ Crop images using the selected grid in `GridList`. For each grid square, emit signal `sig_crop_region`, passing `QRectF` scaled coordinates of region to crop. Returns ------- """ """Collect grid information""" grid_item = self.grid_list.selectedItems()[0] image_coordinates = grid_item.grid.image_coordinates num_rows = grid_item.grid.num_rows num_cols = grid_item.grid.num_cols angle = grid_item.grid.phi alphabet = string.ascii_uppercase """Create directory and crop images""" directory = grid_item.text() if not os.path.exists(directory): os.makedirs(directory) """generate counter-rotated_image for cropping""" self.sig_generate_rotated_image.emit(-angle) """Crop images""" for ix, image_coordinate in enumerate(image_coordinates): """Get bg_image coordinate""" tl_x, tl_y = image_coordinate[0, 0] tl = QPointF(tl_x, tl_y) # top left bl_x, bl_y = image_coordinate[0, 1] bl = QPointF(bl_x, bl_y) # top right tr_x, tr_y = image_coordinate[1, 0] tr = QPointF(tr_x, tr_y) # bottom left br_x, br_y = image_coordinate[1, 1] br = QPointF(br_x, br_y) # bottom right """Find polygon to be cropped""" coords = [tl, bl, br, tr] # pol = QPolygonF(coords) """Determine file name""" col = (ix // num_rows) row = (ix % num_rows) + 1 file_name = str(alphabet[col]) + str(row) full_path = os.path.join(directory, file_name) self.sig_crop_region.emit(coords, angle, full_path) else: print('Can\'t crop bg_image: folder already exist') @pyqtSlot() def del_grid_button_clicked(self): """ Delete grid from `GridList`. Returns ------- """ """Find selected GridListItemWidget """ grid_item = self.grid_list.selectedItems()[0] """Remove from GridList""" item_ix = self.grid_list.row(grid_item) self.grid_list.takeItem(item_ix) """Remove from scene""" grid_item.grid.clear_grid() @pyqtSlot() def like_grid_button_clicked(self): """ Add grid to `grid_list` and emit `sig_place_grid`. Returns ------- """ self.btn_like_grid.setEnabled(False) """Add grid to placed grid widget""" item = GridListWidgetItem(parent=self.grid_list) item.set_grid(self.parent.view.current_grid) # TODO ItemIsEditable does not work # item.setFlags(Qt.ItemIsEditable) # item.setFlags(Qt.ItemIsSelectable) self.sig_place_grid.emit() @pyqtSlot() def label_checkbox_checked(self): """ Enable/disable labels "A1,..,etc." in current grid. :return: """ check_state = self.label_checkbox.checkState() grid = self.parent.view.current_grid if check_state == Qt.Checked: grid.label_enabled = True elif check_state == Qt.Unchecked: grid.label_enabled = False """Redraw grid""" try: tl, br = grid.tl_br_qpointf except ValueError: pass else: tl_x = tl.x() tl_y = tl.y() br_x = br.x() br_y = br.y() angle = grid.phi grid.draw_grid(tl_x, tl_y, br_x, br_y, angle)