class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.resize(600, 300) self.model = QDirModel(self) # 1 self.model.setReadOnly(False) self.model.setSorting(QDir.Name | QDir.IgnoreCase) self.tree = QTreeView(self) # 2 self.tree.setModel(self.model) self.tree.clicked.connect(self.show_info) self.index = self.model.index(QDir.currentPath()) self.tree.expand(self.index) self.tree.scrollTo(self.index) self.info_label = QLabel(self) # 3 self.v_layout = QVBoxLayout() self.v_layout.addWidget(self.tree) self.v_layout.addWidget(self.info_label) self.setLayout(self.v_layout) def show_info(self): # 4 index = self.tree.currentIndex() file_name = self.model.fileName(index) file_path = self.model.filePath(index) file_info = 'File Name: {}\nFile Path: {}'.format(file_name, file_path) self.info_label.setText(file_info)
class QTreeSelection(QToolButton): currentIndexChanged = pyqtSignal(QModelIndex, QModelIndex) currentDataChanged = pyqtSignal(object) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setPopupMode(QToolButton.MenuButtonPopup) self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.tree = QTreeView(self) self.tree.setMinimumWidth(640) self.tree.setSelectionMode(QTreeView.SingleSelection) self.tree.setSelectionBehavior(QTreeView.SelectRows) act = QWidgetAction(self) act.setDefaultWidget(self.tree) self.menu = QMenu(self) self.menu.addAction(act) self.setMenu(self.menu) self.clicked.connect(self.showMenu) def _currentIndexChanged(self, newindex, oldindex): self.menu.close() selected = newindex.sibling(newindex.row(), 0) display = selected.data(Qt.DisplayRole) icon = selected.data(Qt.DecorationRole) self.setText(display or "") self.setIcon(icon or QIcon()) self.currentIndexChanged.emit(newindex, oldindex) self.currentDataChanged.emit(newindex.data(Qt.UserRole)) def currentIndex(self): return self.tree.currentIndex() def currentData(self): return self.currentIndex().data(Qt.UserRole) def setCurrentIndex(self, index): return self.tree.setCurrentIndex(index) def model(self): return self.tree.model() def setModel(self, model): self.tree.setModel(model) self.tree.selectionModel().currentChanged.connect( self._currentIndexChanged) def keyPressEvent(self, event): if (event.key() in (Qt.Key_Up, Qt.Key_Down) and not (event.modifiers() & (Qt.ShiftModifier | Qt.AltModifier | Qt.ControlModifier))): self.tree.keyPressEvent(event) return super().keyPressEvent(event)
class ItemsView(QDockWidget): def __init__(self, title, parent, items): super().__init__(title, parent) self.setFloating(False) self.set_items(items) def set_items(self, items): self.items = items self.itemsTree = QTreeView() self.itemsTree.setHeaderHidden(True) treeModel = QStandardItemModel() rootNode = treeModel.invisibleRootItem() groups = {} for item in items: category = item['data']['category'] node = self.__get_node(item['name']) if (category not in groups): groups[category] = self.__get_node(category) groups[category].appendRow(node) for key in groups: rootNode.appendRow(groups[key]) self.itemsTree.setModel(treeModel) self.itemsTree.expandAll() self.setWidget(self.itemsTree) def set_on_item_selected(self, method): self.itemsTree.selectionModel().selectionChanged.connect( lambda: method(self.__get_selected_item())) def __get_selected_item(self): current = self.itemsTree.currentIndex() found_item = None if (current.parent() != None): for item in self.items: if (item['name'] == current.data()): found_item = item return found_item def __get_node(self, title): node = QStandardItem() node.setEditable(False) node.setText(title) return node
class SearchDir(QWidget): def __init__(self): super().__init__() self.title = '' self.initUI() def initUI(self): self.model = QFileSystemModel() self.model.setRootPath('') self.tree = QTreeView() self.tree.setModel(self.model) self.tree.setAnimated(False) self.tree.setIndentation(20) self.tree.setSortingEnabled(True) windowLayout = QVBoxLayout() windowLayout.addWidget(self.tree) self.setLayout(windowLayout) self.show() def getPath(self): index = self.tree.currentIndex() return self.model.filePath(index)
class Widget(QWidget): def __init__(self, panel): super(Widget, self).__init__(panel) layout = QVBoxLayout() self.setLayout(layout) layout.setSpacing(0) self.searchEntry = SearchLineEdit() self.treeView = QTreeView(contextMenuPolicy=Qt.CustomContextMenu) self.textView = QTextBrowser() applyButton = QToolButton(autoRaise=True) editButton = QToolButton(autoRaise=True) addButton = QToolButton(autoRaise=True) self.menuButton = QPushButton(flat=True) menu = QMenu(self.menuButton) self.menuButton.setMenu(menu) splitter = QSplitter(Qt.Vertical) top = QHBoxLayout() layout.addLayout(top) splitter.addWidget(self.treeView) splitter.addWidget(self.textView) layout.addWidget(splitter) splitter.setSizes([200, 100]) splitter.setCollapsible(0, False) top.addWidget(self.searchEntry) top.addWidget(applyButton) top.addSpacing(10) top.addWidget(addButton) top.addWidget(editButton) top.addWidget(self.menuButton) # action generator for actions added to search entry def act(slot, icon=None): a = QAction(self, triggered=slot) self.addAction(a) a.setShortcutContext(Qt.WidgetWithChildrenShortcut) icon and a.setIcon(icons.get(icon)) return a # hide if ESC pressed in lineedit a = act(self.slotEscapePressed) a.setShortcut(QKeySequence(Qt.Key_Escape)) # import action a = self.importAction = act(self.slotImport, 'document-open') menu.addAction(a) # export action a = self.exportAction = act(self.slotExport, 'document-save-as') menu.addAction(a) # apply button a = self.applyAction = act(self.slotApply, 'edit-paste') applyButton.setDefaultAction(a) menu.addSeparator() menu.addAction(a) # add button a = self.addAction_ = act(self.slotAdd, 'list-add') a.setShortcut(QKeySequence(Qt.Key_Insert)) addButton.setDefaultAction(a) menu.addSeparator() menu.addAction(a) # edit button a = self.editAction = act(self.slotEdit, 'document-edit') a.setShortcut(QKeySequence(Qt.Key_F2)) editButton.setDefaultAction(a) menu.addAction(a) # set shortcut action a = self.shortcutAction = act(self.slotShortcut, 'preferences-desktop-keyboard-shortcuts') menu.addAction(a) # delete action a = self.deleteAction = act(self.slotDelete, 'list-remove') a.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Delete)) menu.addAction(a) # restore action a = self.restoreAction = act(self.slotRestore) menu.addSeparator() menu.addAction(a) # help button a = self.helpAction = act(self.slotHelp, 'help-contents') menu.addSeparator() menu.addAction(a) self.treeView.setSelectionBehavior(QTreeView.SelectRows) self.treeView.setSelectionMode(QTreeView.ExtendedSelection) self.treeView.setRootIsDecorated(False) self.treeView.setAllColumnsShowFocus(True) self.treeView.setModel(model.model()) self.treeView.setCurrentIndex(QModelIndex()) # signals self.searchEntry.returnPressed.connect(self.slotReturnPressed) self.searchEntry.textChanged.connect(self.updateFilter) self.treeView.doubleClicked.connect(self.slotDoubleClicked) self.treeView.customContextMenuRequested.connect(self.showContextMenu) self.treeView.selectionModel().currentChanged.connect(self.updateText) self.treeView.model().dataChanged.connect(self.updateFilter) # highlight text self.highlighter = highlight.Highlighter(self.textView.document()) # complete on snippet variables self.searchEntry.setCompleter(QCompleter([ ':icon', ':indent', ':menu', ':name', ':python', ':selection', ':set', ':symbol', ':template', ':template-run'], self.searchEntry)) self.readSettings() app.settingsChanged.connect(self.readSettings) app.translateUI(self) self.updateColumnSizes() self.setAcceptDrops(True) def dropEvent(self, ev): if not ev.source() and ev.mimeData().hasUrls(): filename = ev.mimeData().urls()[0].toLocalFile() if filename: ev.accept() from . import import_export import_export.load(filename, self) def dragEnterEvent(self, ev): if not ev.source() and ev.mimeData().hasUrls(): ev.accept() def translateUI(self): try: self.searchEntry.setPlaceholderText(_("Search...")) except AttributeError: pass # not in Qt 4.6 shortcut = lambda a: a.shortcut().toString(QKeySequence.NativeText) self.menuButton.setText(_("&Menu")) self.addAction_.setText(_("&Add...")) self.addAction_.setToolTip( _("Add a new snippet. ({key})").format(key=shortcut(self.addAction_))) self.editAction.setText(_("&Edit...")) self.editAction.setToolTip( _("Edit the current snippet. ({key})").format(key=shortcut(self.editAction))) self.shortcutAction.setText(_("Configure Keyboard &Shortcut...")) self.deleteAction.setText(_("&Remove")) self.deleteAction.setToolTip(_("Remove the selected snippets.")) self.applyAction.setText(_("A&pply")) self.applyAction.setToolTip(_("Apply the current snippet.")) self.importAction.setText(_("&Import...")) self.importAction.setToolTip(_("Import snippets from a file.")) self.exportAction.setText(_("E&xport...")) self.exportAction.setToolTip(_("Export snippets to a file.")) self.restoreAction.setText(_("Restore &Built-in Snippets...")) self.restoreAction.setToolTip( _("Restore deleted or changed built-in snippets.")) self.helpAction.setText(_("&Help")) self.searchEntry.setToolTip(_( "Enter text to search in the snippets list.\n" "See \"What's This\" for more information.")) self.searchEntry.setWhatsThis(''.join(map("<p>{0}</p>\n".format, ( _("Enter text to search in the snippets list, and " "press Enter to apply the currently selected snippet."), _("If the search text fully matches the value of the '{name}' variable " "of a snippet, that snippet is selected.").format(name="name"), _("If the search text starts with a colon ':', the rest of the " "search text filters snippets that define the given variable. " "After a space a value can also be entered, snippets will then " "match if the value of the given variable contains the text after " "the space."), _("E.g. entering {menu} will show all snippets that are displayed " "in the insert menu.").format(menu="<code>:menu</code>"), )))) def sizeHint(self): return self.parent().mainwindow().size() / 4 def readSettings(self): data = textformats.formatData('editor') self.textView.setFont(data.font) self.textView.setPalette(data.palette()) def showContextMenu(self, pos): """Called when the user right-clicks the tree view.""" self.menuButton.menu().popup(self.treeView.viewport().mapToGlobal(pos)) def slotReturnPressed(self): """Called when the user presses Return in the search entry. Applies current snippet.""" name = self.currentSnippet() if name: view = self.parent().mainwindow().currentView() insert.insert(name, view) self.parent().hide() # make configurable? view.setFocus() def slotEscapePressed(self): """Called when the user presses ESC in the search entry. Hides the panel.""" self.parent().hide() self.parent().mainwindow().currentView().setFocus() def slotDoubleClicked(self, index): name = self.treeView.model().name(index) view = self.parent().mainwindow().currentView() insert.insert(name, view) def slotAdd(self): """Called when the user wants to add a new snippet.""" edit.Edit(self, None) def slotEdit(self): """Called when the user wants to edit a snippet.""" name = self.currentSnippet() if name: edit.Edit(self, name) def slotShortcut(self): """Called when the user selects the Configure Shortcut action.""" from widgets import shortcuteditdialog name = self.currentSnippet() if name: collection = self.parent().snippetActions action = actions.action(name, None, collection) default = collection.defaults().get(name) mgr = actioncollectionmanager.manager(self.parent().mainwindow()) cb = mgr.findShortcutConflict dlg = shortcuteditdialog.ShortcutEditDialog(self, cb, (collection, name)) if dlg.editAction(action, default): mgr.removeShortcuts(action.shortcuts()) collection.setShortcuts(name, action.shortcuts()) self.treeView.update() def slotDelete(self): """Called when the user wants to delete the selected rows.""" rows = sorted(set(i.row() for i in self.treeView.selectedIndexes()), reverse=True) if rows: for row in rows: name = self.treeView.model().names()[row] self.parent().snippetActions.setShortcuts(name, []) self.treeView.model().removeRow(row) self.updateFilter() def slotApply(self): """Called when the user clicks the apply button. Applies current snippet.""" name = self.currentSnippet() if name: view = self.parent().mainwindow().currentView() insert.insert(name, view) def slotImport(self): """Called when the user activates the import action.""" filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files")) caption = app.caption(_("dialog title", "Import Snippets")) filename = None filename = QFileDialog.getOpenFileName(self, caption, filename, filetypes)[0] if filename: from . import import_export import_export.load(filename, self) def slotExport(self): """Called when the user activates the export action.""" allrows = [row for row in range(model.model().rowCount()) if not self.treeView.isRowHidden(row, QModelIndex())] selectedrows = [i.row() for i in self.treeView.selectedIndexes() if i.column() == 0 and i.row() in allrows] names = self.treeView.model().names() names = [names[row] for row in selectedrows or allrows] filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files")) n = len(names) caption = app.caption(_("dialog title", "Export {num} Snippet", "Export {num} Snippets", n).format(num=n)) filename = QFileDialog.getSaveFileName(self, caption, None, filetypes)[0] if filename: from . import import_export try: import_export.save(names, filename) except (IOError, OSError) as e: QMessageBox.critical(self, _("Error"), _( "Can't write to destination:\n\n{url}\n\n{error}").format( url=filename, error=e.strerror)) def slotRestore(self): """Called when the user activates the Restore action.""" from . import restore dlg = restore.RestoreDialog(self) dlg.setWindowModality(Qt.WindowModal) dlg.populate() dlg.show() dlg.finished.connect(dlg.deleteLater) def slotHelp(self): """Called when the user clicks the small help button.""" userguide.show("snippets") def currentSnippet(self): """Returns the name of the current snippet if it is visible.""" row = self.treeView.currentIndex().row() if row != -1 and not self.treeView.isRowHidden(row, QModelIndex()): return self.treeView.model().names()[row] def updateFilter(self): """Called when the text in the entry changes, updates search results.""" text = self.searchEntry.text() ltext = text.lower() filterVars = text.startswith(':') if filterVars: try: fvar, fval = text[1:].split(None, 1) fhide = lambda v: v.get(fvar) in (True, None) or fval not in v.get(fvar) except ValueError: fvar = text[1:].strip() fhide = lambda v: not v.get(fvar) for row in range(self.treeView.model().rowCount()): name = self.treeView.model().names()[row] nameid = snippets.get(name).variables.get('name', '') if filterVars: hide = fhide(snippets.get(name).variables) elif nameid == text: i = self.treeView.model().createIndex(row, 0) self.treeView.selectionModel().setCurrentIndex(i, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows) hide = False elif nameid.lower().startswith(ltext): hide = False elif ltext in snippets.title(name).lower(): hide = False else: hide = True self.treeView.setRowHidden(row, QModelIndex(), hide) self.updateText() def updateText(self): """Called when the current snippet changes.""" name = self.currentSnippet() self.textView.clear() if name: s = snippets.get(name) self.highlighter.setPython('python' in s.variables) self.textView.setPlainText(s.text) def updateColumnSizes(self): self.treeView.resizeColumnToContents(0) self.treeView.resizeColumnToContents(1)
class Win(QMainWindow): def __init__(self, options): super(Win, self).__init__() self.db = dbtag.Db() self.db.open(options.db) self.db.create_tables() self.rootPath = options.filespath self._init_widgets() self._init_more() def _init_widgets(self): bigsplitter = QSplitter(Qt.Horizontal, self) self.setCentralWidget(bigsplitter) leftsplitter = QSplitter(Qt.Vertical, self) self.tabWidget = QTabWidget(self) self.tagChooser = TagChooser(self.db) self.dirChooser = QTreeView(self) self.tabWidget.addTab(self.dirChooser, 'Dir') self.tabWidget.addTab(self.tagChooser, 'Tags') self.tagEditor = TagEditor(self.db) self.imageList = ImageList() leftsplitter.addWidget(self.tabWidget) leftsplitter.addWidget(self.tagEditor) bigsplitter.addWidget(leftsplitter) bigsplitter.addWidget(self.imageList) self.viewer = None def _init_more(self): self.setWindowTitle('Tags4') self.dirModel = QFileSystemModel() self.dirModel.setFilter(QDir.AllDirs | QDir.Drives | QDir.Hidden | QDir.NoDotAndDotDot) qidx = self.dirModel.setRootPath(self.rootPath) self.dirChooser.setModel(self.dirModel) self.dirChooser.setRootIndex(qidx) self.dirChooser.clicked.connect(self.browseSelectedDir) self.imageList.itemSelectionChanged.connect(self._editTagsItems) self.imageList.itemDoubleClicked.connect(self._spawnViewerItem) self.imageList.setSelectionMode(QAbstractItemView.ExtendedSelection) self.tabWidget.currentChanged.connect(self._tabSelected) self.tagChooser.changed.connect(self.browseSelectedTags) def editTags(self, path): self.tagEditor.setFile(path) def editTagsItems(self, paths): self.tagEditor.setFiles(paths) def spawnViewer(self, files, currentFile): viewer = ImageViewer(self.db, parent=self) viewer.spawn(files, currentFile) @Slot() def _editTagsItems(self): self.editTagsItems([qitem.getPath() for qitem in self.imageList.selectedItems()]) @Slot(QListWidgetItem) def _spawnViewerItem(self, qitem): self.spawnViewer(self.imageList.getFiles(), qitem.getPath()) @Slot() def browseSelectedDir(self): path = self.dirModel.filePath(self.dirChooser.currentIndex()) if not path: return files = [os.path.join(path, f) for f in os.listdir(path)] files = [f for f in files if os.path.isfile(f)] files.sort() self.imageList.setFiles(files) @Slot() def browseSelectedTags(self): self.imageList.setFiles(self.tagChooser.matchingFiles()) @Slot(int) def _tabSelected(self, idx): if idx == 0: self.browseSelectedDir() else: self.browseSelectedTags() def browsePath(self, path): self.imageList.setFiles(os.path.join(path, f) for f in os.listdir(path))
class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.setMinimumSize(QSize(480, 80)) self.setWindowTitle("PyQtSample") central_widget = QWidget(self) self.setCentralWidget(central_widget) hbox_layout = QHBoxLayout(self) central_widget.setLayout(hbox_layout) buttons_background = QWidget(self) buttons_layout = QHBoxLayout() buttons_background.setLayout(buttons_layout) self.add_button = QPushButton("Add") buttons_layout.addWidget(self.add_button) self.remove_button = QPushButton("Remove") buttons_layout.addWidget(self.remove_button) tree_layout = QVBoxLayout(self) self.tree_view = QTreeView() self.tree_view.header().hide() self.tree_view.setMaximumWidth(300) self.tree_model = ObjectsModel.TreeModel() self.tree_view.setModel(self.tree_model) tree_layout.addWidget(buttons_background) tree_layout.addWidget(self.tree_view) hbox_layout.addLayout(tree_layout) self.graphics_view = QGraphicsView() self.scene = QGraphicsScene() self.graphics_view.setScene(self.scene) hbox_layout.addWidget(self.graphics_view) self.properties_view = QTableView() self.properties_view.setMaximumWidth(300) self.properties_model = PropertiesModel.TableModel() self.properties_view.setModel(self.properties_model) hbox_layout.addWidget(self.properties_view) self.init_menu() self.test() self.connectSignals() def connectSignals(self): self.add_button.clicked.connect(self.onAddClicked) self.remove_button.clicked.connect(self.onRemoveClicked) self.tree_view.clicked.connect(self.onClicked) self.properties_model.dataChanged.connect(self.onPropertyChanged) def onPropertyChanged(self): self.rebuildModel() def rebuildModel(self): #save index hierarhy indexes = [] tmp_index = self.tree_view.currentIndex() indexes.append(tmp_index) while tmp_index.parent().isValid(): indexes.append(tmp_index.parent()) tmp_index = tmp_index.parent() self.tree_model.initRoot(self.items) self.tree_view.expandAll() if len(indexes) == 0: self.onClicked(self.tree_model.index(0, 0)) else: last_index = indexes.pop(-1) index = self.tree_model.index(last_index.row(), last_index.column()) #restore index hierarchy while len(indexes) > 0: last_index = indexes.pop(-1) index = self.tree_model.index(last_index.row(), last_index.column(), index) if index.isValid(): self.onClicked(index) else: self.onClicked(self.tree_model.index(0, 0)) def init_menu(self): exit_action = QAction("&Exit", self) exit_action.setShortcut('Ctrl+Q') exit_action.triggered.connect(qApp.quit) file_menu = self.menuBar().addMenu("&File") file_menu.addAction(QAction("Open", self)) file_menu.addAction(QAction("Save", self)) file_menu.addAction(QAction("SaveAs", self)) file_menu.addSeparator() file_menu.addAction(exit_action) def appendObjectOnScene(self, object=DataStructures.Object): if object.rect.isValid(): self.scene.addRect(object.rect, object.color) for child in object.childrens: self.appendObjectOnScene(child) def onClicked(self, index): object = index.data(Qt.UserRole + 1) self.properties_model.initProperties(object) self.scene.clear() self.appendObjectOnScene(object) self.tree_view.setCurrentIndex(index) def onAddClicked(self): index = self.tree_view.currentIndex() print(index) object = index.data(Qt.UserRole + 1) print(object.description()) object.add_children(DataStructures.Object("New item")) self.rebuildModel() def onRemoveClicked(self): index = self.tree_view.currentIndex() object = index.data(Qt.UserRole + 1) if not object.parent: self.items.remove(object) else: object.parent.childrens.remove(object) self.rebuildModel() def test(self): self.items = [] static = DataStructures.Object( "Static", DataStructures.createRect(0, 0, 800, 200)) static.add_children(DataStructures.Object("child_1")) static.add_children(DataStructures.Object("child_2")) static.add_children(DataStructures.Object("child_3")) static.color = QColor(200, 0, 0).name() static.childrens[0].add_children( DataStructures.Object("child_1.1", DataStructures.createRect(40, 40, 80, 40))) self.items.append(static) dynamic = DataStructures.Object( "Dynamic", DataStructures.createRect(0, 0, 200, 800)) dynamic.add_children(DataStructures.Object("child_1")) dynamic.add_children(DataStructures.Object("child_2")) dynamic.add_children(DataStructures.Object("child_3")) dynamic.childrens[2].add_children(DataStructures.Object("child_2.1")) dynamic.color = QColor(0, 0, 200).name() self.items.append(dynamic) self.rebuildModel()
class AddToProject(QDialog): """Dialog to let the user choose one of the folders from the opened proj""" def __init__(self, projects, parent=None): super(AddToProject, self).__init__(parent) #pathProjects must be a list self._projects = projects self.setWindowTitle(translations.TR_ADD_FILE_TO_PROJECT) self.pathSelected = '' vbox = QVBoxLayout(self) hbox = QHBoxLayout() self._list = QListWidget() for project in self._projects: self._list.addItem(project.name) self._list.setCurrentRow(0) self._tree = QTreeView() #self._tree.header().setHidden(True) self._tree.setSelectionMode(QTreeView.SingleSelection) self._tree.setAnimated(True) self.load_tree(self._projects[0]) hbox.addWidget(self._list) hbox.addWidget(self._tree) vbox.addLayout(hbox) hbox2 = QHBoxLayout() btnAdd = QPushButton(translations.TR_ADD_HERE) btnCancel = QPushButton(translations.TR_CANCEL) hbox2.addWidget(btnCancel) hbox2.addWidget(btnAdd) vbox.addLayout(hbox2) btnCancel.connect(self.close) btnAdd.connect(self._select_path) self._list.currentItemChanged['QTreeWidgetItem*', 'QTreeWidgetItem*'].connect(self._project_changed) def _project_changed(self, item, previous): #FIXME, this is not being called, at least in osx for each_project in self._projects: if each_project.name == item.text(): self.load_tree(each_project) def load_tree(self, project): """Load the tree view on the right based on the project selected.""" qfsm = QFileSystemModel() qfsm.setRootPath(project.path) load_index = qfsm.index(qfsm.rootPath()) qfsm.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) qfsm.setNameFilterDisables(False) pext = ["*{0}".format(x) for x in project.extensions] qfsm.setNameFilters(pext) self._tree.setModel(qfsm) self._tree.setRootIndex(load_index) t_header = self._tree.header() t_header.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) t_header.setSectionResizeMode(0, QHeaderView.Stretch) t_header.setStretchLastSection(False) t_header.setClickable(True) self._tree.hideColumn(1) # Size self._tree.hideColumn(2) # Type self._tree.hideColumn(3) # Modification date #FIXME: Changing the name column's title requires some magic #Please look at the project tree def _select_path(self): """Set pathSelected to the folder selected in the tree.""" path = self._tree.model().filePath(self._tree.currentIndex()) if path: self.pathSelected = path self.close()
class FileWidget(QWidget): def __init__(self): super().__init__() # QWidget部件是PyQt5所有用户界面对象的基类。他为QWidget提供默认构造函数。默认构造函数没有父类。 # 创建一个文件系统模型 self.file_model = QFileSystemModel() # 设置目录为当前工作目录 self.file_model.setRootPath(QDir.currentPath()) # 创建树视图,构建文件目录视图 self.treeview = QTreeView() # 绑定此文件模型 self.treeview.setModel(self.file_model) ''' 设置当前勾结点索引为当前工作目录 如果想从整个文件系统根节点开始浏览视图, 简单删掉此行即可 ''' self.treeview.setRootIndex(self.file_model.index(QDir.currentPath())) # 头部显示排序戳 self.treeview.header().setSortIndicatorShown(True) # 创建右键菜单 self.treeview.setContextMenuPolicy(Qt.CustomContextMenu) # point = self.treeview.pos() self.treeview.customContextMenuRequested.connect(self.generateMenu) ''' # 底部按钮布局 self.mkdirButton = QPushButton("Make Directory...") self.rmButton = QPushButton("Remove") buttonLayout = QHBoxLayout() buttonLayout.addWidget(self.mkdirButton) buttonLayout.addWidget(self.rmButton) ''' # 文件管理界面布局 layout = QVBoxLayout() layout.addWidget(self.treeview) # layout.addLayout(buttonLayout) # resize()方法调整窗口的大小。600px宽300px高 self.resize(600, 300) # move()方法移动窗口在屏幕上的位置到x = 300,y = 300坐标。 self.move(300, 300) # 设置窗口的标题 self.setWindowTitle('File Manage') # 设置窗口的图标 self.setWindowIcon(QIcon('File-Explorer.png')) self.setLayout(layout) # 生成右键菜单 def generateMenu(self, position): # 索引默认值 row_num = -1 # 遍历确定行号 for i in self.treeview.selectionModel().selection().indexes(): row_num = i.row() # 保证选中有效项 if row_num != -1: # 创建右键菜单 menu = QMenu() # 提供删除和创建文件/文件夹选项 item1 = menu.addAction("Delete") item2 = menu.addAction("NewDirectory") # 在光标处显示执行菜单 action = menu.exec_(self.treeview.mapToGlobal(position)) if action == item1: #弹出消息框确认此次删除操作 res = self.msgbox() if res: self.delete() else: return elif action == item2: self.mkdirectory() else: return else: return # 删除选定文件/文件夹 def delete(self): index = self.treeview.currentIndex() if index.isValid(): fileInfo = self.file_model.fileInfo(index) if fileInfo.isDir(): self.file_model.rmdir(index) else: self.file_model.remove(index) # 确认框 def msgbox(self): msgBox = QMessageBox() msgBox.setWindowTitle("Warning") msgBox.setText("Delete the file/dir you selected?") msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msgBox.setDefaultButton(QMessageBox.No) # button = QMessageBox.question("Warning", "delete the file/dir?", # QMessageBox.Yes | QMessageBox.No, QMessageBox.No) button = msgBox.exec_() if button == QMessageBox.No: return False elif button == QMessageBox.Yes: return True # 创建文件夹 def mkdirectory(self): index = self.treeview.rootIndex() if index.isValid(): # 弹出输入框录入文件名 dirname, ok = QInputDialog.getText(self, "File Name", "Input an unique dir name:") if ok: self.file_model.mkdir(index, dirname) else: return def keyPressEvent(self, event): if event.key() == Qt.Key_F3: self.close()
class Ui_PoloshirtWindow(QWidget): POLOSHİRTID, POLOSHİRTISIM, POLOSHİRTFIYAT, RESIMURL = range(4) def setupUi(self, PoloshirtWindow): PoloshirtWindow.setObjectName("PoloshirtWindow") PoloshirtWindow.resize(997, 704) PoloshirtWindow.setLayoutDirection(QtCore.Qt.LeftToRight) self.centralwidget = QtWidgets.QWidget(PoloshirtWindow) self.centralwidget.setObjectName("centralwidget") self.gridLayoutWidget = QtWidgets.QWidget(self.centralwidget) self.gridLayoutWidget.setGeometry(QtCore.QRect(60, 120, 791, 271)) self.gridLayoutWidget.setObjectName("gridLayoutWidget") self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget) self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setObjectName("gridLayout") # bukısım c*k onemlı self.treeWidget = QTreeView(self.gridLayoutWidget) self.treeWidget.setEditTriggers( QtWidgets.QTableWidget.NoEditTriggers) # QtWidgets.QAbstractItemView.DoubleClicked | QtWidgets.QAbstractItemView.EditKeyPressed | QtWidgets.QAbstractItemView.SelectedClicked) self.treeWidget.setObjectName("treeWidget") self.gridLayout.addWidget(self.treeWidget, 0, 0, 2, 1) self.gridLayoutWidget_3 = QtWidgets.QWidget(self.centralwidget) self.gridLayoutWidget_3.setGeometry(QtCore.QRect(60, 390, 341, 231)) self.gridLayoutWidget_3.setObjectName("gridLayoutWidget_3") self.gridLayout_3 = QtWidgets.QGridLayout(self.gridLayoutWidget_3) self.gridLayout_3.setContentsMargins(0, 0, 0, 0) self.gridLayout_3.setObjectName("gridLayout_3") self.poloshirtisimlineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3) font = QtGui.QFont() font.setPointSize(10) self.poloshirtisimlineEdit.setFont(font) self.poloshirtisimlineEdit.setObjectName("poloshirtisimlineEdit") self.gridLayout_3.addWidget(self.poloshirtisimlineEdit, 1, 1, 1, 1) self.poloshirtisimlabel = QtWidgets.QLabel(self.gridLayoutWidget_3) font = QtGui.QFont() font.setFamily("Microsoft Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtisimlabel.setFont(font) self.poloshirtisimlabel.setObjectName("poloshirtisimlabel") self.gridLayout_3.addWidget(self.poloshirtisimlabel, 1, 0, 1, 1) self.poloshirtfyatlabel = QtWidgets.QLabel(self.gridLayoutWidget_3) font = QtGui.QFont() font.setFamily("Microsoft Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtfyatlabel.setFont(font) self.poloshirtfyatlabel.setObjectName("poloshirtfyatlabel") self.gridLayout_3.addWidget(self.poloshirtfyatlabel, 2, 0, 1, 1) self.poloshirtidlineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3) self.poloshirtidlineEdit.setEnabled(False) font = QtGui.QFont() font.setPointSize(10) self.poloshirtidlineEdit.setFont(font) self.poloshirtidlineEdit.setObjectName("poloshirtidlineEdit") self.gridLayout_3.addWidget(self.poloshirtidlineEdit, 0, 1, 1, 1) self.poloshirtidlabel = QtWidgets.QLabel(self.gridLayoutWidget_3) font = QtGui.QFont() font.setFamily("Microsoft Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtidlabel.setFont(font) self.poloshirtidlabel.setObjectName("poloshirtidlabel") self.gridLayout_3.addWidget(self.poloshirtidlabel, 0, 0, 1, 1) self.poloshirtfiyatlineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3) font = QtGui.QFont() font.setPointSize(10) self.poloshirtfiyatlineEdit.setFont(font) self.poloshirtfiyatlineEdit.setObjectName("poloshirtfiyatlineEdit") self.gridLayout_3.addWidget(self.poloshirtfiyatlineEdit, 2, 1, 1, 1) self.poloshirtresimurllineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3) font = QtGui.QFont() font.setPointSize(10) self.poloshirtresimurllineEdit.setFont(font) self.poloshirtresimurllineEdit.setObjectName("poloshirtresimurllineEdit") self.gridLayout_3.addWidget(self.poloshirtresimurllineEdit, 3, 1, 1, 1) self.poloshirtresimurllabel = QtWidgets.QLabel(self.gridLayoutWidget_3) font = QtGui.QFont() font.setFamily("Microsoft Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtresimurllabel.setFont(font) self.poloshirtresimurllabel.setObjectName("poloshirtresimurllabel") self.gridLayout_3.addWidget(self.poloshirtresimurllabel, 3, 0, 1, 1) self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget) self.verticalLayoutWidget.setGeometry(QtCore.QRect(419, 410, 211, 211)) self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) self.verticalLayout.setContentsMargins(0, 0, 0, 0) self.verticalLayout.setObjectName("verticalLayout") self.poloshirteklebuton = QtWidgets.QPushButton(self.verticalLayoutWidget) font = QtGui.QFont() font.setFamily("MS Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirteklebuton.setFont(font) self.poloshirteklebuton.setObjectName("poloshirteklebuton") self.verticalLayout.addWidget(self.poloshirteklebuton) self.poloshirtduzenlebuton = QtWidgets.QPushButton(self.verticalLayoutWidget) font = QtGui.QFont() font.setFamily("MS Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtduzenlebuton.setFont(font) self.poloshirtduzenlebuton.setObjectName("poloshirtduzenlebuton") self.verticalLayout.addWidget(self.poloshirtduzenlebuton) self.poloshirtsilbuton = QtWidgets.QPushButton(self.verticalLayoutWidget) font = QtGui.QFont() font.setFamily("MS Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) self.poloshirtsilbuton.setFont(font) self.poloshirtsilbuton.setObjectName("poloshirtsilbuton") self.verticalLayout.addWidget(self.poloshirtsilbuton) self.formLayoutWidget = QtWidgets.QWidget(self.centralwidget) self.formLayoutWidget.setGeometry(QtCore.QRect(650, 500, 271, 31)) self.formLayoutWidget.setObjectName("formLayoutWidget") self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget) self.formLayout.setContentsMargins(0, 0, 0, 0) self.formLayout.setObjectName("formLayout") font = QtGui.QFont() font.setFamily("MS Sans Serif") font.setPointSize(10) font.setBold(True) font.setWeight(75) font = QtGui.QFont() font.setPointSize(10) self.label = QtWidgets.QLabel(self.centralwidget) self.label.setGeometry(QtCore.QRect(190, 20, 561, 51)) font = QtGui.QFont() font.setFamily("Myanmar Text") font.setPointSize(12) font.setBold(True) font.setWeight(75) self.label.setFont(font) self.label.setFrameShape(QtWidgets.QFrame.NoFrame) self.label.setFrameShadow(QtWidgets.QFrame.Plain) self.label.setObjectName("label") self.label_3 = QtWidgets.QLabel(self.centralwidget) self.label_3.setGeometry(QtCore.QRect(350, 70, 170, 51)) font = QtGui.QFont() font.setFamily("Myanmar Text") font.setPointSize(18) font.setBold(True) font.setWeight(75) self.label_3.setFont(font) self.label_3.setFrameShape(QtWidgets.QFrame.NoFrame) self.label_3.setFrameShadow(QtWidgets.QFrame.Plain) self.label_3.setObjectName("label_3") PoloshirtWindow.setCentralWidget(self.centralwidget) self.statusbar = QtWidgets.QStatusBar(PoloshirtWindow) self.statusbar.setObjectName("statusbar") PoloshirtWindow.setStatusBar(self.statusbar) self.actionPoloshirt = QtWidgets.QAction(PoloshirtWindow) self.actionPoloshirt.setObjectName("actionPoloshirt") self.actionPoloshirt = QtWidgets.QAction(PoloshirtWindow) self.actionPoloshirt.setObjectName("actionPoloshirt") self.actionPoloshirt = QtWidgets.QAction(PoloshirtWindow) self.actionPoloshirt.setObjectName("actionPoloshirt") self.actioncgiyim = QtWidgets.QAction(PoloshirtWindow) self.actioncgiyim.setObjectName("actioncgiyim") self.retranslateUi(PoloshirtWindow) QtCore.QMetaObject.connectSlotsByName(PoloshirtWindow) #SORT SIRALAMA self.treeWidget.setSortingEnabled(True) #only int self.onlyInt = QIntValidator() self.poloshirtfiyatlineEdit.setValidator(self.onlyInt) # bu kısmı kullan nolur model = self.treetablemodelyaratma(self) self.treetablemodelyaratma2(model) self.treeWidget.setModel(model) # Buton clickleri self.treeWidget.doubleClicked.connect(self.on_doubleClicked) self.poloshirteklebuton.clicked.connect(self.eklebuton) self.poloshirtsilbuton.clicked.connect(self.silbuton) self.poloshirtduzenlebuton.clicked.connect(self.duzenlebuton) def treetablemodelyaratma(self, parent): model = QStandardItemModel(0, 4, parent) model.setHeaderData(self.POLOSHİRTID, Qt.Horizontal, "Poloshirt id") model.setHeaderData(self.POLOSHİRTISIM, Qt.Horizontal, "Poloshirt isim") model.setHeaderData(self.POLOSHİRTFIYAT, Qt.Horizontal, "Poloshirt fiyat") model.setHeaderData(self.RESIMURL, Qt.Horizontal, "Poloshirt Resimurl") return model def on_doubleClicked(self, index): item = self.treeWidget.currentIndex() item2 = item.model().itemFromIndex(index).text() self.poloshirtidlineEdit.setText(item2) # tabloyu update etme yöntemı self.updateview() def eklebuton(self): if (self.poloshirtfiyatlineEdit.text() =="" or self.poloshirtisimlineEdit.text()=="" or self.poloshirtresimurllineEdit.text()==""): return else: conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi") cursor = conn.cursor(pymysql.cursors.DictCursor) query = "INSERT INTO `poloshirt` (poloshirt_isim, poloshirt_fiyat,poloshirt_resimurl) VALUES (%s,%s,%s)" val = (self.poloshirtisimlineEdit.text(), self.poloshirtfiyatlineEdit.text(), self.poloshirtresimurllineEdit.text()) cursor.execute(query, val) conn.commit() # tabloyu update etme yöntemı self.updateview() self.poloshirtfiyatlineEdit.clear() self.poloshirtisimlineEdit.clear() self.poloshirtresimurllineEdit.clear() self.poloshirtidlineEdit.clear() def silbuton(self): if (self.poloshirtidlineEdit.text()==""): return else: conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi") cursor = conn.cursor(pymysql.cursors.DictCursor) query = "DELETE FROM `poloshirt` where id=%s" val = (self.poloshirtidlineEdit.text()) cursor.execute(query,val) conn.commit() self.updateview() self.poloshirtidlineEdit.clear() def duzenlebuton(self): if (self.poloshirtidlineEdit.text()==""or self.poloshirtfiyatlineEdit.text() == "" or self.poloshirtisimlineEdit.text() == "" or self.poloshirtresimurllineEdit.text() == ""): return else: conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi") cursor = conn.cursor(pymysql.cursors.DictCursor) query = "UPDATE `poloshirt` SET poloshirt_isim=%s,poloshirt_fiyat=%s,poloshirt_resimurl=%s where id=%s" val = (self.poloshirtisimlineEdit.text(),self.poloshirtfiyatlineEdit.text(),self.poloshirtresimurllineEdit.text(),self.poloshirtidlineEdit.text()) cursor.execute(query, val) conn.commit() self.updateview() self.poloshirtidlineEdit.clear() self.poloshirtfiyatlineEdit.clear() self.poloshirtisimlineEdit.clear() self.poloshirtresimurllineEdit.clear() self.poloshirtidlineEdit.clear() def updateview(self): model = self.treetablemodelyaratma(self) self.treeWidget.setModel(model) self.treetablemodelyaratma2(model) def treetablemodelyaratma2(self, model): conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi") cursor = conn.cursor(pymysql.cursors.DictCursor) query = "SELECT * FROM `poloshirt`" cursor.execute(query) data = cursor.fetchall() for row in data: model.insertRow(0) model.setData(model.index(0, self.POLOSHİRTID), row['id']) model.setData(model.index(0, self.POLOSHİRTISIM), row['poloshirt_isim']) model.setData(model.index(0, self.POLOSHİRTFIYAT), row['poloshirt_fiyat']) model.setData(model.index(0, self.RESIMURL), row['poloshirt_resimurl']) def retranslateUi(self, PoloshirtWindow): _translate = QtCore.QCoreApplication.translate PoloshirtWindow.setWindowTitle(_translate("PoloshirtWindow", "Poloshirtwindow")) self.poloshirtisimlabel.setText(_translate("PoloshirtWindow", "Poloshirt isim:")) self.poloshirtfyatlabel.setText(_translate("PoloshirtWindow", "Poloshirt Fiyat:")) self.poloshirtidlabel.setText(_translate("PoloshirtWindow", "ID:")) self.poloshirtresimurllabel.setText(_translate("PoloshirtWindow", "Resim_Url")) self.poloshirteklebuton.setText(_translate("PoloshirtWindow", "Ekle")) self.poloshirtduzenlebuton.setText(_translate("PoloshirtWindow", "Düzenle")) self.poloshirtsilbuton.setText(_translate("PoloshirtWindow", "Sil")) self.label.setText(_translate("PoloshirtWindow", "TAYFUN TESKTİL ÜRÜN DÜZENLEME")) self.label_3.setText(_translate("PoloshirtWindow", "POLOSHİRT")) self.actionPoloshirt.setText(_translate("PoloshirtWindow", "Poloshirt")) self.actionPoloshirt.setText(_translate("PoloshirtWindow", "Poloshirt")) self.actionPoloshirt.setText(_translate("PoloshirtWindow", "Poloshirt")) self.actioncgiyim.setText(_translate("PoloshirtWindow", "İcgiyim"))
class _LocatorDialog(QDialog): """Locator widget and implementation """ def __init__(self, parent, commandClasses): QDialog.__init__(self, parent) self._terminated = False self._commandClasses = commandClasses self._createUi() self._loadingTimer = QTimer(self) self._loadingTimer.setSingleShot(True) self._loadingTimer.setInterval(200) self._loadingTimer.timeout.connect(self._applyLoadingCompleter) self._completerLoaderThread = _CompleterLoaderThread(self) self.finished.connect(self._terminate) self._command = None self._updateCurrentCommand() def _createUi(self): self.setWindowTitle(core.project().path().replace(os.sep, '/') or 'Locator') self.setLayout(QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(1) biggerFont = self.font() biggerFont.setPointSizeF(biggerFont.pointSizeF() * 2) self.setFont(biggerFont) self._edit = _CompletableLineEdit(self) self._edit.updateCurrentCommand.connect(self._updateCurrentCommand) self._edit.enterPressed.connect(self._onEnterPressed) self._edit.installEventFilter(self) # catch Up, Down self._edit.setFont(biggerFont) self.layout().addWidget(self._edit) self.setFocusProxy(self._edit) self._table = QTreeView(self) self._table.setFont(biggerFont) self._model = _CompleterModel() self._table.setModel(self._model) self._table.setItemDelegate(HTMLDelegate(self._table)) self._table.setRootIsDecorated(False) self._table.setHeaderHidden(True) self._table.clicked.connect(self._onItemClicked) self._table.setAlternatingRowColors(True) self._table.installEventFilter(self) # catch focus and give to the edit self.layout().addWidget(self._table) width = QFontMetrics(self.font()).width('x' * 64) # width of 64 'x' letters self.resize(width, width * 0.62) def _terminate(self): if not self._terminated: if self._command is not None: self._command.terminate() self._command = None self._edit.terminate() self._completerLoaderThread.terminate() if self._model: self._model.terminate() core.workspace().focusCurrentDocument() self._terminated = True def _updateCurrentCommand(self): """Try to parse line edit text and set current command """ if self._terminated: return newCommand = self._parseCurrentCommand() if newCommand is not self._command: if self._command is not None: self._command.updateCompleter.disconnect(self._updateCompletion) self._command.terminate() self._command = newCommand if self._command is not None: self._command.updateCompleter.connect(self._updateCompletion) self._updateCompletion() def _updateCompletion(self): """User edited text or moved cursor. Update inline and TreeView completion """ if self._command is not None: completer = self._command.completer() if completer is not None and completer.mustBeLoaded: self._loadingTimer.start() self._completerLoaderThread.loadCompleter(self._command, completer) else: self._applyCompleter(self._command, completer) else: self._applyCompleter(None, _HelpCompleter(self._commandClasses)) def _applyLoadingCompleter(self): """Set 'Loading...' message """ self._applyCompleter(None, StatusCompleter('<i>Loading...</i>')) def onCompleterLoaded(self, command, completer): """The method called from _CompleterLoaderThread when the completer is ready This code works in the GUI thread """ self._applyCompleter(command, completer) def _applyCompleter(self, command, completer): """Apply completer. Called by _updateCompletion or by thread function when Completer is constructed """ self._loadingTimer.stop() if command is not None: command.onCompleterLoaded(completer) if completer is None: completer = _HelpCompleter([command]) if self._edit.cursorPosition() == len(self._edit.text()): # if cursor at the end of text self._edit.setInlineCompletion(completer.inline()) self._model.setCompleter(completer) if completer.columnCount() > 1: self._table.resizeColumnToContents(0) self._table.setColumnWidth(0, self._table.columnWidth(0) + 20) # 20 px spacing between columns selItem = completer.autoSelectItem() if selItem: index = self._model.createIndex(selItem[0], selItem[1]) self._table.setCurrentIndex(index) def _onItemClicked(self, index): """Item in the TreeView has been clicked. Open file, if user selected it """ if self._command is not None: fullText = self._model.completer.getFullText(index.row()) if fullText is not None: self._command.onItemClicked(fullText) if self._tryExecCurrentCommand(): self.accept() return else: self._edit.setText(self._command.lineEditText()) self._updateCurrentCommand() self._edit.setFocus() def _onEnterPressed(self): """User pressed Enter or clicked item. Execute command, if possible """ if self._table.currentIndex().isValid(): self._onItemClicked(self._table.currentIndex()) else: self._tryExecCurrentCommand() def _tryExecCurrentCommand(self): if self._command is not None and self._command.isReadyToExecute(): self._command.execute() self.accept() return True else: return False def _chooseCommand(self, words): for cmd in self._commandClasses: if cmd.command == words[0]: return cmd, words[1:] isPath = words and (words[0].startswith('/') or words[0].startswith('./') or words[0].startswith('../') or words[0].startswith('~/') or words[0][1:3] == ':\\' or words[0][1:3] == ':/' ) isNumber = len(words) == 1 and all([c.isdigit() for c in words[0]]) def matches(cmd): if isPath: return cmd.isDefaultPathCommand elif isNumber: return cmd.isDefaultNumericCommand else: return cmd.isDefaultCommand for cmd in self._commandClasses: if matches(cmd): return cmd, words def _parseCurrentCommand(self): """ Parse text and try to get (command, completable word index) Return None if failed to parse """ # Split line text = self._edit.commandText() words = splitLine(text) if not words: return None # Find command cmdClass, args = self._chooseCommand(words) if isinstance(self._command, cmdClass): command = self._command else: command = cmdClass() # Try to make command object try: command.setArgs(args) except InvalidCmdArgs: return None else: return command def eventFilter(self, obj, event): if obj is self._edit: if event.type() == QEvent.KeyPress and \ event.key() in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown): return self._table.event(event) elif obj is self._table: if event.type() == QEvent.FocusIn: self._edit.setFocus() return True return False
class CatalogMainWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) # Структура главного окна # Создаем меню self.menu_bar = self.menuBar() # Создаем блоки меню # Блок меню 'Учет движения товаров' self.gma_menu = self.menu_bar.addMenu('Учет движения товаров') self.gma_menu.setEnabled(False) self.ro_open_btn = QAction(self) self.ro_open_btn.setText('Приходный ордер') self.wo_open_btn = QAction(self) self.wo_open_btn.setText('Расходный ордер') self.gma_menu.addAction(self.ro_open_btn) self.gma_menu.addAction(self.wo_open_btn) # Блок меню 'Отчеты' self.r_menu = self.menu_bar.addMenu('Отчеты') self.r_menu.setEnabled(False) self.tws_report_btn = QAction(self) self.tws_report_btn.setText('Операции с поставщиками') self.gis_report_btn = QAction(self) self.gis_report_btn.setText('Товары в наличии') self.r_menu.addAction(self.tws_report_btn) self.r_menu.addAction(self.gis_report_btn) # Блок меню 'Справочники' self.d_menu = self.menu_bar.addMenu('Справочники') self.d_menu.setEnabled(False) self.u_catalog_btn = QAction(self) self.u_catalog_btn.setText('Пользователи') self.r_catalog_btn = QAction(self) self.r_catalog_btn.setText('Права пользователей') self.c_catalog_btn = QAction(self) self.c_catalog_btn.setText('Категории') self.n_catalog_btn = QAction(self) self.n_catalog_btn.setText('Номенклатура товаров') self.un_catalog_btn = QAction(self) self.un_catalog_btn.setText('Единицы измерения') self.s_catalog_btn = QAction(self) self.s_catalog_btn.setText('Поставщики') self.p_catalog_btn = QAction(self) self.p_catalog_btn.setText('Должности') self.e_catalog_btn = QAction(self) self.e_catalog_btn.setText('Сотрудники') self.d_menu.addAction(self.u_catalog_btn) self.d_menu.addAction(self.r_catalog_btn) self.d_menu.addAction(self.c_catalog_btn) self.d_menu.addAction(self.n_catalog_btn) self.d_menu.addAction(self.un_catalog_btn) self.d_menu.addAction(self.s_catalog_btn) self.d_menu.addAction(self.p_catalog_btn) self.d_menu.addAction(self.e_catalog_btn) # Верхний виджет с полным путем до файла БД self.db_lbl = QLabel() self.db_lbl.setText('Путь до БД:') self.db_lbl.setStyleSheet("border-style: none;" "font-size: 10pt;") self.db_path_lbl = QLineEdit() self.db_path_lbl.setStyleSheet( "background-color: white;" "font-size: 10pt;" "color: green;") self.db_path_lbl.setFixedSize(700, 25) self.db_path_lbl.setEnabled(False) self.db_path_btn = QPushButton('...') self.db_path_btn.setFixedSize(25, 25) self.db_path_btn.setStyleSheet( "border-width: 1px;" "border-style: solid;" "border-color: dimgray;" "border-radius: 5px;" "background-color: gainsboro;") self.db_path_btn.clicked.connect(self.on_db_path_btn_clicked) self.tdw = QDockWidget() self.tdw.setFixedHeight(65) self.tdw.setFeatures(self.tdw.NoDockWidgetFeatures) self.tdw_grid = QGridLayout() self.tdw_grid.setColumnStretch(3, 1) self.tdw_grid.addWidget(self.db_lbl) self.tdw_grid.addWidget(self.db_path_lbl) self.tdw_grid.addWidget(self.db_path_btn) self.tdw_frame = QFrame() self.tdw_frame.setStyleSheet( "background-color: ghostwhite;" "border-width: 0.5px;" "border-style: solid;" "border-color: silver;") self.tdw_frame.setLayout(self.tdw_grid) self.tdw.setWidget(self.tdw_frame) self.addDockWidget(QtCore.Qt.TopDockWidgetArea, self.tdw) # Левый виджет с окном каталога продукции self.ldw = QDockWidget() self.ldw.setFixedSize(500, 570) self.ldw.setFeatures(self.ldw.NoDockWidgetFeatures) self.ldw_tree = QTreeView() self.ldw_tree.setFixedSize(500, 530) self.ldw_tree.setHeaderHidden(True) self.ldw_tree_model = QStandardItemModel() self.ldw_tree.setModel(self.ldw_tree_model) self.ldw_tree.clicked.connect(self.on_ldw_tree_clicked) self.outf_scroll = QScrollArea() self.outf_scroll.setWidget(self.ldw_tree) self.ldw.setWidget(self.outf_scroll) self.ldw.setWindowTitle("Каталог продукции") # Центральный виджет с карточкой товара self.cdw = QDockWidget() self.cdw.setFeatures(self.cdw.NoDockWidgetFeatures) #self.setCentralWidget(self.cdw) # Нижний виджет со служебными сообщениями self.smdw = QDockWidget() self.smdw.setFixedHeight(140) self.smdw.setFeatures(self.smdw.NoDockWidgetFeatures) self.sm_list_widget = QListWidget() self.smdw.setWidget(self.sm_list_widget) self.smdw.setWindowTitle("Служебные сообщения") # Функции главного окна # Функция выбора файла базы данных def on_db_path_btn_clicked(self): global db_path db_path, _filter = QFileDialog.getOpenFileName( None, "Open Data File", '.', "(*.sqlite)") self.db_path_lbl.setText(db_path) self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.ldw) self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.smdw) # Создаем все таблицы, которые нужны con = QtSql.QSqlDatabase.addDatabase('QSQLITE') con.setDatabaseName(db_path) con.open() if 'rules' not in con.tables(): query = QtSql.QSqlQuery() query.exec( "CREATE TABLE rules(rule STRING UNIQUE, description STRING)") r1 = "admin" d1 = "Доступны все опции" query.exec( "INSERT INTO rules(rule, description) VALUES ('%s','%s')" % (r1, d1)) r2 = "Кладовщик" d2 = "Доступны опции кладовщика" query.exec( "INSERT INTO rules(rule, description) VALUES ('%s','%s')" % (r2, d2)) if 'nomenclature' not in con.tables(): query = QtSql.QSqlQuery() query.exec( "CREATE TABLE nomenclature(item_numb INTEGER UNIQUE PRIMARY KEY NOT NULL, \ item_name STRING NOT NULL, item_unit STRING REFERENCES units (unit) NOT NULL, \ item_cat STRING REFERENCES categories (category) NOT NULL, item_img BLOB)") if 'units' not in con.tables(): query = QtSql.QSqlQuery() query.exec("CREATE TABLE units(unit STRING PRIMARY KEY \ UNIQUE NOT NULL)") if 'categories' not in con.tables(): query = QtSql.QSqlQuery() query.exec( "CREATE TABLE categories(category STRING PRIMARY KEY UNIQUE \ NOT NULL, description STRING NOT NULL)") # Создаем таблицу 'users', если не существует if 'users' not in con.tables(): query = QtSql.QSqlQuery() query.exec( "CREATE TABLE users(id_user INTEGER PRIMARY KEY AUTOINCREMENT \ unique NOT NULL, \ login STRING UNIQUE NOT NULL, password STRING NOT NULL, \ name TEXT NOT NULL, \ rules STRING REFERENCES rules (rule) NOT NULL)") if 'suppliers' not in con.tables(): query = QtSql.QSqlQuery() query.exec("CREATE TABLE suppliers( \ id_supplier INTEGER UNIQUE PRIMARY KEY AUTOINCREMENT NOT NULL, \ supplier STRING UNIQUE NOT NULL, \ ownerchipform STRING NOT NULL, \ address STRING NOT NULL, \ phone STRING NOT NULL, \ email STRING NOT NULL \ )") if 'positions' not in con.tables(): query = QtSql.QSqlQuery() query.exec( "CREATE TABLE positions(position STRING PRIMARY KEY UNIQUE \ NOT NULL)") if 'employee' not in con.tables(): query = QtSql.QSqlQuery() query.exec( "CREATE TABLE employee(employee_id INTEGER UNIQUE PRIMARY KEY AUTOINCREMENT, \ fio STRING NOT NULL, \ position STRING REFERENCES positions (position) NOT NULL)") con.close() con = sqlite3.connect(db_path) cur = con.cursor() sql_cats_select = """\ SELECT DISTINCT item_cat FROM nomenclature """ cur.execute(sql_cats_select) arr = cur.fetchall() #print(arr) for cat in arr: sql_items_select = "SELECT item_name FROM nomenclature \ WHERE item_cat = ?" cur.execute(sql_items_select, [cat[0]]) items = cur.fetchall() #print(items) parent_item = QStandardItem(cat[0]) self.ldw_tree_model.appendRow(parent_item) j = 0 for item in items: child_item = QStandardItem(item[0]) parent_item.setChild(j, 0, child_item) j = j + 1 cur.close() con.close() self.gma_menu.setEnabled(True) self.r_menu.setEnabled(True) self.d_menu.setEnabled(True) self.ro_open_btn.triggered.connect( lambda: documents_class.on_ro_doc_btn( self, db_path)) self.wo_open_btn.triggered.connect( lambda: documents_class.on_wo_doc_btn( self, db_path)) self.gis_report_btn.triggered.connect( lambda: gis_report_class.on_gis_report_btn( self, db_path)) self.tws_report_btn.triggered.connect( lambda: tws_report_class.on_tws_report_btn( self, db_path)) self.u_catalog_btn.triggered.connect( lambda: directories_class.on_u_catalog_btn( self, db_path)) self.r_catalog_btn.triggered.connect( lambda: directories_class.on_r_catalog_btn( self, db_path)) self.c_catalog_btn.triggered.connect( lambda: directories_class.on_c_catalog_btn( self, db_path)) self.n_catalog_btn.triggered.connect( lambda: directories_class.on_n_catalog_btn( self, db_path)) self.un_catalog_btn.triggered.connect( lambda: directories_class.on_un_catalog_btn( self, db_path)) self.s_catalog_btn.triggered.connect( lambda: directories_class.on_s_catalog_btn( self, db_path)) self.p_catalog_btn.triggered.connect( lambda: directories_class.on_p_catalog_btn( self, db_path)) self.e_catalog_btn.triggered.connect( lambda: directories_class.on_e_catalog_btn( self, db_path)) # Функция выбора товара из каталога-дерева def on_ldw_tree_clicked(self): #print('вах') try: current_index = self.ldw_tree.currentIndex() file_name = self.ldw_tree.model().data(current_index) con = sqlite3.connect(db_path) cur = con.cursor() sql_item_select = "SELECT item_name FROM nomenclature \ WHERE item_name = ?" cur.execute(sql_item_select, [file_name]) item = cur.fetchone() if item: self.setCentralWidget(self.cdw) self.cdw.setWindowTitle("Карточка товара") item_card = item_card_class(self, db_path, file_name) self.cdw.setWidget(item_card) cur.close() con.close() except Exception as e: print('Ошибка:\n', traceback.format_exc())
class ClientList(QWidget): def __init__(self, data: ()): super().__init__() self.tree_view = QTreeView() self.model = QStandardItemModel() self.rootNode = self.model.invisibleRootItem() self.applyModel() buttons = QHBoxLayout() add = QPushButton("Add") add.clicked.connect(self.addClient) remove = QPushButton("Remove") remove.clicked.connect(self.removeClient) buttons.addWidget(add) buttons.addWidget(remove) layout = QVBoxLayout() layout.addLayout(buttons) layout.addWidget(self.tree_view) self.setLayout(layout) if data: self.setData(data) self.in_progress = False def addClient(self): branch = [QStandardItem(), QStandardItem()] branch[0].setText("/") branch[1].setText("client_") self.model.appendRow(branch) index = self.model.index(self.model.rowCount() - 1, 0) self.tree_view.setCurrentIndex(index) self.applyModel() def removeClient(self): index = self.tree_view.currentIndex() if index.isValid(): self.model.removeRow(index.row()) self.applyModel() def applyModel(self): self.model.setHeaderData(0, Qt.Horizontal, "Path") self.model.setHeaderData(1, Qt.Horizontal, "Client variable") self.tree_view.setModel(self.model) def getData(self): data = {} for row in range(self.model.rowCount()): path = self.model.data(self.model.index(row, 0)) client = self.model.data(self.model.index(row, 1)) if path and client: data[path] = client return data def setData(self, data): for path, client in data.items(): branch = [QStandardItem(), QStandardItem()] branch[0].setText(path) branch[1].setText(client) self.model.appendRow(branch) self.applyModel()
class FileBrowserWidget(QWidget): on_open = pyqtSignal(str) def __init__(self): super().__init__() self.initUI() def initUI(self): self.model = QFileSystemModel() self.rootFolder = '' self.model.setRootPath(self.rootFolder) self.tree = QTreeView() self.tree.setModel(self.model) self.tree.setAnimated(False) self.tree.setIndentation(20) self.tree.setSortingEnabled(True) self.tree.sortByColumn(0, 0) self.tree.setColumnWidth(0, 200) self.tree.setDragEnabled(True) self.tree.setWindowTitle("Dir View") self.tree.resize(640, 480) self.tree.doubleClicked.connect(self.onDblClick) self.tree.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.tree.customContextMenuRequested.connect( self.onCustomContextMenuRequested) windowLayout = QVBoxLayout() windowLayout.addWidget(self.tree) windowLayout.setContentsMargins(0, 0, 0, 0) self.setLayout(windowLayout) def onCustomContextMenuRequested(self, point): index = self.tree.indexAt(point) selectedFile = None selectedFolder = None ctx = QMenu("Context menu", self) if index.isValid(): file = self.model.fileInfo(index) selectedFile = file.absoluteFilePath() selectedFolder = selectedFile if file.isDir( ) else file.absolutePath() if file.isDir(): ctx.addAction( "Open in file manager", lambda: QDesktopServices.openUrl( QUrl.fromLocalFile(selectedFile))) if not file.isDir(): for wndTyp, meta in WindowTypes.types: text = 'Open with ' + meta.get('displayName', meta['name']) print(wndTyp, meta) ctx.addAction( QAction(text, self, statusTip=text, triggered=lambda dummy, meta=meta: navigate( "WINDOW", "Type=" + meta['name'], "FileName=" + selectedFile))) ctx.addSeparator() ctx.addAction("Set root folder ...", lambda: self.selectRootFolder(preselect=selectedFolder)) ctx.exec(self.tree.viewport().mapToGlobal(point)) def selectRootFolder(self, preselect=None): if preselect == None: preselect = self.rootFolder dir = QFileDialog.getExistingDirectory(self, "Set root folder", preselect) if dir != None: self.setRoot(dir) def setRoot(self, dir): self.rootFolder = dir self.model.setRootPath(dir) self.tree.setRootIndex(self.model.index(dir)) def onDblClick(self, index): if index.isValid(): file = self.model.fileInfo(index) if not file.isDir(): navigate("OPEN", "FileName=" + file.absoluteFilePath()) def saveState(self): if self.tree.currentIndex().isValid(): info = self.model.fileInfo(self.tree.currentIndex()) return {"sel": info.absoluteFilePath(), "root": self.rootFolder} def restoreState(self, state): try: self.setRoot(state["root"]) except: pass try: idx = self.model.index(state["sel"]) if idx.isValid(): self.tree.expand(idx) self.tree.setCurrentIndex(idx) self.tree.scrollTo(idx, QAbstractItemView.PositionAtCenter) except: pass
class ModelGui(QDialog): """The gui to start and manipulate the analysis model""" ANALYSIS, ID, NUMPOINTS = range(3) NO_PARENT_ID = 0xFFFFFFFF def __init__(self, analysis_event_queue, select, delete, load): super(ModelGui, self).__init__() self.analysis_event_queue = analysis_event_queue self.select_callback = select self.delete_callback = delete self.load_callback = load self.name = None self.label_name = None self.meta_name = None self.hsne_name = None self.title = 'Analysis hierarchy viewer' self.left = 10 self.top = 10 self.width = 1000 self.height = 400 self.init_ui() self.id_item = {} self.root_id = None def init_ui(self): """All the ui layout in one place. Note: Part of the ui is switchable depending on the selected demo type, see the ui_matrix""" self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.model = self.create_analysis_model(self) self.analysis_model = None self.top_scale = None self.thumb_size = (40, 40) # The analysis tree self.tree = QTreeView() self.tree.setSelectionBehavior(QAbstractItemView.SelectRows) self.tree.header().setSectionResizeMode(QHeaderView.ResizeToContents) self.tree.setIconSize(QSize(*self.thumb_size)) self.tree.setModel(self.model) self.tree.setAnimated(False) self.tree.setIndentation(20) self.tree.setSortingEnabled(True) self.tree.resize(640, 480) # Control layout controlLayout = QVBoxLayout() self.preconfigured_group = QGroupBox("Preconfigured") preconfigured_layout = QFormLayout(self) self.preconfigured_combo = QComboBox(self) self.preconfigured_combo.addItem('None', userData=None) for config in CONFIGS: self.preconfigured_combo.addItem(config.descriptor, userData=config) self.preconfigured_combo.currentIndexChanged[int].connect( self.on_preconfigured) preconfigured_layout.addRow(QLabel("Load preset demo:"), self.preconfigured_combo) self.preconfigured_group.setLayout(preconfigured_layout) controlLayout.addWidget(self.preconfigured_group) # Data type settings self.demo_type_group = QGroupBox("Data type") data_type_layout = QFormLayout(self) self.data_type_combo = QComboBox(self) self.data_type_combo.addItem("Image is a data point", DemoType.LABELLED_DEMO) self.data_type_combo.addItem("Point and metadata", DemoType.POINT_DEMO) self.data_type_combo.addItem("Hyperspectral image", DemoType.HYPERSPECTRAL_DEMO) data_type_layout.addRow(QLabel("Visualization style:"), self.data_type_combo) self.data_type_combo.currentIndexChanged[int].connect( self.on_demo_style) data_button = QPushButton("Data") data_button.clicked.connect(self.on_load) self.data_label = QLabel("<choose data .npy>") data_type_layout.addRow(data_button, self.data_label) self.xy_label = QLabel("Image size") self.label_x = QLabel("X:") self.label_y = QLabel("Y:") self.image_x = QLineEdit() self.image_x.setValidator(QIntValidator(1, 10000)) self.image_y = QLineEdit() self.image_y.setValidator(QIntValidator(1, 10000)) self.xy_container = QWidget() self.xy_layout = QHBoxLayout() self.xy_layout.setContentsMargins(0, 0, 0, 0) self.xy_layout.addWidget(self.label_x) self.xy_layout.addWidget(self.image_x) self.xy_layout.addWidget(self.label_y) self.xy_layout.addWidget(self.image_y) self.xy_container.setLayout(self.xy_layout) data_type_layout.addRow(self.xy_label, self.xy_container) self.label_button = QPushButton("Labels") self.label_button.clicked.connect(self.on_load_labels) self.label_label = QLabel("<optionally choose labels .npy>") data_type_layout.addRow(self.label_button, self.label_label) self.meta_button = QPushButton("Label/Color metadata") self.meta_button.clicked.connect(self.on_load_labelscolors) self.meta_label = QLabel("<optionally choose label/color .csv>") data_type_layout.addRow(self.meta_button, self.meta_label) self.demo_type_group.setLayout(data_type_layout) controlLayout.addWidget(self.demo_type_group) # Hsne settings self.hsne_group = QGroupBox("hSNE settings") self.hsne_form_layout = QFormLayout(self) hsne_button = QPushButton("Preload") hsne_button.clicked.connect(self.on_load_hsne) self.hsne_label = QLabel("<optionally choose existing .hsne>") self.hsne_form_layout.addRow(hsne_button, self.hsne_label) self.scale_spin = QSpinBox(self) self.scale_spin.setRange(1, 10) self.scale_spin.setValue(4) self.hsne_form_layout.addRow(QLabel("Scales:"), self.scale_spin) self.hsne_group.setLayout(self.hsne_form_layout) controlLayout.addWidget(self.hsne_group) # Embedding settings self.embedding_group = QGroupBox("Embedding settings") embed_form_layout = QFormLayout(self) self.iter_spin = QSpinBox(self) self.iter_spin.setRange(350, 1000) self.iter_spin.setSingleStep(5) self.iter_spin.setValue(500) embed_form_layout.addRow(QLabel("Iterations:"), self.iter_spin) self.embedding_group.setLayout(embed_form_layout) controlLayout.addWidget(self.embedding_group) clear_start_layout = QHBoxLayout() self.start_button = QPushButton("Start") self.start_button.clicked.connect(self.on_start) self.start_button.setDisabled(True) self.clear_button = QPushButton("Clear") self.clear_button.clicked.connect(self.on_clear) clear_start_layout.addWidget(self.start_button) clear_start_layout.addWidget(self.clear_button) controlLayout.addLayout(clear_start_layout) self.delete_button = QPushButton("Delete selected") self.delete_button.clicked.connect(self.on_delete) # Selection self.tree.clicked.connect(self.on_selected) # Layout main_layout = QGridLayout() # row, col, rowSpan, colSpan main_layout.addWidget(self.tree, 0, 0, 10, 5) main_layout.addLayout(controlLayout, 0, 5, 10, 3) main_layout.addWidget(self.delete_button, 11, 2, 1, 2) self.setLayout(main_layout) self.counter = 0 self.event_timer = QTimer(self) self.event_timer.start(500) self.event_timer.timeout.connect(self.update_tree) # Dynamic UI matrix # According to DemoType True widgets are shown False widgets hidden self.ui_matrix = { DemoType.LABELLED_DEMO: { True: [ self.label_button, self.label_label, self.xy_container, self.xy_label ], False: [self.meta_button, self.meta_label] }, DemoType.POINT_DEMO: { True: [self.meta_button, self.meta_label], False: [ self.label_button, self.label_label, self.xy_container, self.xy_label ] }, DemoType.HYPERSPECTRAL_DEMO: { True: [self.xy_container, self.xy_label], False: [ self.label_button, self.label_label, self.meta_button, self.meta_label ] } } self.show() self.on_demo_style(0) def set_analysis_model(self, analysis_model): # TODO empty event queue self.analysis_model = analysis_model self.top_scale = self.analysis_model.top_scale_id @property def iterations(self): return self.iter_spin.value() @property def scales(self): return self.scale_spin.value() @property def demo_type(self): return self.data_type_combo.currentData() @property def im_size_x(self): return int(self.image_x.text()) @property def im_size_y(self): return int(self.image_y.text()) @pyqtSlot(int) def on_preconfigured(self, index): config = self.preconfigured_combo.itemData(index) self.on_clear() if config is None: return if type(config).__name__ == "LabelledImage": self.data_type_combo.setCurrentIndex(0) self.name = config.data.data_file self.data_label.setText(config.data.data_file) self.label_name = config.data.label_file self.label_label.setText(config.data.label_file) if config.hsne.hsne_file != "": self.hsne_name = config.hsne.hsne_file self.__set_scale_from_hsne_file() self.scale_spin.setDisabled(True) elif config.hsne.scales > 0: self.scale_spin.setValue(config.hsne.scales) if config.image.dim_x > 0 and config.image.dim_y > 0: self.image_x.setText(str(config.image.dim_x)) self.image_y.setText(str(config.image.dim_y)) elif type(config).__name__ == "PointMeta": self.data_type_combo.setCurrentIndex(1) self.name = config.data.data_file self.data_label.setText(config.data.data_file) self.meta_name = config.data.meta_file self.meta_label.setText(config.data.meta_file) if config.hsne.hsne_file != "": self.hsne_name = config.hsne.hsne_file self.__set_scale_from_hsne_file() self.scale_spin.setDisabled(True) elif config.hsne.scales > 0: self.scale_spin.setValue(config.hsne.scales) elif type(config).__name__ == "HyperspectralImage": self.data_type_combo.setCurrentIndex(2) self.name = config.data.data_file self.data_label.setText(config.data.data_file) if config.hsne.hsne_file != "": self.hsne_name = config.hsne.hsne_file self.__set_scale_from_hsne_file() self.scale_spin.setDisabled(True) elif config.hsne.scales > 0: self.scale_spin.setValue(config.hsne.scales) if config.image.dim_x > 0 and config.image.dim_y > 0: self.image_x.setText(str(config.image.dim_x)) self.image_y.setText(str(config.image.dim_y)) self.start_button.setEnabled(True) @pyqtSlot(int) def on_demo_style(self, index): # set the visibility of the widgets according to the # type of demo being given for state in [False, True]: for widget in self.ui_matrix[self.demo_type][state]: widget.setVisible(state) @pyqtSlot() def on_load(self): workdir = os.path.dirname(os.path.abspath(__file__)) result = QFileDialog.getOpenFileName( self, 'Open a numpy file where each row is a data point and columns are dimensions', workdir, "Numpy files (*.npy)") if result[0]: self.hsne_label.setText("") self.hsne_name = None self.scale_spin.setEnabled(True) self.name = result[0] # print(f"Selected: {self.name}") self.start_button.setEnabled(True) if (self.demo_type == DemoType.LABELLED_DEMO or self.demo_type == DemoType.HYPERSPECTRAL_DEMO): # partial data load (in memory) to read the shape the_data = np.load(self.name, mmap_mode='r') image_flat_size = the_data.shape[1] if self.demo_type == DemoType.HYPERSPECTRAL_DEMO: image_flat_size = the_data.shape[0] xsize = int(math.sqrt(image_flat_size)) ysize = int(image_flat_size / xsize) self.image_x.setText(str(xsize)) self.image_y.setText(str(ysize)) self.data_label.setText(str(Path(self.name).name)) def __set_scale_from_hsne_file(self): scale_value = nptsne.HSne.read_num_scales(self.hsne_name) self.hsne_label.setText(str(Path(self.hsne_name).name)) self.scale_spin.setValue(scale_value) self.scale_spin.setDisabled(True) @pyqtSlot() def on_load_hsne(self): workdir = os.path.dirname(os.path.abspath(__file__)) result = QFileDialog.getOpenFileName( self, 'Open a pre-calculated hSNE analysis file .hsne', workdir, "hSNE files (*.hsne)") if result[0]: self.hsne_name = result[0] # print(f"Selected: {self.name}") self.__set_scale_from_hsne_file() @pyqtSlot() def on_load_labels(self): workdir = os.path.dirname(os.path.abspath(__file__)) result = QFileDialog.getOpenFileName( self, 'Open a numpy file where each row is an integer label', workdir, "Numpy files (*.npy)") if result[0]: self.label_name = result[0] self.label_label.setText(Path(self.label_name).name) @pyqtSlot() def on_load_labelscolors(self): workdir = os.path.dirname(os.path.abspath(__file__)) result = QFileDialog.getOpenFileName( self, 'Open a CSV file with header where the columns pairs of Label, #COLOR_', workdir, "CSV files (*.csv)") if result[0]: self.meta_name = result[0] self.meta_label.setText(Path(self.meta_name).name) @pyqtSlot() def on_start(self): self.load_callback(self.name, self.label_name, self.meta_name, self.hsne_name) @pyqtSlot() def on_selected(self): analysis_id = self._get_selected_id() if analysis_id: self.select_callback(int(analysis_id)) @pyqtSlot() def on_delete(self): analysis_id = self._get_selected_id() if analysis_id: self.delete_callback([int(analysis_id)]) @pyqtSlot() def on_clear(self): if not self.root_id is None: self.select_callback(int(self.root_id)) self.clear() self.name = None self.data_label.setText("<choose data .npy>") self.label_name = None self.label_label.setText("<optionally choose labels .npy>") self.meta_name = None self.meta_label.setText("<optionally choose label/color .csv>") self.scale_spin.setValue(4) self.scale_spin.setEnabled(True) self.start_button.setDisabled(True) self.hsne_name = None self.hsne_label.setText("<optionally choose existing .hsne>") self.image_x.setText("") self.image_y.setText("") def _get_selected_id(self): index = self.tree.currentIndex() if index is None: return None return self.model.itemData(index.siblingAtColumn(self.ID))[0] def create_analysis_model(self, parent): model = QStandardItemModel(0, 3, parent) model.setHeaderData(self.ANALYSIS, Qt.Horizontal, "Analysis") model.setHeaderData(self.ID, Qt.Horizontal, "Id") model.setHeaderData(self.NUMPOINTS, Qt.Horizontal, "#Points") return model def add_test_analysis(self): parent_id = ModelGui.NO_PARENT_ID if self.counter > 0: parent_id = self.counter - 1 self.add_analysis(self.counter, f"{self.counter} Blah blah blah", parent_id, 150) self.counter = self.counter + 1 def update_tree(self): # Update tree based on queued events while True: event = {} try: event = self.analysis_event_queue.get_nowait() except queue.Empty: break if event['event'] == AnalysisEvent.ADDED: self.add_analysis(event['id'], event['name'], event['parent_id'], event['number_of_points']) continue if event['event'] == AnalysisEvent.FINISHED: self.finish_analysis(event['id'], event['name'], event['image_buf']) continue if event['event'] == AnalysisEvent.REMOVED: self.remove_analysis(event['id']) def add_analysis(self, analysis_id, name, parent_id, numpoints): im = ImageQt.ImageQt(Image.new('RGB', self.thumb_size, (100, 0, 200))) item = QStandardItem(QIcon(QPixmap.fromImage(im)), name) # Need to persist the thumbnails otherwise the ImageQT will get garbage # collected along with the memory item.__thumb = im if parent_id == ModelGui.NO_PARENT_ID: #print("Adding root") self.clear() self.model.insertRow(0, [ item, QStandardItem(str(analysis_id)), QStandardItem(str(numpoints)) ]) self.root_id = analysis_id self.id_item[analysis_id] = item else: #print("Adding child") parent = self.find_analysis_item(parent_id) if parent is not None: parent.appendRow([ item, QStandardItem(str(analysis_id)), QStandardItem(str(numpoints)) ]) self.id_item[analysis_id] = item self.tree.expand(parent.index()) def remove_analysis(self, analysis_id): if analysis_id == self.root_id: self.clear() return item = self.find_analysis_item(analysis_id) if item: if item.parent: try: item.parent().removeRow(item.row()) except RuntimeError: # TODO fix bug that means this is being deleted twice pass del self.id_item[analysis_id] def finish_analysis(self, analysis_id, name, image_buf): print("finished ", analysis_id) img = PIL.Image.open(image_buf) thumbnail = img.resize(self.thumb_size, PIL.Image.ANTIALIAS) # thumbnail.show() im = ImageQt.ImageQt(thumbnail) item = self.find_analysis_item(analysis_id) item.setIcon(QIcon(QPixmap.fromImage(im))) item.__thumb = im def find_analysis_item(self, analysis_id): """ Get the item using the numeric analysis_id """ return self.id_item.get(analysis_id, None) def clear(self): print('Clear model content') if self.model is not None: # print('Remove rows') self.model.removeRows(0, self.model.rowCount()) # print('Reset bookkeeping') self.id_item = {} self.root_id = None
class Win(QMainWindow): def __init__(self, options): super(Win, self).__init__() self.db = dbtag.Db() self.db.open(options.db) self.db.create_tables() self.rootPath = options.filespath self._init_widgets() self._init_more() def _init_widgets(self): bigsplitter = QSplitter(Qt.Horizontal, self) self.setCentralWidget(bigsplitter) leftsplitter = QSplitter(Qt.Vertical, self) self.tabWidget = QTabWidget(self) self.tagChooser = TagChooser(self.db) self.dirChooser = QTreeView(self) self.tabWidget.addTab(self.dirChooser, 'Dir') self.tabWidget.addTab(self.tagChooser, 'Tags') self.tagEditor = TagEditor(self.db) self.imageList = ImageList() leftsplitter.addWidget(self.tabWidget) leftsplitter.addWidget(self.tagEditor) bigsplitter.addWidget(leftsplitter) bigsplitter.addWidget(self.imageList) self.viewer = ImageViewer(self.db) def _init_more(self): self.setWindowTitle('Tags4') self.dirModel = QFileSystemModel() self.dirModel.setFilter(QDir.AllDirs | QDir.Drives | QDir.Hidden | QDir.NoDotAndDotDot) qidx = self.dirModel.setRootPath(self.rootPath) self.dirChooser.setModel(self.dirModel) self.dirChooser.setRootIndex(qidx) self.dirChooser.clicked.connect(self.browseSelectedDir) self.imageList.itemSelectionChanged.connect(self._editTagsItems) self.imageList.itemDoubleClicked.connect(self._spawnViewerItem) self.imageList.setSelectionMode(QAbstractItemView.ExtendedSelection) self.tabWidget.currentChanged.connect(self._tabSelected) self.tagChooser.changed.connect(self.browseSelectedTags) def editTags(self, path): self.tagEditor.setFile(path) def editTagsItems(self, paths): self.tagEditor.setFiles(paths) def spawnViewer(self, files, currentFile): self.viewer.spawn(files, currentFile) @Slot() def _editTagsItems(self): self.editTagsItems([qitem.getPath() for qitem in self.imageList.selectedItems()]) @Slot(QListWidgetItem) def _spawnViewerItem(self, qitem): self.spawnViewer(self.imageList.getFiles(), qitem.getPath()) @Slot() def browseSelectedDir(self): path = self.dirModel.filePath(self.dirChooser.currentIndex()) if not path: return files = [os.path.join(path, f) for f in os.listdir(path)] files = filter(os.path.isfile, files) files.sort() self.imageList.setFiles(files) @Slot() def browseSelectedTags(self): self.imageList.setFiles(self.tagChooser.matchingFiles()) @Slot(int) def _tabSelected(self, idx): if idx == 0: self.browseSelectedDir() else: self.browseSelectedTags() def browsePath(self, path): self.imageList.setFiles(os.path.join(path, f) for f in os.listdir(path))
class _LocatorDialog(QDialog): """Locator widget and implementation """ def __init__(self, parent, commandClasses): QDialog.__init__(self, parent) self._terminated = False self._commandClasses = commandClasses self._createUi() self._loadingTimer = QTimer(self) self._loadingTimer.setSingleShot(True) self._loadingTimer.setInterval(200) self._loadingTimer.timeout.connect(self._applyLoadingCompleter) self._completerLoaderThread = _CompleterLoaderThread(self) self.finished.connect(self._terminate) self._command = None self._updateCurrentCommand() def _createUi(self): self.setWindowTitle(core.project().path() or 'Locator') self.setLayout(QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(1) biggerFont = self.font() biggerFont.setPointSizeF(biggerFont.pointSizeF() * 2) self.setFont(biggerFont) self._edit = _CompletableLineEdit(self) self._edit.updateCurrentCommand.connect(self._updateCurrentCommand) self._edit.enterPressed.connect(self._onEnterPressed) self._edit.installEventFilter(self) # catch Up, Down self.layout().addWidget(self._edit) self.setFocusProxy(self._edit) self._table = QTreeView(self) self._table.setFont(biggerFont) self._model = _CompleterModel() self._table.setModel(self._model) self._table.setItemDelegate(HTMLDelegate(self._table)) self._table.setRootIsDecorated(False) self._table.setHeaderHidden(True) self._table.clicked.connect(self._onItemClicked) self._table.setAlternatingRowColors(True) self._table.installEventFilter( self) # catch focus and give to the edit self.layout().addWidget(self._table) width = QFontMetrics(self.font()).width('x' * 64) # width of 64 'x' letters self.resize(width, width * 0.62) def _terminate(self): if not self._terminated: if self._command is not None: self._command.terminate() self._command = None self._edit.terminate() self._completerLoaderThread.terminate() core.workspace().focusCurrentDocument() self._terminated = True def _updateCurrentCommand(self): """Try to parse line edit text and set current command """ if self._terminated: return newCommand = self._parseCurrentCommand() if newCommand is not self._command: if self._command is not None: self._command.updateCompleter.disconnect( self._updateCompletion) self._command.terminate() self._command = newCommand if self._command is not None: self._command.updateCompleter.connect(self._updateCompletion) self._updateCompletion() def _updateCompletion(self): """User edited text or moved cursor. Update inline and TreeView completion """ if self._command is not None: completer = self._command.completer() if completer is not None and completer.mustBeLoaded: self._loadingTimer.start() self._completerLoaderThread.loadCompleter( self._command, completer) else: self._applyCompleter(self._command, completer) else: self._applyCompleter(None, _HelpCompleter(self._commandClasses)) def _applyLoadingCompleter(self): """Set 'Loading...' message """ self._applyCompleter(None, StatusCompleter('<i>Loading...</i>')) def onCompleterLoaded(self, command, completer): """The method called from _CompleterLoaderThread when the completer is ready This code works in the GUI thread """ self._applyCompleter(command, completer) def _applyCompleter(self, command, completer): """Apply completer. Called by _updateCompletion or by thread function when Completer is constructed """ self._loadingTimer.stop() if command is not None: command.onCompleterLoaded(completer) if completer is None: completer = _HelpCompleter([command]) if self._edit.cursorPosition() == len( self._edit.text()): # if cursor at the end of text self._edit.setInlineCompletion(completer.inline()) self._model.setCompleter(completer) if completer.columnCount() > 1: self._table.resizeColumnToContents(0) self._table.setColumnWidth(0, self._table.columnWidth(0) + 20) # 20 px spacing between columns selItem = completer.autoSelectItem() if selItem: index = self._model.createIndex(selItem[0], selItem[1]) self._table.setCurrentIndex(index) def _onItemClicked(self, index): """Item in the TreeView has been clicked. Open file, if user selected it """ if self._command is not None: fullText = self._model.completer.getFullText(index.row()) if fullText is not None: self._command.onItemClicked(fullText) if self._tryExecCurrentCommand(): self.accept() return else: self._edit.setText(self._command.lineEditText()) self._updateCurrentCommand() self._edit.setFocus() def _onEnterPressed(self): """User pressed Enter or clicked item. Execute command, if possible """ if self._table.currentIndex().isValid(): self._onItemClicked(self._table.currentIndex()) else: self._tryExecCurrentCommand() def _tryExecCurrentCommand(self): if self._command is not None and self._command.isReadyToExecute(): self._command.execute() self.accept() return True else: return False def _chooseCommand(self, words): for cmd in self._commandClasses: if cmd.command == words[0]: return cmd, words[1:] isPath = words and (words[0].startswith('/') or words[0].startswith('./') or words[0].startswith('../') or words[0].startswith('~/') or words[0][1:3] == ':\\' or words[0][1:3] == ':/') isNumber = len(words) == 1 and all([c.isdigit() for c in words[0]]) def matches(cmd): if isPath: return cmd.isDefaultPathCommand elif isNumber: return cmd.isDefaultNumericCommand else: return cmd.isDefaultCommand for cmd in self._commandClasses: if matches(cmd): return cmd, words def _parseCurrentCommand(self): """ Parse text and try to get (command, completable word index) Return None if failed to parse """ # Split line text = self._edit.commandText() words = splitLine(text) if not words: return None # Find command cmdClass, args = self._chooseCommand(words) if isinstance(self._command, cmdClass): command = self._command else: command = cmdClass() # Try to make command object try: command.setArgs(args) except InvalidCmdArgs: return None else: return command def eventFilter(self, obj, event): if obj is self._edit: if event.type() == QEvent.KeyPress and \ event.key() in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown): return self._table.event(event) elif obj is self._table: if event.type() == QEvent.FocusIn: self._edit.setFocus() return True return False
class AddToProject(QDialog): """Dialog to let the user choose one of the folders from the opened proj""" def __init__(self, projects, parent=None): super(AddToProject, self).__init__(parent) # projects must be a list self._projects = projects self.setWindowTitle(translations.TR_ADD_FILE_TO_PROJECT) self.path_selected = '' vbox = QVBoxLayout(self) hbox = QHBoxLayout() self._list = QListWidget() for project in self._projects: self._list.addItem(project.name) self._list.setCurrentRow(0) self._tree = QTreeView() self._tree.setHeaderHidden(True) self._tree.setSelectionMode(QTreeView.SingleSelection) self._tree.setAnimated(True) self.load_tree(self._projects[0]) hbox.addWidget(self._list) hbox.addWidget(self._tree) vbox.addLayout(hbox) hbox2 = QHBoxLayout() btn_add = QPushButton(translations.TR_ADD_HERE) btn_cancel = QPushButton(translations.TR_CANCEL) hbox2.addWidget(btn_cancel) hbox2.addWidget(btn_add) vbox.addLayout(hbox2) btn_add.clicked.connect(self._select_path) btn_cancel.clicked.connect(self.close) self._list.currentItemChanged.connect(self._project_changed) def _project_changed(self, item, previous): # FIXME, this is not being called, at least in osx for each_project in self._projects: if each_project.name == item.text(): self.load_tree(each_project) def load_tree(self, project): """Load the tree view on the right based on the project selected.""" qfsm = QFileSystemModel() qfsm.setRootPath(project.path) load_index = qfsm.index(qfsm.rootPath()) qfsm.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) qfsm.setNameFilterDisables(False) pext = ["*{0}".format(x) for x in project.extensions] qfsm.setNameFilters(pext) self._tree.setModel(qfsm) self._tree.setRootIndex(load_index) t_header = self._tree.header() t_header.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) t_header.setSectionResizeMode(0, QHeaderView.Stretch) t_header.setStretchLastSection(False) t_header.setSectionsClickable(True) self._tree.hideColumn(1) # Size self._tree.hideColumn(2) # Type self._tree.hideColumn(3) # Modification date # FIXME: Changing the name column's title requires some magic # Please look at the project tree def _select_path(self): """Set path_selected to the folder selected in the tree.""" path = self._tree.model().filePath(self._tree.currentIndex()) if path: self.path_selected = path self.close()
class Dialog_ImageFolder(): def __init__(self, parent, title, init_path): self.w = QDialog(parent) self.parent = parent self.left = 300 self.top = 300 self.width = 600 self.height = 400 self.title = title self.dirModel = QFileSystemModel() self.dirModel.setRootPath(init_path) self.dirModel.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs) self.treeview = QTreeView() self.treeview.setModel(self.dirModel) self.treeview.setRootIndex(self.dirModel.index("")) self.treeview.clicked.connect(self.on_clicked) #--- Hide All Header Sections Except First ---- header = self.treeview.header() for sec in range(1, header.count()): header.setSectionHidden(sec, True) #--- ---- ---- ---- ---- ---- ---- ---- ---- -- focus_index = self.dirModel.index(init_path) self.treeview.setCurrentIndex(focus_index) self.current_row_changed() self.listview = QListView() self.listview.setViewMode(QListView.IconMode) self.listview.setIconSize(QSize(192, 192)) targetfiles1 = glob.glob(os.path.join(init_path, '*.png')) targetfiles2 = glob.glob(os.path.join(init_path, '*.tif')) targetfiles3 = glob.glob(os.path.join(init_path, '*.tiff')) targetfiles = targetfiles1 + targetfiles2 + targetfiles3 lm = _MyListModel(targetfiles, self.parent) self.listview.setModel(lm) self.sub_layout = QHBoxLayout() self.sub_layout.addWidget(self.treeview) self.sub_layout.addWidget(self.listview) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.main_layout = QVBoxLayout() self.main_layout.addLayout(self.sub_layout) self.main_layout.addWidget(self.buttonBox) self.w.setGeometry(self.left, self.top, self.width, self.height) self.w.setWindowTitle(self.title) self.w.setWindowIcon(QIcon(os.path.join(icon_dir, 'Mojo2_16.png'))) self.w.setLayout(self.main_layout) def current_row_changed(self): index = self.treeview.currentIndex() self.treeview.scrollTo(index, QAbstractItemView.EnsureVisible) self.treeview.resizeColumnToContents(0) def on_clicked(self, index): path = self.dirModel.fileInfo(index).absoluteFilePath() targetfiles1 = glob.glob(os.path.join(path, '*.png')) targetfiles2 = glob.glob(os.path.join(path, '*.tif')) targetfiles3 = glob.glob(os.path.join(path, '*.tiff')) targetfiles = targetfiles1 + targetfiles2 + targetfiles3 lm = _MyListModel(targetfiles, self.parent) self.listview.setModel(lm) def accept(self): index = self.treeview.currentIndex() self.newdir = self.dirModel.filePath(index) self.w.done(1) def reject(self): self.w.done(0) def GetValue(self): index = self.treeview.currentIndex() self.newdir = self.dirModel.filePath(index) return self.newdir
class NavigatorDock(DockWidget): def __init__(self): DockWidget.__init__(self, core.mainWindow(), '&Navigator', QIcon(':/enkiicons/goto.png'), "Alt+N") self._tags = [] self._tree = QTreeView(self) self._tree.installEventFilter(self) self._tree.setHeaderHidden(True) self.setFocusProxy(self._tree) self._filterEdit = LineEdit(self) self._filterEdit.setClearButtonVisible(True) self._filterEdit.textEdited.connect(self._applyFilter) self._filterEdit.clearButtonClicked.connect(self._applyFilter) self._filterEdit.clearButtonClicked.connect(self._tree.setFocus) self._filterEdit.clearButtonClicked.connect(self._hideFilter) self._filterEdit.installEventFilter(self) self._displayWidget = QWidget(self) layout = QVBoxLayout(self._displayWidget) layout.addWidget(self._tree) layout.addWidget(self._filterEdit) layout.setContentsMargins(0, 0, 0, 0) self.setWidget(self._displayWidget) self._tagModel = _TagModel(self._tree) self._tagModel.jumpToTagDone.connect(self._hideFilter) self._tree.setModel(self._tagModel) self._tree.activated.connect(self._tagModel.onActivated) self._tree.clicked.connect(self._tagModel.onActivated) self._tagModel.modelAboutToBeReset.connect(self._onModelAboutToBeReset) self._tagModel.modelReset.connect(self._onModelReset) self._currentTagPath = None self._errorLabel = None self._installed = False def term(self): self._tagModel.term() def install(self): if not self._installed: core.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self) core.actionManager().addAction("mView/aNavigator", self.showAction()) self._installed = True def remove(self): if self._installed: core.mainWindow().removeDockWidget(self) core.actionManager().removeAction("mView/aNavigator") self.hide() self._installed = False def setTags(self, tags): self._tags = tags self._setFilteredTags(tags) self._hideFilter() if self.widget() is not self._displayWidget: self.setWidget(self._displayWidget) self._displayWidget.show() if self._errorLabel is not None: self._errorLabel.hide() def _setFilteredTags(self, tags): self._tagModel.setTags(tags) def onError(self, error): self._displayWidget.hide() if self._errorLabel is None: self._errorLabel = QLabel(self) self._errorLabel.setWordWrap(True) self._errorLabel.setText(error) if not self.widget() is self._errorLabel: self.setWidget(self._errorLabel) self._errorLabel.show() self._displayWidget.hide() def _onModelAboutToBeReset(self): currIndex = self._tree.currentIndex() self._currentTagPath = self._tagModel.tagPathForIndex(currIndex) if currIndex.isValid() else None def _onModelReset(self): self._tree.expandAll() # restore current item if self._currentTagPath is not None: index = self._tagModel.indexForTagPath(self._currentTagPath) if index.isValid(): self._tree.setCurrentIndex(index) def eventFilter(self, object_, event): if object_ is self._tree: if event.type() == QEvent.KeyPress: if event.key() == Qt.Key_Backspace: if event.modifiers() == Qt.ControlModifier: self._onTreeCtrlBackspace() else: self._onTreeBackspace() return True elif event.text() and \ (event.text().isalnum() or event.text() == '_'): self._onTreeTextTyped(event.text()) return True elif object_ is self._filterEdit: if event.type() == QEvent.KeyPress: if event.key() in (Qt.Key_Up, Qt.Key_Down): self._tree.setFocus() self._tree.event(event) return True elif event.key() in (Qt.Key_Enter, Qt.Key_Return): currIndex = self._tree.currentIndex() if currIndex.isValid(): self._tagModel.onActivated(currIndex) return DockWidget.eventFilter(self, object_, event) def _hideFilter(self): hadText = self._filterEdit.text() != '' self._filterEdit.clear() self._filterEdit.hide() if hadText: self._applyFilter() def _applyFilter(self): text = self._filterEdit.text() if text: if not text.startswith('*'): text = '*' + text if not text.endswith('*'): text = text + '*' wildcard = text.lower() filteredTags = _filterTags(wildcard, self._tags) self._setFilteredTags(filteredTags) self._tree.expandAll() if filteredTags: firstMatchingTag = _findFirstMatching(wildcard, filteredTags) path = _tagPath(firstMatchingTag) index = self._tagModel.indexForTagPath(path) self._tree.setCurrentIndex(index) else: self._setFilteredTags(self._tags) if text: self._filterEdit.show() elif not self._filterEdit.hasFocus(): self._hideFilter() def _onTreeTextTyped(self, text): self._filterEdit.setText(self._filterEdit.text() + text) self._applyFilter() def _onTreeBackspace(self): text = self._filterEdit.text() if text: self._filterEdit.setText(text[:-1]) self._applyFilter() def _onTreeCtrlBackspace(self): self._hideFilter() self._applyFilter()
class Widget(QWidget): def __init__(self, panel): super(Widget, self).__init__(panel) layout = QVBoxLayout() self.setLayout(layout) layout.setSpacing(0) self.searchEntry = SearchLineEdit() self.treeView = QTreeView(contextMenuPolicy=Qt.CustomContextMenu) self.textView = QTextBrowser() applyButton = QToolButton(autoRaise=True) editButton = QToolButton(autoRaise=True) addButton = QToolButton(autoRaise=True) self.menuButton = QPushButton(flat=True) menu = QMenu(self.menuButton) self.menuButton.setMenu(menu) splitter = QSplitter(Qt.Vertical) top = QHBoxLayout() layout.addLayout(top) splitter.addWidget(self.treeView) splitter.addWidget(self.textView) layout.addWidget(splitter) splitter.setSizes([200, 100]) splitter.setCollapsible(0, False) top.addWidget(self.searchEntry) top.addWidget(applyButton) top.addSpacing(10) top.addWidget(addButton) top.addWidget(editButton) top.addWidget(self.menuButton) # action generator for actions added to search entry def act(slot, icon=None): a = QAction(self, triggered=slot) self.addAction(a) a.setShortcutContext(Qt.WidgetWithChildrenShortcut) icon and a.setIcon(icons.get(icon)) return a # hide if ESC pressed in lineedit a = act(self.slotEscapePressed) a.setShortcut(QKeySequence(Qt.Key_Escape)) # import action a = self.importAction = act(self.slotImport, 'document-open') menu.addAction(a) # export action a = self.exportAction = act(self.slotExport, 'document-save-as') menu.addAction(a) # apply button a = self.applyAction = act(self.slotApply, 'edit-paste') applyButton.setDefaultAction(a) menu.addSeparator() menu.addAction(a) # add button a = self.addAction_ = act(self.slotAdd, 'list-add') a.setShortcut(QKeySequence(Qt.Key_Insert)) addButton.setDefaultAction(a) menu.addSeparator() menu.addAction(a) # edit button a = self.editAction = act(self.slotEdit, 'document-edit') a.setShortcut(QKeySequence(Qt.Key_F2)) editButton.setDefaultAction(a) menu.addAction(a) # set shortcut action a = self.shortcutAction = act( self.slotShortcut, 'preferences-desktop-keyboard-shortcuts') menu.addAction(a) # delete action a = self.deleteAction = act(self.slotDelete, 'list-remove') a.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Delete)) menu.addAction(a) # restore action a = self.restoreAction = act(self.slotRestore) menu.addSeparator() menu.addAction(a) # help button a = self.helpAction = act(self.slotHelp, 'help-contents') menu.addSeparator() menu.addAction(a) self.treeView.setSelectionBehavior(QTreeView.SelectRows) self.treeView.setSelectionMode(QTreeView.ExtendedSelection) self.treeView.setRootIsDecorated(False) self.treeView.setAllColumnsShowFocus(True) self.treeView.setModel(model.model()) self.treeView.setCurrentIndex(QModelIndex()) # signals self.searchEntry.returnPressed.connect(self.slotReturnPressed) self.searchEntry.textChanged.connect(self.updateFilter) self.treeView.doubleClicked.connect(self.slotDoubleClicked) self.treeView.customContextMenuRequested.connect(self.showContextMenu) self.treeView.selectionModel().currentChanged.connect(self.updateText) self.treeView.model().dataChanged.connect(self.updateFilter) # highlight text self.highlighter = highlight.Highlighter(self.textView.document()) # complete on snippet variables self.searchEntry.setCompleter( QCompleter([ ':icon', ':indent', ':menu', ':name', ':python', ':selection', ':set', ':symbol', ':template', ':template-run' ], self.searchEntry)) self.readSettings() app.settingsChanged.connect(self.readSettings) app.translateUI(self) self.updateColumnSizes() self.setAcceptDrops(True) def dropEvent(self, ev): if not ev.source() and ev.mimeData().hasUrls(): filename = ev.mimeData().urls()[0].toLocalFile() if filename: ev.accept() from . import import_export import_export.load(filename, self) def dragEnterEvent(self, ev): if not ev.source() and ev.mimeData().hasUrls(): ev.accept() def translateUI(self): try: self.searchEntry.setPlaceholderText(_("Search...")) except AttributeError: pass # not in Qt 4.6 shortcut = lambda a: a.shortcut().toString(QKeySequence.NativeText) self.menuButton.setText(_("&Menu")) self.addAction_.setText(_("&Add...")) self.addAction_.setToolTip( _("Add a new snippet. ({key})").format( key=shortcut(self.addAction_))) self.editAction.setText(_("&Edit...")) self.editAction.setToolTip( _("Edit the current snippet. ({key})").format( key=shortcut(self.editAction))) self.shortcutAction.setText(_("Configure Keyboard &Shortcut...")) self.deleteAction.setText(_("&Remove")) self.deleteAction.setToolTip(_("Remove the selected snippets.")) self.applyAction.setText(_("A&pply")) self.applyAction.setToolTip(_("Apply the current snippet.")) self.importAction.setText(_("&Import...")) self.importAction.setToolTip(_("Import snippets from a file.")) self.exportAction.setText(_("E&xport...")) self.exportAction.setToolTip(_("Export snippets to a file.")) self.restoreAction.setText(_("Restore &Built-in Snippets...")) self.restoreAction.setToolTip( _("Restore deleted or changed built-in snippets.")) self.helpAction.setText(_("&Help")) self.searchEntry.setToolTip( _("Enter text to search in the snippets list.\n" "See \"What's This\" for more information.")) self.searchEntry.setWhatsThis(''.join( map("<p>{0}</p>\n".format, ( _("Enter text to search in the snippets list, and " "press Enter to apply the currently selected snippet."), _("If the search text fully matches the value of the '{name}' variable " "of a snippet, that snippet is selected.").format( name="name"), _("If the search text starts with a colon ':', the rest of the " "search text filters snippets that define the given variable. " "After a space a value can also be entered, snippets will then " "match if the value of the given variable contains the text after " "the space."), _("E.g. entering {menu} will show all snippets that are displayed " "in the insert menu.").format(menu="<code>:menu</code>"), )))) def sizeHint(self): return self.parent().mainwindow().size() / 4 def readSettings(self): data = textformats.formatData('editor') self.textView.setFont(data.font) self.textView.setPalette(data.palette()) def showContextMenu(self, pos): """Called when the user right-clicks the tree view.""" self.menuButton.menu().popup(self.treeView.viewport().mapToGlobal(pos)) def slotReturnPressed(self): """Called when the user presses Return in the search entry. Applies current snippet.""" name = self.currentSnippet() if name: view = self.parent().mainwindow().currentView() insert.insert(name, view) self.parent().hide() # make configurable? view.setFocus() def slotEscapePressed(self): """Called when the user presses ESC in the search entry. Hides the panel.""" self.parent().hide() self.parent().mainwindow().currentView().setFocus() def slotDoubleClicked(self, index): name = self.treeView.model().name(index) view = self.parent().mainwindow().currentView() insert.insert(name, view) def slotAdd(self): """Called when the user wants to add a new snippet.""" edit.Edit(self, None) def slotEdit(self): """Called when the user wants to edit a snippet.""" name = self.currentSnippet() if name: edit.Edit(self, name) def slotShortcut(self): """Called when the user selects the Configure Shortcut action.""" from widgets import shortcuteditdialog name = self.currentSnippet() if name: collection = self.parent().snippetActions action = actions.action(name, None, collection) default = collection.defaults().get(name) mgr = actioncollectionmanager.manager(self.parent().mainwindow()) cb = mgr.findShortcutConflict dlg = shortcuteditdialog.ShortcutEditDialog( self, cb, (collection, name)) if dlg.editAction(action, default): mgr.removeShortcuts(action.shortcuts()) collection.setShortcuts(name, action.shortcuts()) self.treeView.update() def slotDelete(self): """Called when the user wants to delete the selected rows.""" rows = sorted(set(i.row() for i in self.treeView.selectedIndexes()), reverse=True) if rows: for row in rows: name = self.treeView.model().names()[row] self.parent().snippetActions.setShortcuts(name, []) self.treeView.model().removeRow(row) self.updateFilter() def slotApply(self): """Called when the user clicks the apply button. Applies current snippet.""" name = self.currentSnippet() if name: view = self.parent().mainwindow().currentView() insert.insert(name, view) def slotImport(self): """Called when the user activates the import action.""" filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files")) caption = app.caption(_("dialog title", "Import Snippets")) filename = None filename = QFileDialog.getOpenFileName(self, caption, filename, filetypes)[0] if filename: from . import import_export import_export.load(filename, self) def slotExport(self): """Called when the user activates the export action.""" allrows = [ row for row in range(model.model().rowCount()) if not self.treeView.isRowHidden(row, QModelIndex()) ] selectedrows = [ i.row() for i in self.treeView.selectedIndexes() if i.column() == 0 and i.row() in allrows ] names = self.treeView.model().names() names = [names[row] for row in selectedrows or allrows] filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files")) n = len(names) caption = app.caption( _("dialog title", "Export {num} Snippet", "Export {num} Snippets", n).format(num=n)) filename = QFileDialog.getSaveFileName(self, caption, None, filetypes)[0] if filename: from . import import_export try: import_export.save(names, filename) except (IOError, OSError) as e: QMessageBox.critical( self, _("Error"), _("Can't write to destination:\n\n{url}\n\n{error}"). format(url=filename, error=e.strerror)) def slotRestore(self): """Called when the user activates the Restore action.""" from . import restore dlg = restore.RestoreDialog(self) dlg.setWindowModality(Qt.WindowModal) dlg.populate() dlg.show() dlg.finished.connect(dlg.deleteLater) def slotHelp(self): """Called when the user clicks the small help button.""" userguide.show("snippets") def currentSnippet(self): """Returns the name of the current snippet if it is visible.""" row = self.treeView.currentIndex().row() if row != -1 and not self.treeView.isRowHidden(row, QModelIndex()): return self.treeView.model().names()[row] def updateFilter(self): """Called when the text in the entry changes, updates search results.""" text = self.searchEntry.text() ltext = text.lower() filterVars = text.startswith(':') if filterVars: try: fvar, fval = text[1:].split(None, 1) fhide = lambda v: v.get(fvar) in (True, None ) or fval not in v.get(fvar) except ValueError: fvar = text[1:].strip() fhide = lambda v: not v.get(fvar) for row in range(self.treeView.model().rowCount()): name = self.treeView.model().names()[row] nameid = snippets.get(name).variables.get('name', '') if filterVars: hide = fhide(snippets.get(name).variables) elif nameid == text: i = self.treeView.model().createIndex(row, 0) self.treeView.selectionModel().setCurrentIndex( i, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows) hide = False elif nameid.lower().startswith(ltext): hide = False elif ltext in snippets.title(name).lower(): hide = False else: hide = True self.treeView.setRowHidden(row, QModelIndex(), hide) self.updateText() def updateText(self): """Called when the current snippet changes.""" name = self.currentSnippet() self.textView.clear() if name: s = snippets.get(name) self.highlighter.setPython('python' in s.variables) self.textView.setPlainText(s.text) def updateColumnSizes(self): self.treeView.resizeColumnToContents(0) self.treeView.resizeColumnToContents(1)
class Window(QWidget): def __init__(self, connection): super(Window, self).__init__() self.conn = connection self.proxyModel = MySortFilterProxyModel(self) self.proxyModel.setDynamicSortFilter(True) self.proxyView = QTreeView() set_tree_view(self.proxyView) self.proxyView.setModel(self.proxyModel) self.proxyView.customContextMenuRequested.connect(self.pop_menu) self.filterType = QComboBox() self.filterModule = QComboBox() self.filterClass = QComboBox() self.filterNote = QComboBox() self.infLabel = QLabel() self.link_type = QComboBox() self.resView = QTreeView() set_tree_view(self.resView) self.resModel = QSortFilterProxyModel(self.resView) self.resView.setModel(self.resModel) self.resView.customContextMenuRequested.connect(self.menu_res_view) self.link_box = self.set_layout() self.sort_key = None self.repo = [] self.old_links = [] self.new_links = [] self.query_time = time_run() self.curr_id_db = 0 self.setWindowTitle("Custom Sort/Filter Model") self.resize(900, 750) def set_layout(self): filter_box: QGroupBox = self.set_filter_box() height = 92 filter_box.setMaximumHeight(height) link_box = self.set_link_box() link_box.setMaximumHeight(height) link_box.hide() stack_layout = QVBoxLayout() stack_layout.addWidget(filter_box) stack_layout.addWidget(link_box) proxyLayout = QGridLayout() proxyLayout.addWidget(self.proxyView, 0, 0) proxyLayout.addLayout(stack_layout, 1, 0) proxyLayout.addWidget(self.resView, 2, 0) proxyLayout.setRowStretch(0, 5) proxyLayout.setRowStretch(1, 0) proxyLayout.setRowStretch(2, 3) proxyGroupBox = QGroupBox("Module/Class/Method list") proxyGroupBox.setLayout(proxyLayout) mainLayout = QVBoxLayout() mainLayout.addWidget(proxyGroupBox) self.setLayout(mainLayout) return link_box def save_clicked(self, btn): { "Unload DB": save_init, "Copy all links": copy_to_clipboard }[btn.text()]() def set_filter_box(self): save_btn = QDialogButtonBox(Qt.Vertical) save_btn.addButton("Unload DB", QDialogButtonBox.ActionRole) save_btn.addButton("Copy all links", QDialogButtonBox.ActionRole) save_btn.clicked.connect(self.save_clicked) self.filterNote.addItem("All") self.filterNote.addItem("Not blank") filterTypeLabel = QLabel("&Type Filter") filterTypeLabel.setBuddy(self.filterType) filterModuleLabel = QLabel("&Module Filter") filterModuleLabel.setBuddy(self.filterModule) filterClassLabel = QLabel("&Class Filter") filterClassLabel.setBuddy(self.filterClass) filterNoteLabel = QLabel("&Remark Filter") filterNoteLabel.setBuddy(self.filterNote) filter_box = QGridLayout() filter_box.addWidget(filterTypeLabel, 0, 0) filter_box.addWidget(filterModuleLabel, 0, 1) filter_box.addWidget(filterClassLabel, 0, 2) filter_box.addWidget(filterNoteLabel, 0, 3) filter_box.addWidget(save_btn, 0, 4, 2, 1) filter_box.addWidget(self.filterType, 1, 0) filter_box.addWidget(self.filterModule, 1, 1) filter_box.addWidget(self.filterClass, 1, 2) filter_box.addWidget(self.filterNote, 1, 3) self.set_filters_combo() self.textFilterChanged() self.filterType.currentIndexChanged.connect(self.textFilterChanged) self.filterModule.currentIndexChanged.connect( self.textFilterModuleChanged) self.filterClass.currentIndexChanged.connect(self.textFilterChanged) self.filterNote.currentIndexChanged.connect(self.textFilterChanged) grp_box = QGroupBox() grp_box.setFlat(True) grp_box.setLayout(filter_box) return grp_box def set_filters_combo(self): curs = self.conn.cursor() self.filterType.clear() self.filterType.addItem("All") curs.execute(qsel3) for cc in curs: self.filterType.addItem(memb_type[cc[0]]) self.filterModule.clear() self.filterModule.addItem("All") self.filterModule.addItem("") curs.execute(qsel0) for cc in curs: self.filterModule.addItem(cc[0]) self.filterClass.clear() self.filterClass.addItem("All") curs.execute(qsel1) for cc in curs: self.filterClass.addItem(cc[0]) def textFilterModuleChanged(self): curs = self.conn.cursor() self.filterClass.clear() self.filterClass.addItem("All") if self.filterModule.currentText() == "All": curs.execute(qsel1) else: curs.execute( ("select distinct class from methods2 " "where module = ? order by class;"), (self.filterModule.currentText(), ), ) for cc in curs: self.filterClass.addItem(cc[0]) for cc in curs: self.filterType.addItem(memb_type[cc[0]]) def menu_res_view(self, pos): """ only copy to clipboard """ menu = QMenu(self) menu.addAction("clipboard") # menu.addAction("refresh") action = menu.exec_(self.resView.mapToGlobal(pos)) if action: self._to_clipboard() def _to_clipboard(self): rr = [] for rep in self.repo: pp = [str(x) for x in rep] rr.append("\t".join(pp)) QApplication.clipboard().setText("\n".join(rr)) def pop_menu(self, pos): idx = self.proxyView.indexAt(pos) menu = QMenu(self) if idx.isValid(): menu.addAction("First level only") menu.addSeparator() menu.addAction("sort by level") menu.addAction("sort by module") menu.addSeparator() menu.addAction("append row") if idx.isValid(): menu.addAction("delete rows") menu.addAction("edit links") menu.addSeparator() menu.addAction("not called") menu.addSeparator() menu.addAction("complexity") menu.addSeparator() menu.addAction("refresh") menu.addAction("reload DB") action = menu.exec_(self.proxyView.mapToGlobal(pos)) if action: self.menu_action(action.text()) def setSourceModel(self, model: QStandardItemModel): self.proxyModel.setSourceModel(model) set_columns_width(self.proxyView) set_headers(self.proxyModel, main_headers) def textFilterChanged(self): self.proxyModel.filter_changed( self.filterType.currentText(), self.filterModule.currentText(), self.filterClass.currentText(), self.filterNote.currentText(), ) def menu_action(self, act: str): { "First level only": self.first_level_only, "sort by level": self.sort_by_level, "sort by module": self.sort_by_module, "append row": self.append_row, "refresh": self.refresh, "not called": self.is_not_called, "complexity": self.recalc_complexity, "reload DB": self.reload_data, "edit links": self.edit_links, "delete rows": self.delete_selected_rows, }[act]() def recalc_complexity(self): """ add radon cyclomatic complexity repor data """ mm = self.filterModule.currentText() module = "" if mm == "All" else mm cc_list = cc_report(module) for row in cc_list: self.update_cc(row) mark_deleted_methods(cc_list, module) def update_cc(self, row: Iterable): """ @param row: CC, length, type(C/F/M), module, class, method """ sql_sel = ("select id from methods2 where " "type = ? and module = ? and class = ? and method = ?") sql_upd = "update methods2 set cc = ?, length = ? " "where id = ?" sql_ins = ("insert into methods2 (CC, length, type, module, " "Class, method, remark) values(?,?,?,?,?,?,?);") rr = (*row, ) qq = self.conn.cursor() id = qq.execute(sql_sel, rr[2:]).fetchone() if id: qq.execute(sql_upd, (*rr[:2], id[0])) else: tt = datetime.now().strftime("%Y-%m-%d %H:%M") qq.execute(sql_ins, (*rr, tt)) self.conn.commit() def is_not_called(self): qq = self.conn.cursor() qq.execute(not_called) self.set_res_model(qq, call_headers, False) set_columns_width(self.resView, proportion=(2, 2, 5, 7, 7, 2, 3, 5)) set_headers(self.resModel, call_headers) def reload_data(self): sql1 = ( "delete from methods2;", "insert into methods2 (" "ID, type, module, class, method, CC, length, remark) " "values (?, ?, ?, ?, ?, ?, ?, ?);", ) input_file = prj_path / input_meth load_table(input_file, sql1) sql2 = ( "delete from one_link;", "insert into one_link (id, call_id) values (?, ?);", ) input_file = prj_path / input_link load_table(input_file, sql2) curs = conn.cursor() curs.execute("delete from links;") conn.commit() curs.execute(all_levels_link) conn.commit() self.refresh() def refresh(self): model = QStandardItemModel(0, len(main_headers.split(",")), self.proxyView) qq = conn.cursor() qq.execute(qsel2) vv = ((x[0], memb_type[x[1]], *x[2:-2], x[-2].rjust(4), x[-1]) for x in qq) fill_in_model(model, vv) self.setSourceModel(model) def clear_report_view(self): self.repo.clear() model = QStandardItemModel(0, len(rep_headers.split(",")), self.resView) self.resModel.setSourceModel(model) set_columns_width(self.resView, proportion=(3, 2, 2, 2, 7, 7, 7, 2, 2, 1)) set_headers(self.resModel, rep_headers) self.query_time = time_run() def append_row(self): crs = conn.cursor() items = ( memb_key[self.proxyModel.type_filter], self.proxyModel.module_filter, self.proxyModel.class_filter, "", "", "", "", self.query_time[0], ) crs.execute(ins0, items) idn = crs.lastrowid conn.commit() param = ( self.proxyModel.rowCount(), (self.proxyModel.type_filter, *items[1:]), idn, ) add_row(self.proxyModel, param) def delete_selected_rows(self): idx_list = self.proxyView.selectionModel().selectedRows() idx_list.reverse() for p_idx in idx_list: if p_idx.isValid(): row = p_idx.row() self.delete_from_db(p_idx) self.proxyModel.removeRows(row, 1) def delete_from_db(self, index: QModelIndex): id_db = self.proxyModel.get_data(index, Qt.UserRole) conn.execute("delete from methods2 where id=?;", (id_db, )) conn.commit() def edit_links(self): index = self.proxyView.currentIndex() ss = self.proxyModel.get_data(index) id_db = self.proxyModel.get_data(index, Qt.UserRole) self.infLabel.setText("{:04d}: {}".format(id_db, ".".join(ss[1:4]))) self.link_box.show() qq = conn.cursor() qq.execute(sql_links.format(id_db, id_db)) self.set_res_model(qq, link_headers, True) self.repo.append((id_db, 'Sel', *ss[:4])) set_columns_width(self.resView, proportion=(3, 2, 8, 8, 8)) set_headers(self.resModel, link_headers) self.old_links = qq.execute(sql_id2.format(id_db, id_db)).fetchall() self.new_links = self.old_links[:] self.curr_id_db = id_db def set_res_model(self, qq: Iterable, headers: str, user_data: bool): self.repo.clear() for row in qq: self.repo.append(row) model = QStandardItemModel(0, len(headers.split(",")), self.resView) fill_in_model(model, self.repo, user_data) self.resModel.setSourceModel(model) def set_link_box(self): self.link_type.addItem("What") self.link_type.addItem("From") f_type = QLabel("Link &type:") f_type.setBuddy(self.link_type) ok_btn = QDialogButtonBox() ok_btn.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) ok_btn.addButton("+", QDialogButtonBox.ActionRole) ok_btn.addButton("-", QDialogButtonBox.ActionRole) ok_btn.clicked.connect(self.btn_clicked) l_box = QGridLayout() l_box.addWidget(self.infLabel, 0, 0) l_box.addWidget(f_type, 1, 0) l_box.addWidget(self.link_type, 1, 1) l_box.addWidget(ok_btn, 1, 2) l_box.setRowStretch(0, 1) l_box.setRowStretch(1, 0) l_box.setRowStretch(2, 1) grp = QGroupBox() grp.setFlat(True) grp.setLayout(l_box) return grp def btn_clicked(self, btn): { "OK": self.ok_clicked, "Cancel": self.cancel_cliked, "+": self.plus_clicked, "-": self.minus_clicked, }[btn.text()]() def ok_clicked(self): s_new = set(self.new_links) s_old = set(self.old_links) added = s_new - s_old removed = s_old - s_new if removed: for link in removed: conn.execute("delete from one_link where id=? and call_id=?;", link) if added: for link in added: conn.execute( "insert into one_link (id, call_id) values (?, ?);", link) conn.commit() self.resModel.sourceModel().clear() self.link_box.hide() if removed or added: recreate_links() def cancel_cliked(self): self.resModel.sourceModel().clear() self.link_box.hide() def plus_clicked(self): """ add link to resModel """ to_insert = self.collect_links_with_selected() row_no = self.resModel.rowCount() for row in to_insert: add_row(self.resModel, (row_no, row[1:], row[0])) row_no += 1 def collect_links_with_selected(self): """ creation links according to selected rows in proxyView and direction of link selected in self.link_type: self.curr_id_db - DB id of edited method (object) link is a pair of ids (what called, called from) """ stat = self.link_type.currentText() idx_sel = self.proxyView.selectedIndexes() idx_col0 = [ix for ix in idx_sel if ix.column() == 0] to_insert = [] for idx in idx_col0: id = self.proxyModel.get_data(idx, Qt.UserRole) link = (id, self.curr_id_db) if stat == "What" else (self.curr_id_db, id) if link in self.new_links or link[::-1] in self.new_links: continue self.new_links.append(link) row = self.proxyModel.get_data(idx)[:-1] to_insert.append([id, stat] + row) return to_insert def minus_clicked(self): idx_sel = self.resView.selectionModel().selectedRows() idx_sel.reverse() for idx in idx_sel: self.remove_in_new_links(idx) self.remove_in_model(idx) def remove_in_new_links(self, index: QModelIndex): link_type = self.resModel.data(index) id_db = self.resModel.data(index, Qt.UserRole) link = ((id_db, self.curr_id_db) if link_type == "What" else (self.curr_id_db, id_db)) self.new_links.remove(link) def remove_in_model(self, index): row = index.row() self.resModel.removeRows(row, 1) def get_selected_methods(self): """ Returns lists of rows selected in the proxyView: @return: list of selected methods """ indexes = self.proxyView.selectionModel().selectedRows() methods = [] for idx in indexes: methods.append(self.proxyModel.get_data(idx)) return methods def first_level_only(self): """ select method to create link-report depending on number of selected methods @return: None """ self.clear_report_view() self.sort_key = sort_keys["by module"] ids = self.proxyView.selectionModel().selectedRows() opt = len(ids) if len(ids) < 3 else "more than 2" { 1: self.selected_only_one, 2: self.selected_exactly_two, "more than 2": self.selected_more_than_two }[opt](1) def prep_sql(self, sql: str, lvl: int = 0) -> str: mod = self.filterModule.currentText() cls = self.filterClass.currentText() return (sql + ("" if mod == "All" else where_mod.format(mod)) + ("" if cls == "All" else where_cls.format(cls)) + (and_level if lvl else "") + group_by) def selected_only_one(self, lvl): pre = (self.query_time[1], "Sel", "") names = self.get_selected_methods() self.sorted_report(self.repo, (pre, names, "")) lst = self.first_1_part(what_call_1, lvl) pre = (self.query_time[1], "What", "") self.sorted_report(self.repo, (pre, lst, "")) lst = self.first_1_part(called_from_1, lvl) pre = (self.query_time[1], "From", "") self.sorted_report(self.repo, (pre, lst, "")) fill_in_model(self.resModel.sourceModel(), self.repo, user_data=False) def first_1_part(self, sql: str, lvl: int): p_sql = self.prep_sql(sql, lvl) ids = self.get_db_ids() lst = self.exec_sql_b(p_sql, ids) return [(*map(str, x), ) for x in lst] def get_db_ids(self): ids = [] indexes = self.proxyView.selectionModel().selectedRows() for idx in indexes: ids.append(self.proxyModel.get_data(idx, Qt.UserRole)) return ids def selected_exactly_two(self, lvl): pre = (self.query_time[1], "Sel") names = self.get_selected_methods() n_names = [("A", *names[0]), ("B", *names[1])] self.sorted_report(self.repo, (pre, n_names, "")) self.report_four("What", lvl) self.report_four("From", lvl) fill_in_model(self.resModel.sourceModel(), self.repo, user_data=False) def report_four(self, what, lvl): sql = {"What": what_call_1, "From": called_from_1}[what] p_sql = self.prep_sql(sql, lvl) ids = self.get_db_ids() lst_a = self.first_2_part((ids[0], ), sql) lst_b = self.first_2_part((ids[1], ), sql) self.sorted_report(self.repo, ( (self.query_time[1], what, "A | B"), list(set(lst_a) | set(lst_b)), "", )) self.sorted_report(self.repo, ( (self.query_time[1], what, "A - B"), list(set(lst_a) - set(lst_b)), "", )) self.sorted_report(self.repo, ( (self.query_time[1], what, "B - A"), list(set(lst_b) - set(lst_a)), "", )) self.sorted_report(self.repo, ( (self.query_time[1], what, "A & B"), list(set(lst_a) & set(lst_b)), "", )) def first_2_part(self, ids: Iterable, sql: str) -> list: lst = self.exec_sql_b(sql, ids) return [(*map(str, x), ) for x in lst] def selected_more_than_two(self, lvl): pre = (self.query_time[1], "Sel", "") names = self.get_selected_methods() self.sorted_report(self.repo, (pre, names, "")) self.report_23("What", lvl) self.report_23("From", lvl) fill_in_model(self.resModel.sourceModel(), self.repo, user_data=False) def report_23(self, param, lvl): sql = {"What": what_id, "From": from_id}[param] ids = self.get_db_ids() links = self.exec_sql_2(ids, lvl, sql) rep_prep = pre_report(links) self.methods_by_id_list(three_or_more, rep_prep[0:3:2], param, "ALL") self.methods_by_id_list(three_or_more, rep_prep[1:], param, "ANY") def exec_sql_2(self, ids, lvl, sql) -> list: """ @param: ids - list of id of selected rows @param: lvl - level of call: all or only first @param: sql - select methods by type of link: "call What"/"called From" @return: list of tuples (method_id, level of call) """ res = [] curs = self.conn.cursor() loc_sql = sql.format("and level=1" if lvl else "") for id_ in ids: w_id = curs.execute(loc_sql, (id_, )) res.append(dict(w_id)) return res def methods_by_id_list(self, sql: str, ids: list, what: str, all_any: str): if ids: cc = self.exec_sql_f(sql, (",".join((map(str, ids[0]))), )) pre = (self.query_time[1], what, all_any) vv = insert_levels(cc, ids[1]) self.sorted_report(self.repo, (pre, vv, "")) def sort_by_level(self): """ Show lists of methods sorted by level @param ids: indexes of selected methods @param names: selected methods as (module, class, method) list @return: None """ self.clear_report_view() self.sort_key = sort_keys["by level"] self.sel_count_handle() def sort_by_module(self): """ Show lists of methods sorted by module name @param ids: indexes of selected methods @param names: selected methods as (module, class, method) list @return: None """ self.clear_report_view() self.sort_key = sort_keys["by module"] self.sel_count_handle() def sel_count_handle(self): """ This method does the same as the "first_level_only" method @return: None """ ids = self.proxyView.selectionModel().selectedRows() opt = len(ids) if len(ids) < 3 else "more than 2" { 1: self.selected_only_one, 2: self.selected_exactly_two, "more than 2": self.selected_more_than_two }[opt](0) def exec_sql_b(self, sql: str, sql_par: tuple): """ exesute SQL - bind parameters with '?' @param sql: @param sql_par: @return: list of lists of strings """ curs = self.conn.cursor() cc = curs.execute(sql, sql_par) return [(*map(str, x), ) for x in cc] def exec_sql_f(self, sql: str, sql_par: tuple): """ exesute SQL - insert parameters into SQL with str.format method @param sql: @param sql_par: @return: list of lists of strings """ curs = self.conn.cursor() cc = curs.execute(sql.format(*sql_par)) return [(*map(str, x), ) for x in cc] def sorted_report(self, report: list, rep_data: tuple): pre, lst, post = rep_data lst.sort(key=self.sort_key) for ll in lst: report.append((*pre, *ll, *post))