class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.model = Project() self.view = QTreeView(self) self.view.setModel(self.model) self.view.setDragEnabled(True) self.view.setDragDropMode(QAbstractItemView.InternalMove) self.setCentralWidget(self.view) def mousePressEvent(self, e): print(e.x(), e.y()) return QMainWindow.mousePressEvent(self, e)
class MainWidget(QWidget): def __init__(self, parent=None): super(MainWidget, self).__init__(parent) # define tree view self.treeView = QTreeView(self) # insert line edit self.lineEdit1 = QLineEdit(self) self.lineEdit2 = QLineEdit(self) # insert a button group widget self.buttonGroupWidget1 = ButtonGroupWidget(self) self.buttonGroupWidget1.setTitle("Select option") self.buttonGroupWidget1.addRadioButton("Option 1", 1) self.buttonGroupWidget1.addRadioButton("Option 2", 2) self.buttonGroupWidget1.addRadioButton("Option 3", 3) layoutMain = QVBoxLayout(self) layoutMain.addWidget(self.treeView) layoutMain.addWidget(self.lineEdit1) layoutMain.addWidget(self.lineEdit2) layoutMain.addWidget(self.buttonGroupWidget1) # Create the data model and map the model to the widgets. self._model = DataModel() self.treeView.setModel(self._model) self._dataMapper = QDataWidgetMapper() self._dataMapper.setModel(self._model) # the mapping works fine for line edits and combo boxes self._dataMapper.addMapping(self.lineEdit1, 0) self._dataMapper.addMapping(self.lineEdit2, 1) # mapping to custom property self._dataMapper.addMapping(self.buttonGroupWidget1, 1, "selectedOption") self.treeView.selectionModel().currentChanged.connect( self.setSelection) def setSelection(self, current): parent = current.parent() # self._dataMapper.setRootIndex(parent) self._dataMapper.setCurrentModelIndex(current)
class FileSystemView(QWidget): def __init__(self, dir_path): super().__init__() # appWidth = 800 # appHeight = 300 self.setWindowTitle('File System Viewer') # self.setGeometry(300, 300, appWidth, appHeight) self.model = QFileSystemModel() self.model.setRootPath(dir_path) self.tree = QTreeView() self.tree.setModel(self.model) self.tree.setRootIndex(self.model.index(dir_path)) self.tree.setColumnWidth(0, 250) self.tree.setAlternatingRowColors(True) layout = QVBoxLayout() layout.addWidget(self.tree) self.setLayout(layout)
def model_view(): app = QApplication() splitter = QSplitter() model = QFileSystemModel() model.setRootPath(QDir.currentPath()) parentIndex = model.index(QDir.currentPath()) tree = QTreeView(splitter) tree.setModel(model) tree.setRootIndex(parentIndex) list = QListView(splitter) list.setModel(model) list.setRootIndex(parentIndex) table = QTableView(splitter) table.setModel(model) table.setRootIndex(parentIndex) splitter.setWindowTitle("Two views onto the same file system model") splitter.show() app.exec_()
class TreeView(QWidget): def __init__(self, path=None): super(TreeView, self).__init__() self.model = QFileSystemModel() if path: self.selectedPath = path self.model.setRootPath(self.selectedPath) else: self.selectedPath = QDir.currentPath() self.model.setRootPath(self.selectedPath) mainLayout = QVBoxLayout() mainLayout.setContentsMargins(0, 0, 0, 0) self.setLayout(mainLayout) self.tree = QTreeView() self.tree.setModel(self.model) self.tree.setRootIndex(self.model.index(self.selectedPath)) self.tree.setColumnHidden(1, True) self.tree.setColumnHidden(2, True) self.tree.setColumnHidden(3, True) mainLayout.addWidget(self.tree) def changePath(self, path): self.selectedPath = path self.tree.setModel(None) self.model.setRootPath(self.selectedPath) self.model.setNameFilters( ["*.png", "*.jpg", "*.bmp", "*.svg", "*.tiff", "*.gif"]) self.tree.setModel(self.model) self.tree.setRootIndex(self.model.index(self.selectedPath)) self.tree.setColumnHidden(1, True) self.tree.setColumnHidden(2, True) self.tree.setColumnHidden(3, True) self.repaint()
class DownloadDirTreeWidget(QWidget): def __init__(self, root_path) -> None: QWidget.__init__(self) # self.index stores the index of the latest item which is clicked # self.root_path is the path to the folder currently showing self.index = None self.root_path = os.path.abspath(root_path) self.dir_view = QTreeView() self.model = QFileSystemModel(self.dir_view) self.model.setRootPath(self.root_path) self.dir_view.clicked.connect(self.onFileItemClicked) self.dir_view.doubleClicked.connect(self.onFileItemDoubleClicked) self.dir_view.setModel(self.model) self.dir_view.setRootIndex(self.model.index(self.root_path)) open_button = QPushButton("Open") open_button.clicked.connect(self.openFile) open_in_file_explorer_button = QPushButton("Open in File Explorer") open_in_file_explorer_button.clicked.connect(self.openInFileExplorer) self.root_path_line_edit = QLineEdit(self.root_path) self.root_path_line_edit.returnPressed.connect( self.onChangeLineEditReturned) self.root_path_line_edit.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.root_path_line_edit.adjustSize() change_path_button = QPushButton('Change Directory') change_path_button.clicked.connect(self.onChangeButtonClicked) addressCompleter = QCompleter() addressCompleter.setModel(self.model) self.root_path_line_edit.setCompleter(addressCompleter) # Set layout layout = QGridLayout() layout.addWidget(self.root_path_line_edit, 0, 0, 1, 1) layout.addWidget(change_path_button, 0, 1, 1, 1) layout.addWidget(self.dir_view, 1, 0, 1, 2) layout.addWidget(open_button, 2, 0, 1, 1) layout.addWidget(open_in_file_explorer_button, 2, 1, 1, 1) layout.setMargin(0) self.setLayout(layout) def setRootPath(self, root_path): self.root_path = os.path.abspath(root_path) def openFile(self): if self.index is not None: file_path = self.model.filePath(self.index).replace('/', '\\') is_dir = self.model.isDir(self.index) # If is file, open with default program # If is directory, open with file explorer if is_dir is False: os.startfile(file_path, 'open') else: subprocess.run(['explorer', file_path]) def openInFileExplorer(self): if self.index is None: file_path = self.model.filePath(self.index).replace('/', '\\') subprocess.run(['explorer', '/select,', file_path]) def onFileItemClicked(self, index): # When clicked, resize and update self.index self.dir_view.resizeColumnToContents(0) self.index = index def onFileItemDoubleClicked(self, index): # When double clicked, update self.index and open the file directly self.index = index if self.sender().model().isDir(index) is False: self.openFile() def onChangeButtonClicked(self): new_path = QFileDialog.getExistingDirectory(self, 'Change Directory', self.root_path) self.changeRootPath(new_path) def onChangeLineEditReturned(self): new_path = self.root_path_line_edit.text() if os.path.isdir(new_path): self.changeRootPath(new_path) else: subprocess.run(['explorer', new_path]) self.root_path_line_edit.setText(self.root_path) def changeRootPath(self, new_path: str): if os.path.exists(new_path): self.root_path = os.path.abspath(new_path) self.dir_view.setRootIndex(self.model.index(self.root_path)) self.root_path_line_edit.setText(self.root_path)
class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) #QWidget.__init__(self) self.createActions() self.createMenus() self.setWindowTitle("Map") self.painter = MyPainter() self.createLayout() self.fileName = '' #按钮 选取状态 self.state = False #QObject.connect(self.painter, QtCore.SIGNAL("get_point_id()"), self, QtCore.SLOT("stateDisplay()")) self.painter.valueChanged.connect(self.stateDisplay) self.button3.clicked.connect(self.clearPoint) self.button0.clicked.connect(self.displayPoint) self.button4.clicked.connect(self.createLane) self.button5.clicked.connect(self.createConn) #读取点号 def stateDisplay(self): self.brower1.setText(str(self.painter.start_point.id)) self.brower2.setText(str(self.painter.end_point.id)) def clearPoint(self): self.painter.target_point = 0 self.painter.start_point.id = '' self.painter.end_point.id = '' self.painter.repaint() self.brower1.setText(str(self.painter.start_point.id)) self.brower2.setText(str(self.painter.end_point.id)) #根据输入框内id显示点号。当不显示时,需点击鼠标两下,由self.target_point的设计决定 def displayPoint(self): self.painter.start_point.id = int(self.brower1.text()) self.painter.start_point.draw_point = self.painter.allPoint[ self.painter.start_point.id].draw_point self.painter.end_point.id = int(self.brower2.text()) self.painter.end_point.draw_point = self.painter.allPoint[ self.painter.end_point.id].draw_point self.painter.repaint() #可能需要加入条件,起始点id小于终止点id。conn同 def createLane(self): #self.painter.data msgBox = QtWidgets.QMessageBox() if (self.painter.start_point.id == '' or self.painter.end_point.id == ''): msgBox.setText("输入错误") else: startline = self.painter.data[self.painter.data[0] == self.painter.start_point.id] endline = self.painter.data[self.painter.data[0] == self.painter.end_point.id] start_index = startline.index.values[0] end_index = endline.index.values[0] output_data = self.painter.data.iloc[start_index:end_index + 1, :] #输出 output_data[0] = output_data[0].astype('int64') num = len(os.listdir("outputDATA\\Lane")) output_data.to_csv('outputDATA\\Lane\\' + str(num) + '.txt', sep='\t', index=False, header=False) #此处不加等于即路段留有衔接点 #存有部分点没删除?for in和remove的问题。 #12.12 保留路段末尾处衔接点,用于生成道路 for item in self.painter.allPoint: if int(item.id) >= int(self.painter.start_point.id) and int( item.id) < int(self.painter.end_point.id): self.painter.displayPoint.remove(item) #print(item.id,self.painter.start_point.id,self.painter.end_point.id) self.painter.repaint() msgBox.setText("已生成目标道路Lane" + str(num)) msgBox.exec_() def createConn(self): #self.painter.data msgBox = QtWidgets.QMessageBox() if (self.painter.start_point.id == '' or self.painter.end_point.id == ''): msgBox.setText("输入错误") else: startline = self.painter.data[self.painter.data[0] == self.painter.start_point.id] endline = self.painter.data[self.painter.data[0] == self.painter.end_point.id] start_index = startline.index.values[0] end_index = endline.index.values[0] output_data = self.painter.data.iloc[start_index:end_index + 1, :] #输出 output_data[0] = output_data[0].astype('int64') num = len(os.listdir("outputDATA\\Conn")) output_data.to_csv('outputDATA\\Conn\\' + str(num) + '.txt', sep='\t', index=False, header=False) for item in self.painter.allPoint: if int(item.id) >= int(self.painter.start_point.id) and int( item.id) < int(self.painter.end_point.id): self.painter.displayPoint.remove(item) #print(item.id,self.painter.start_point.id,self.painter.end_point.id) self.painter.repaint() msgBox.setText("已生成目标道路Conn" + str(num)) msgBox.exec_() def fileList(self): #树状文件列表 self.model = QDirModel() self.view = QTreeView(self) self.view.setModel(self.model) self.view.setColumnHidden(1, True) self.view.setColumnHidden(2, True) self.view.setColumnHidden(3, True) self.view.setHeaderHidden(True) #self.view.setRootIndex(self.model.index("c:/")) self.layout.addWidget(self.view, 0, 0, 2, 1) def createLayout(self): self.widget = QWidget() self.setCentralWidget(self.widget) #栅格布局 self.layout = QGridLayout() #设置拉伸因子,每一列的比例 self.layout.setColumnStretch(0, 1) self.layout.setColumnStretch(1, 4) #控件 self.fileList() self.layout.addWidget(self.painter, 0, 1) #页面 self.button1 = QtWidgets.QLabel("起始点") self.brower1 = QtWidgets.QLineEdit() self.button2 = QtWidgets.QLabel("终止点") self.brower2 = QtWidgets.QLineEdit() self.button3 = QtWidgets.QPushButton("清除") self.button0 = QtWidgets.QPushButton("显示点号") self.button4 = QtWidgets.QPushButton("生成Lane") self.button5 = QtWidgets.QPushButton("生成Conn") labelLayout1 = QtWidgets.QHBoxLayout() labelLayout2 = QtWidgets.QHBoxLayout() labelLayout1.addWidget(self.button1) labelLayout1.addWidget(self.brower1) labelLayout1.addWidget(self.button2) labelLayout1.addWidget(self.brower2) labelLayout1.addWidget(self.button3) labelLayout2.addWidget(self.button0) labelLayout2.addWidget(self.button4) labelLayout2.addWidget(self.button5) self.layout.addLayout(labelLayout1, 1, 1) self.layout.addLayout(labelLayout2, 2, 1) self.widget.setLayout(self.layout) def createActions(self): self.openRoadAct = QtWidgets.QAction("加载道路数据", self, shortcut="Ctrl+O", triggered=self.openRoad) self.openPointAct = QtWidgets.QAction("加载点数据", self, shortcut="Ctrl+P", triggered=self.openPoint) #self.saveAsAct = QtWidgets.QAction("&Save As...", self, shortcut="Ctrl+S",triggered=self.saveAs) self.exitAct = QtWidgets.QAction("退出", self, shortcut="Ctrl+Q", triggered=self.close) #self.aboutAct = QtWidgets.QAction("&About", self, triggered=self.about) #self.aboutQtAct = QtWidgets.QAction("About &Qt", self,triggered=QtWidgets.qApp.aboutQt) def createMenus(self): self.fileMenu = self.menuBar().addMenu("&打开") self.fileMenu.addAction(self.openRoadAct) self.fileMenu.addAction(self.openPointAct) #self.fileMenu.addAction(self.saveAsAct) self.fileMenu.addAction(self.exitAct) self.menuBar().addSeparator() #self.helpMenu = self.menuBar().addMenu("&Help") #self.helpMenu.addAction(self.aboutAct) #self.helpMenu.addAction(self.aboutQtAct) #加载NovAtel文件数据 def openRoad(self): fileName = QtWidgets.QFileDialog.getOpenFileName( self, "加载数据", QtCore.QDir.currentPath(), "Files (*.txt *.csv)")[0] if not fileName: return inFile = QtCore.QFile(fileName) if not inFile.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text): QtWidgets.QMessageBox.warning( self, "Text", "Cannot read file %s:\n%s." % (fileName, inFile.errorString())) return #data = QtCore.QTextStream(inFile) #self.line=data.readAll() inFile.close() self.painter.addData(fileName) #加载MissionPoint文件数据 #getOpenFileNames()函数,可打开多个文件 def openPoint(self): fileName = QtWidgets.QFileDialog.getOpenFileName( self, "加载数据", QtCore.QDir.currentPath(), "Files (*.txt *.csv)")[0] if not fileName: return inFile = QtCore.QFile(fileName) if not inFile.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text): QtWidgets.QMessageBox.warning( self, "Text", "Cannot read file %s:\n%s." % (fileName, inFile.errorString())) return inFile.close() self.painter.addPointData(fileName)
source_model.invisibleRootItem().appendRow(row) list.append("FancyTextNumber {}".format(i)) # Needed by QMLModelViewClient role_names = { Qt.DisplayRole: QByteArray(b'_text'), Qt.BackgroundRole: QByteArray(b'_color') } source_model.setItemRoleNames(role_names) roles = [Qt.DisplayRole, Qt.BackgroundRole] print("Creating registry host") node = QRemoteObjectRegistryHost(QUrl("local:registry")) node2 = QRemoteObjectHost(QUrl("local:replica"), QUrl("local:registry")) node2.enableRemoting(source_model, "RemoteModel", roles) view = QTreeView() view.setWindowTitle("SourceView") view.setModel(source_model) view.show() handler = TimerHandler(source_model) QTimer.singleShot(5000, handler.change_data) QTimer.singleShot(10000, handler.insert_data) QTimer.singleShot(11000, handler.change_flags) QTimer.singleShot(12000, handler.remove_data) QTimer.singleShot(13000, handler.move_data) sys.exit(app.exec_())
class ApplicationPage(QWidget): host: "Host" def __init__(self, host: "Host", **kwargs): super().__init__(**kwargs) self.host = host self.model = AppStoreModel(self, self.host.app_store) self._layout() def _layout(self): layout = QHBoxLayout(self) self.setLayout(layout) self.tree = QTreeView(self) self.tree.setModel(self.model) self.tree.setUniformRowHeights(True) self.tree.setColumnWidth(0, 200) self.tree.setDragEnabled(True) self.tree.setDragDropMode(QAbstractItemView.InternalMove) self.tree.viewport().setAcceptDrops(True) layout.addWidget(self.tree, 1) buttons = QVBoxLayout() buttons.setAlignment(Qt.AlignTop) add_button = QPushButton(QIcon.fromTheme("list-add"), "", self) add_button.setToolTip("Add application") add_button.clicked.connect(self.on_add) buttons.addWidget(add_button) mkdir_button = QPushButton(QIcon.fromTheme("folder-new"), "", self) mkdir_button.setToolTip("Make directory") mkdir_button.clicked.connect(self.on_mkdir) buttons.addWidget(mkdir_button) delete_button = QPushButton(QIcon.fromTheme("list-remove"), "", self) delete_button.setToolTip("Remove selected item") delete_button.clicked.connect(self.on_delete) buttons.addWidget(delete_button) layout.addLayout(buttons) def on_add(self): dialog = QInputDialog(self) dialog.setLabelText("Enter appconfig.json URL") if dialog.exec_() == QInputDialog.Rejected: return app_url = dialog.textValue().strip() self.host.app_store.add_app_ui([app_url]) def on_mkdir(self): dialog = QInputDialog(self) dialog.setLabelText("Directory name") if dialog.exec_() == QInputDialog.Rejected: return dirname = dialog.textValue().strip() if not dirname or "/" in dirname: QMessageBox.critical(self, "Invalid input", "This directory name cannot be used") return self.host.app_store.mkdir(dirname) def on_delete(self): data = self.model.mimeData(self.tree.selectedIndexes()) if not data: return data = json.loads( data.data("application/x-qabstractitemmodeldatalist").data()) if data["type"] == "dir": confirm = QMessageBox.question( self, "Remove folder", f"Remove {data['id']}?\n\nAll applications will be moved to the top level", ) if confirm == QMessageBox.StandardButton.No: return self.host.app_store.rmdir(data["id"]) else: app = self.host.app_store[data["id"]] confirm = QMessageBox.question( self, "Remove application", f"Remove {app['appName']}?", ) if confirm == QMessageBox.StandardButton.No: return self.host.app_store.remove_app(data["id"])
class Snippets(QDialog): def __init__(self, parent=None): super(Snippets, self).__init__(parent) # Create widgets self.setWindowModality(Qt.NonModal) self.title = QLabel(self.tr("Snippet Editor")) self.saveButton = QPushButton(self.tr("Save")) self.revertButton = QPushButton(self.tr("Revert")) self.clearHotkeyButton = QPushButton(self.tr("Clear Hotkey")) self.setWindowTitle(self.title.text()) self.newFolderButton = QPushButton("New Folder") self.deleteSnippetButton = QPushButton("Delete") self.newSnippetButton = QPushButton("New Snippet") self.edit = QPlainTextEdit() self.resetting = False self.columns = 3 self.keySequenceEdit = QKeySequenceEdit(self) self.currentHotkey = QKeySequence() self.currentHotkeyLabel = QLabel("") self.currentFileLabel = QLabel() self.currentFile = "" self.snippetDescription = QLineEdit() self.snippetEditsPending = False self.clearSelection() #Set Editbox Size font = getMonospaceFont(self) self.edit.setFont(font) font = QFontMetrics(font) self.edit.setTabStopWidth(4 * font.width(' ')); #TODO, replace with settings API #Files self.files = QFileSystemModel() self.files.setRootPath(snippetPath) self.files.setNameFilters(["*.py"]) #Tree self.tree = QTreeView() self.tree.setModel(self.files) self.tree.setSortingEnabled(True) self.tree.hideColumn(2) self.tree.sortByColumn(0, Qt.AscendingOrder) self.tree.setRootIndex(self.files.index(snippetPath)) for x in range(self.columns): #self.tree.resizeColumnToContents(x) self.tree.header().setSectionResizeMode(x, QHeaderView.ResizeToContents) treeLayout = QVBoxLayout() treeLayout.addWidget(self.tree) treeButtons = QHBoxLayout() treeButtons.addWidget(self.newFolderButton) treeButtons.addWidget(self.newSnippetButton) treeButtons.addWidget(self.deleteSnippetButton) treeLayout.addLayout(treeButtons) treeWidget = QWidget() treeWidget.setLayout(treeLayout) # Create layout and add widgets buttons = QHBoxLayout() buttons.addWidget(self.clearHotkeyButton) buttons.addWidget(self.keySequenceEdit) buttons.addWidget(self.currentHotkeyLabel) buttons.addWidget(self.revertButton) buttons.addWidget(self.saveButton) description = QHBoxLayout() description.addWidget(QLabel(self.tr("Description: "))) description.addWidget(self.snippetDescription) vlayoutWidget = QWidget() vlayout = QVBoxLayout() vlayout.addWidget(self.currentFileLabel) vlayout.addWidget(self.edit) vlayout.addLayout(description) vlayout.addLayout(buttons) vlayoutWidget.setLayout(vlayout) hsplitter = QSplitter() hsplitter.addWidget(treeWidget) hsplitter.addWidget(vlayoutWidget) hlayout = QHBoxLayout() hlayout.addWidget(hsplitter) self.showNormal() #Fixes bug that maximized windows are "stuck" self.settings = QSettings("Vector 35", "Snippet Editor") if self.settings.contains("ui/snippeteditor/geometry"): self.restoreGeometry(self.settings.value("ui/snippeteditor/geometry")) else: self.edit.setMinimumWidth(80 * font.averageCharWidth()) self.edit.setMinimumHeight(30 * font.lineSpacing()) # Set dialog layout self.setLayout(hlayout) # Add signals self.saveButton.clicked.connect(self.save) self.revertButton.clicked.connect(self.loadSnippet) self.clearHotkeyButton.clicked.connect(self.clearHotkey) self.tree.selectionModel().selectionChanged.connect(self.selectFile) self.newSnippetButton.clicked.connect(self.newFileDialog) self.deleteSnippetButton.clicked.connect(self.deleteSnippet) self.newFolderButton.clicked.connect(self.newFolder) def registerAllSnippets(self): for action in list(filter(lambda x: x.startswith("Snippet\\"), UIAction.getAllRegisteredActions())): UIActionHandler.globalActions().unbindAction(action) UIAction.unregisterAction(action) for snippet in includeWalk(snippetPath, ".py"): (snippetDescription, snippetKey, snippetCode) = loadSnippetFromFile(snippet) if not snippetDescription: actionText = "Snippet\\" + snippet else: actionText = "Snippet\\" + snippetDescription UIAction.registerAction(actionText, snippetKey) UIActionHandler.globalActions().bindAction(actionText, UIAction(makeSnippetFunction(snippetCode))) def clearSelection(self): self.keySequenceEdit.clear() self.currentHotkey = QKeySequence() self.currentHotkeyLabel.setText("") self.currentFileLabel.setText("") self.snippetDescription.setText("") self.edit.setPlainText("") def reject(self): self.settings.setValue("ui/snippeteditor/geometry", self.saveGeometry()) if self.snippetChanged(): question = QMessageBox.question(self, self.tr("Discard"), self.tr("You have unsaved changes, quit anyway?")) if question != QMessageBox.StandardButton.Yes: return self.accept() def newFolder(self): (folderName, ok) = QInputDialog.getText(self, self.tr("Folder Name"), self.tr("Folder Name: ")) if ok and folderName: index = self.tree.selectionModel().currentIndex() selection = self.files.filePath(index) if QFileInfo(selection).isDir(): QDir(selection).mkdir(folderName) else: QDir(snippetPath).mkdir(folderName) def selectFile(self, new, old): if (self.resetting): self.resetting = False return newSelection = self.files.filePath(new.indexes()[0]) if QFileInfo(newSelection).isDir(): self.clearSelection() return if old.length() > 0: oldSelection = self.files.filePath(old.indexes()[0]) if not QFileInfo(oldSelection).isDir() and self.snippetChanged(): question = QMessageBox.question(self, self.tr("Discard"), self.tr("Snippet changed. Discard changes?")) if question != QMessageBox.StandardButton.Yes: self.resetting = True self.tree.selectionModel().select(old, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) return False self.currentFile = newSelection self.loadSnippet() def loadSnippet(self): self.currentFileLabel.setText(QFileInfo(self.currentFile).baseName()) log_debug("Loading %s as a snippet." % self.currentFile) (snippetDescription, snippetKey, snippetCode) = loadSnippetFromFile(self.currentFile) self.snippetDescription.setText(snippetDescription) if snippetDescription else self.snippetDescription.setText("") self.keySequenceEdit.setKeySequence(snippetKey[0]) if len(snippetKey) != 0 else self.keySequenceEdit.setKeySequence(QKeySequence("")) self.edit.setPlainText(snippetCode) if snippetCode else self.edit.setPlainText("") def newFileDialog(self): (snippetName, ok) = QInputDialog.getText(self, self.tr("Snippet Name"), self.tr("Snippet Name: ")) if ok and snippetName: if not snippetName.endswith(".py"): snippetName += ".py" index = self.tree.selectionModel().currentIndex() selection = self.files.filePath(index) if QFileInfo(selection).isDir(): open(os.path.join(selection, snippetName), "w").close() else: open(os.path.join(snippetPath, snippetName), "w").close() log_debug("Snippet %s created." % snippetName) def deleteSnippet(self): selection = self.tree.selectedIndexes()[::self.columns][0] #treeview returns each selected element in the row snippetName = self.files.fileName(selection) question = QMessageBox.question(self, self.tr("Confirm"), self.tr("Confirm deletion: ") + snippetName) if (question == QMessageBox.StandardButton.Yes): log_debug("Deleting snippet %s." % snippetName) self.clearSelection() self.files.remove(selection) def snippetChanged(self): if (self.currentFile == "" or QFileInfo(self.currentFile).isDir()): return False (snippetDescription, snippetKey, snippetCode) = loadSnippetFromFile(self.currentFile) if (not snippetCode): return False if len(snippetKey) == 0 and not self.keySequenceEdit.keySequence().isEmpty(): return True if len(snippetKey) != 0 and snippetKey[0] != self.keySequenceEdit.keySequence(): return True return self.edit.toPlainText() != snippetCode or \ self.snippetDescription.text() != snippetDescription def save(self): log_debug("Saving snippet %s" % self.currentFile) outputSnippet = open(self.currentFile, "w") outputSnippet.write("#" + self.snippetDescription.text() + "\n") outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n") outputSnippet.write(self.edit.toPlainText()) outputSnippet.close() self.registerAllSnippets() def clearHotkey(self): self.keySequenceEdit.clear()
class MainWindow(QMainWindow): def __init__(self): super().__init__() # SET => App Icon self.icon = QIcon("img/iconXLNK.png") # End self.tree_view = None self.file_system_model = None # SET => Window Icon self.setWindowIcon(self.icon) # End # WSET => Window Title self.setWindowTitle("XLNK | Data Manager") # End # Menus self.menu = self.menuBar() self.file_menu = self.menu.addMenu("&File") self.edit_menu = self.menu.addMenu("&Edit") self.view_menu = self.menu.addMenu("&View") self.help_menu = self.menu.addMenu("&Help") # End # =================== # Menu Button Actions # Exit QAction exit_action = QAction("Exit", self) exit_action.setShortcut(QKeySequence.Quit) exit_action.triggered.connect(self.close) self.file_menu.addAction(exit_action) # End # End # Tool Bar toolbar = QToolBar(self) self.addToolBar(toolbar) # delete action on toolbar delete_action_tb = QAction("DELETE TABLE ROW", self) delete_action_tb.setStatusTip("Obrisi Red U Tabeli") delete_action_tb.triggered.connect(self.delete_table_row_tb) toolbar.addAction(delete_action_tb) # Dock Widget dock_widget = QDockWidget("EXPLORER", self) # File System Model self.file_system_model = QFileSystemModel() self.file_system_model.setRootPath(QDir.currentPath()) # SET => Tree View MOdel self.tree_view = QTreeView() self.tree_view.setModel(self.file_system_model) self.tree_view.setRootIndex( self.file_system_model.index(QDir.currentPath() + "/data")) self.tree_view.clicked.connect(self.file_clicked_handler) dock_widget.setWidget(self.tree_view) dock_widget.setFloating(False) self.addDockWidget(Qt.LeftDockWidgetArea, dock_widget) # QLabel qlabel = QLabel(self) qlabel.setText("Welcome to XLNK.") # Central Widget self.clicked_file = None self.setCentralWidget(qlabel) self.showMaximized() # ToolBar Functions # TODO def delete_table_row_tb(self): print("Ugraditi funkciju za brisanje reda iz tabele.") def file_clicked_handler(self, index): index = self.tree_view.currentIndex() file_clicked_param = os.path.basename( self.file_system_model.filePath(index)) self.clicked_file = file_clicked_param self.status_bar = QStatusBar(self) self.status_bar.showMessage( "File Selected: {}".format(file_clicked_param), 3000) self.setStatusBar(self.status_bar) self.workspace_widget = WorkSpaceWidget(self, self.clicked_file) self.setCentralWidget(self.workspace_widget)
class MyWidget(QtWidgets.QWidget): @Slot() def run_report(self): dialog = ReportNameDialog() if dialog.exec_(): metadata_fields = {} item_model = self.list.model() for i in range(item_model.rowCount()): qmodel_index = item_model.index(i, 0) index_name = item_model.item(i, 0).text() if item_model.itemData(qmodel_index).get(10) == 2: metadata_fields[index_name] = "" if hasattr(item_model.item(i, 1), "text"): index_value = item_model.item(i, 1).text() if index_name == DOCUMENT_TYPE: if index_value == TYPE_ASSET: index_value = "IO" if index_value == TYPE_FOLDER: index_value = "SO" if index_value == TYPE_ANY: index_value = "" metadata_fields[index_name] = index_value self.progress = QProgressDialog( "Creating Report\n\nPlease Wait...", None, 0, 100) self.progress.setModal(True) self.progress.setWindowTitle("Report") self.progress.setAutoClose(True) self.progress.setAutoReset(True) self.callback = CallBack() worker = Worker(self.client, self.search_value.text(), dialog.report(), metadata_fields, self.callback, dialog.auto_report()) self.callback.change_value.connect(self.progress.setValue) self.callback.max_value.connect(self.progress.setMaximum) self.callback.reset_dialog.connect(self.progress.reset) self.threadpool.start(worker) self.progress.show() def __init__(self): super().__init__() self.threadpool = QThreadPool() self.threadpool.setMaxThreadCount(1) self.setWindowTitle("Preservica Solr Reporting") self.windowModality() self.progress = None self.callback = None self.search_label = QtWidgets.QLabel( "Main Search Term (% returns everything)") self.search_value = QtWidgets.QLineEdit("%") self.help_label = QtWidgets.QLabel( "Select from the list of indexed fields below to " "add columns to the output spreadsheet\n\n" "Indexes can have an optional filter term. Filters " "are only applied to selected fields") self.list = QTreeView() self.list.setSelectionBehavior(QAbstractItemView.SelectRows) model = QStandardItemModel() model.setHorizontalHeaderLabels(['Index Name', 'Index Filter Term']) self.list.setModel(model) self.list.setUniformRowHeights(True) self.list.setAlternatingRowColors(True) self.list.setColumnWidth(0, 350) self.list.setColumnWidth(1, 250) self.list.setItemDelegateForColumn( 1, ComboDelegate(self.list, DOCUMENT_TYPE, model)) if not os.path.isfile("credentials.properties"): dialog = PasswordDialog() if dialog.exec_(): self.client = ContentAPI(dialog.username(), dialog.password(), dialog.tenant(), dialog.server()) else: raise SystemExit else: self.client = ContentAPI() for index_name in self.client.indexed_fields(): if index_name == "xip.full_text": continue index = QStandardItem(index_name) index.setCheckable(True) index.setEditable(False) model.appendRow([index]) self.run_report_button = QtWidgets.QPushButton("Run Report") self.run_report_button.released.connect(self.run_report) self.list.setModel(model) self.layout = QtWidgets.QVBoxLayout() self.layout.addWidget(self.search_label) self.layout.addWidget(self.search_value) self.layout.addWidget(self.help_label) self.layout.addWidget(self.list) self.layout.addWidget(self.run_report_button) self.setLayout(self.layout)
class BindiffViewerDialog(QDialog): def __init__(self, bv, match_db, role, primary_be, secondary_be): super(BindiffViewerDialog, self).__init__() self.bv = bv self.primary_be = primary_be self.secondary_be = secondary_be self.role = role # UI self.match_model = BindiffMatchModel(bv, match_db, role, primary_be, secondary_be) self.match_view = QTreeView() self.match_view.setModel(self.match_model) self.match_view.setSelectionMode(QTreeView.ExtendedSelection) self.match_view.setContextMenuPolicy(Qt.CustomContextMenu) self.match_view.customContextMenuRequested.connect(self.match_view_context_menu_requested) self.match_view.doubleClicked.connect(self.match_view_double_clicked) self.match_view.setRootIsDecorated(False) self.match_view.setFont(binaryninjaui.getMonospaceFont(self)) for i in range(len(self.match_model.column_infos)): self.match_view.resizeColumnToContents(i) self.match_view.setSortingEnabled(True) self.match_view.sortByColumn(0, Qt.AscendingOrder) layout = QVBoxLayout() layout.addWidget(self.match_view) self.setLayout(layout) self.setWindowTitle("BinDiff Viewer") self.resize(1000, 640) flags = self.windowFlags() flags |= Qt.WindowMaximizeButtonHint flags &= ~Qt.WindowContextHelpButtonHint self.setWindowFlags(flags) def match_view_double_clicked(self, index): if not index.isValid(): assert(False) return if self.role == None: return entry = self.match_model.entries[index.row()] if self.role == 0: address = entry["address1"] elif self.role == 1: address = entry["address2"] else: assert(False) self.bv.navigate(self.bv.file.view, address) def match_view_context_menu_requested(self, pos): if self.role == None: return selected_indices = self.match_view.selectionModel().selectedIndexes() # This may return each row multiple times, so we uniquify selected = set([i.row() for i in selected_indices]) def action_port_symbols(): for i in selected: self.port_symbols(i) menu = QMenu(self.match_view) menu.addAction("Port symbols", action_port_symbols) menu.exec_(self.match_view.mapToGlobal(pos)) def port_symbols(self, i): if self.role == None: return entry = self.match_model.entries[i] target_index = self.role source_index = 1 if target_index == 0 else 0 source_name = entry["name{}".format(source_index + 1)] target_address = entry["address{}".format(target_index + 1)] old_sym = self.bv.get_symbol_at(target_address) target_name = None if old_sym: target_name = old_sym.name target_text = target_name if target_name else "<unnamed>" if not source_name: bn.log_warn("Port symbols: {} @ {:x} has no source name, skipping".format(target_text, target_address)) return if old_sym and not old_sym.auto: bn.log_warn("Port symbols: {} @ {:x} is already named, skipping".format(target_text, target_address)) return bn.log_info("Port symbols: {} @ {:x} -> {}".format(target_text, target_address, source_name)) new_sym = bn.Symbol(bn.SymbolType.FunctionSymbol, target_address, source_name) self.bv.define_user_symbol(new_sym)
class ComponentTreeViewTab(QWidget): def __init__(self, parent=None): super().__init__() self.setLayout(QVBoxLayout()) self.setParent(parent) self.componentsTabLayout = QVBoxLayout() self.component_tree_view = QTreeView() self.componentsTabLayout.addWidget(self.component_tree_view) self.layout().addLayout(self.componentsTabLayout) self.component_tree_view.setDragEnabled(True) self.component_tree_view.setAcceptDrops(True) self.component_tree_view.setDropIndicatorShown(True) self.component_tree_view.header().hide() self.component_tree_view.updateEditorGeometries() self.component_tree_view.updateGeometries() self.component_tree_view.updateGeometry() self.component_tree_view.clicked.connect(self._set_button_state) self.component_tree_view.setSelectionMode(QAbstractItemView.SingleSelection) self.component_tool_bar = QToolBar("Actions", self) self.new_component_action = create_and_add_toolbar_action( "new_component.png", "New Component", self.parent().show_add_component_window, self.component_tool_bar, self, True, ) self.new_translation_action = create_and_add_toolbar_action( "new_translation.png", "New Translation", lambda: self._add_transformation(TransformationType.TRANSLATION), self.component_tool_bar, self, ) self.new_rotation_action = create_and_add_toolbar_action( "new_rotation.png", "New Rotation", lambda: self._add_transformation(TransformationType.ROTATION), self.component_tool_bar, self, ) self.create_link_action = create_and_add_toolbar_action( "create_link.png", "Create Link", self.on_create_link, self.component_tool_bar, self, ) self.duplicate_action = create_and_add_toolbar_action( "duplicate.png", "Duplicate", self.on_duplicate_node, self.component_tool_bar, self, ) self.edit_component_action = create_and_add_toolbar_action( "edit_component.png", "Edit Component", self.parent().show_edit_component_dialog, self.component_tool_bar, self, ) self.delete_action = create_and_add_toolbar_action( "delete.png", "Delete", self.on_delete_item, self.component_tool_bar, self ) self.zoom_action = create_and_add_toolbar_action( "zoom.svg", "Zoom To Component", self.on_zoom_item, self.component_tool_bar, self, ) self.component_tool_bar.insertSeparator(self.zoom_action) self.componentsTabLayout.insertWidget(0, self.component_tool_bar) def set_up_model(self, instrument): self.component_model = ComponentTreeModel(instrument) self.component_delegate = ComponentEditorDelegate( self.component_tree_view, instrument ) self.component_tree_view.setItemDelegate(self.component_delegate) self.component_tree_view.setModel(self.component_model) def _set_button_state(self): set_button_states( self.component_tree_view, self.delete_action, self.duplicate_action, self.new_rotation_action, self.new_translation_action, self.create_link_action, self.zoom_action, self.edit_component_action, ) def on_create_link(self): selected = self.component_tree_view.selectedIndexes() if len(selected) > 0: self.component_model.add_link(selected[0]) self._expand_transformation_list(selected[0]) self._set_button_state() def on_duplicate_node(self): selected = self.component_tree_view.selectedIndexes() if len(selected) > 0: self.component_model.duplicate_node(selected[0]) self._expand_transformation_list(selected[0]) def _expand_transformation_list(self, node: QModelIndex): expand_transformation_list(node, self.component_tree_view, self.component_model) def _add_transformation(self, transformation_type: TransformationType): add_transformation( transformation_type, self.component_tree_view, self.component_model ) def on_delete_item(self): selected = self.component_tree_view.selectedIndexes() for item in selected: self.component_model.remove_node(item) self._set_button_state() def on_zoom_item(self): selected = self.component_tree_view.selectedIndexes()[0] component = selected.internalPointer() self.sceneWidget.zoom_to_component( self.sceneWidget.get_entity(component.name), self.sceneWidget.view.camera() )
return self.createIndex(row, column, childItem) else: return QModelIndex() def parent(self, index): if not index.isValid(): return QModelIndex() childItem = index.internalPointer() if not childItem: return QModelIndex() parentItem = childItem.parent() if parentItem == self.rootItem: return QModelIndex() return self.createIndex(parentItem.row(), 0, parentItem) if __name__ == "__main__": # http://blog.mathieu-leplatre.info/filesystem-watch-with-pyqt4.html import sys # app = QApplication(sys.argv) TreeView = QTreeView() TreeModel = NodeTree( TreeView, [['A', ['a', 1], ['b', ['c', 1]]], ['C', 'D'], ['E', 'F']]) TreeView.setModel(TreeModel) TreeView.show() # app.exec_()
class ExportDialog(QWidget): def __init__(self, parent=None): super().__init__(parent) self.model = ExportChangesModel() self.error_dialog = None self.setWindowIcon(QIcon("paragon.ico")) self.setWindowTitle("Export") main_layout = QVBoxLayout(self) self.export_button = QPushButton() self.export_button.setText("Export") self.main_view = QTreeView() self.main_view.setModel(self.model) self.main_view.setHeaderHidden(True) self.main_view.setEditTriggers( QtWidgets.QAbstractItemView.NoEditTriggers) self.remember_selections_checkbox = QCheckBox() self.remember_selections_checkbox.setText("Remember selections.") main_layout.addWidget(self.export_button) main_layout.addWidget(self.main_view) main_layout.addWidget(self.remember_selections_checkbox) self.setLayout(main_layout) self.main_view.expanded.connect(self._on_item_expanded) self.export_button.clicked.connect(self._on_export_triggered) self.remember_selections_checkbox.stateChanged.connect( self._on_remember_selections_state_changed) self._set_remember_checkbox_state_from_settings() def _set_remember_checkbox_state_from_settings(self): settings_service = locator.get_static("SettingsService") if settings_service.get_remember_exports(): self.remember_selections_checkbox.setCheckState(QtCore.Qt.Checked) @staticmethod def _on_remember_selections_state_changed(state: int): should_remember = state == QtCore.Qt.Checked locator.get_static("SettingsService").set_remember_exports( should_remember) def closeEvent(self, event: QtGui.QCloseEvent): self.model.save_selected_items_tree() event.accept() def _on_export_triggered(self): file_name, ok = QFileDialog.getSaveFileName( self, caption="Select Changes File Destination", filter="*.json") if ok: self._try_export_and_write(file_name) def _try_export_and_write(self, file_name): logging.debug("Exporting selected data to %s" % file_name) try: export_data = self.model.export_selected_items() with open(file_name, "w", encoding="utf-8") as f: json.dump(export_data, f, indent=4, ensure_ascii=False) except: logging.exception("An error occurred while exporting data.") self.error_dialog = ErrorDialog( "An error occurred while exporting. See the log for details.") self.error_dialog.show() def _on_item_expanded(self, index: QModelIndex): self.model.update_data_for_index(index)
class CatsTreeWindow(PBDialog): """Extension of `PBDialog` that shows the categories tree""" def __init__( self, parent=None, askCats=False, askForBib=None, askForExp=None, expButton=True, previous=[], single=False, multipleRecords=False, ): """Initialize instance parameters and call the function that creates the layout. Parameters: parent (default None): the parent widget askCats (default False): if True, enable checkboxes for selection of categories askForBib (default None): the optional key which identifies in the database the bibtex entry for which categories are being selected askForExp (default None): the optional ID which identifies in the database the experiment for which categories are being selected expButton (default True): if True, add a button to accept the widget content and later ask for experiments previous (default []): the list of categories that must be selected at the beginning single (default False): if True, only allow the selection of a single category (the parent category, typically). Multiple checkboxes can be selected, but only the first one will be considered multipleRecords: used when dealing with categories corresponding to multiple records. Activate a tristate checkbox for the initial list of categories, which are typically not the same for all the elements in the list """ PBDialog.__init__(self, parent) self.setWindowTitle(cwstr.cats) self.currLayout = QVBoxLayout(self) self.setLayout(self.currLayout) self.askCats = askCats self.askForBib = askForBib self.askForExp = askForExp self.expButton = expButton self.previous = previous self.single = single self.multipleRecords = multipleRecords self.result = False self.marked = [] self.root_model = None self.proxyModel = None self.tree = None self.menu = None self.timer = None self.expsButton = None self.filterInput = None self.newCatButton = None self.acceptButton = None self.cancelButton = None self.setMinimumWidth(400) self.setMinimumHeight(600) self.createForm() def populateAskCat(self): """If selection of categories is allowed, add some information on the bibtex/experiment for which the categories are requested and a simple message, then create few required empty lists """ if self.askCats: if self.askForBib is not None: try: bibitem = pBDB.bibs.getByBibkey(self.askForBib, saveQuery=False)[0] except IndexError: pBGUILogger.warning( cwstr.entryNotInDb % self.askForBib, exc_info=True, ) return try: if bibitem["inspire"] != "" and bibitem[ "inspire"] is not None: link = "<a href='%s'>%s</a>" % ( pBView.getLink(self.askForBib, "inspire"), self.askForBib, ) elif bibitem["arxiv"] != "" and bibitem[ "arxiv"] is not None: link = "<a href='%s'>%s</a>" % ( pBView.getLink(self.askForBib, "arxiv"), self.askForBib, ) elif bibitem["doi"] != "" and bibitem["doi"] is not None: link = "<a href='%s'>%s</a>" % ( pBView.getLink(self.askForBib, "doi"), self.askForBib, ) else: link = self.askForBib bibtext = PBLabel( cwstr.markCatBibKAT % (link, bibitem["author"], bibitem["title"])) except KeyError: bibtext = PBLabel(cwstr.markCatBibK % (self.askForBib)) self.currLayout.addWidget(bibtext) elif self.askForExp is not None: try: expitem = pBDB.exps.getByID(self.askForExp)[0] except IndexError: pBGUILogger.warning( cwstr.expNotInDb % self.askForExp, exc_info=True, ) return try: exptext = PBLabel( cwstr.markCatExpINC % (self.askForExp, expitem["name"], expitem["comments"])) except KeyError: exptext = PBLabel(cwstr.markCatExpI % (self.askForExp)) self.currLayout.addWidget(exptext) else: if self.single: comment = PBLabel(cwstr.selectCat) else: comment = PBLabel(cwstr.selectCats) self.currLayout.addWidget(comment) self.marked = [] self.parent().selectedCats = [] return True def onCancel(self): """Reject the dialog content and close the window""" self.result = False self.close() def onOk(self, exps=False): """Accept the dialog content (update the list of selected categories) and close the window. May set `self.result` to "Exps" for later opening of a new dialog to ask for experiments. Parameter: exps (default False): if True, set the result to "Exps", otherwise to "Ok" """ self.parent().selectedCats = [ idC for idC in self.root_model.selectedCats.keys() if self.root_model.selectedCats[idC] == True ] self.parent().previousUnchanged = [ idC for idC in self.root_model.previousSaved.keys() if self.root_model.previousSaved[idC] == True ] if (self.single and len(self.parent().selectedCats) > 1 and self.parent().selectedCats[0] == 0): self.parent().selectedCats.pop(0) self.parent().selectedCats = [self.parent().selectedCats[0]] self.result = "Exps" if exps else "Ok" self.close() def changeFilter(self, string): """When the filter `QLineEdit` is changed, update the `LeafFilterProxyModel` regexp filter Parameter: string: the new filter string """ self.proxyModel.setFilterRegExp(str(string)) self.tree.expandAll() def onAskExps(self): """Action to perform when the selection of categories will be folloed by the selection of experiments. Call `self.onOk` with `exps = True`. """ self.onOk(exps=True) def onNewCat(self): """Action to perform when the creation of a new category is requested """ editCategory(self, self.parent()) def keyPressEvent(self, e): """Manage the key press events. Do nothing unless `Esc` is pressed: in this case close the dialog """ if e.key() == Qt.Key_Escape: self.close() def createForm(self): """Create the dialog content, connect the model to the view and eventually add the buttons at the end """ self.populateAskCat() catsTree = pBDB.cats.getHier() self.filterInput = QLineEdit("", self) self.filterInput.setPlaceholderText(cwstr.filterCat) self.filterInput.textChanged.connect(self.changeFilter) self.currLayout.addWidget(self.filterInput) self.filterInput.setFocus() self.tree = QTreeView(self) self.currLayout.addWidget(self.tree) self.tree.setMouseTracking(True) self.tree.entered.connect(self.handleItemEntered) self.tree.doubleClicked.connect(self.cellDoubleClick) self.tree.setExpandsOnDoubleClick(False) catsNamedTree = self._populateTree(catsTree[0], 0) self.root_model = CatsModel( pBDB.cats.getAll(), [catsNamedTree], self, self.previous, multipleRecords=self.multipleRecords, ) self.proxyModel = LeafFilterProxyModel(self) self.proxyModel.setSourceModel(self.root_model) self.proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxyModel.setSortCaseSensitivity(Qt.CaseInsensitive) self.proxyModel.setFilterKeyColumn(-1) self.tree.setModel(self.proxyModel) self.tree.expandAll() self.tree.setHeaderHidden(True) # self.tree.doubleClicked.connect(self.askAndPerformAction) self.newCatButton = QPushButton(cwstr.addNew, self) self.newCatButton.clicked.connect(self.onNewCat) self.currLayout.addWidget(self.newCatButton) if self.askCats: self.acceptButton = QPushButton(cwstr.ok, self) self.acceptButton.clicked.connect(self.onOk) self.currLayout.addWidget(self.acceptButton) if self.expButton: self.expsButton = QPushButton(cwstr.askExp, self) self.expsButton.clicked.connect(self.onAskExps) self.currLayout.addWidget(self.expsButton) # cancel button self.cancelButton = QPushButton(cwstr.cancel, self) self.cancelButton.clicked.connect(self.onCancel) self.cancelButton.setAutoDefault(True) self.currLayout.addWidget(self.cancelButton) def _populateTree(self, children, idCat): """Read the list of categories recursively and populate the categories tree Parameters: children: the list of children categories of the currently considered one idCat: the id of the current category """ name = pBDB.cats.getByID(idCat)[0]["name"] children_list = [] for child in cats_alphabetical(children, pBDB): child_item = self._populateTree(children[child], child) children_list.append(child_item) return NamedElement(idCat, name, children_list) def handleItemEntered(self, index): """Process event when mouse enters an item and create a `QTooltip` which describes the category, with a timer Parameter: index: a `QModelIndex` instance """ if index.isValid(): row = index.row() else: return try: idString = self.proxyModel.sibling(row, 0, index).data() except AttributeError: pBLogger.debug("", exc_info=True) return try: idCat, catName = idString.split(": ") except AttributeError: pBLogger.debug("", exc_info=True) return idCat = idCat.strip() try: self.timer.stop() QToolTip.showText(QCursor.pos(), "", self.tree.viewport()) except AttributeError: pass try: catData = pBDB.cats.getByID(idCat)[0] except IndexError: pBGUILogger.exception(cwstr.failedFind) return self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.timeout.connect(lambda: QToolTip.showText( QCursor.pos(), cwstr.catId.format(idC=idCat, cat=catData["name"]) + cwstr. entriesCorrespondent.format(en=pBDB.catBib.countByCat(idCat)) + cwstr.expsAssociated.format(ex=pBDB.catExp.countByCat(idCat)), self.tree.viewport(), self.tree.visualRect(index), 3000, )) self.timer.start(500) def contextMenuEvent(self, event): """Create a right click menu with few actions on the selected category Parameter: event: a `QEvent` """ indexes = self.tree.selectedIndexes() try: index = indexes[0] except IndexError: pBLogger.debug(cwstr.clickMissingIndex) return if index.isValid(): row = index.row() else: return try: idString = self.proxyModel.sibling(row, 0, index).data() except AttributeError: pBLogger.debug("", exc_info=True) return try: idCat, catName = idString.split(": ") except AttributeError: pBLogger.debug("", exc_info=True) return idCat = idCat.strip() menu = PBMenu() self.menu = menu titAction = QAction(cwstr.catDescr % catName) titAction.setDisabled(True) bibAction = QAction(cwstr.openEntryList) modAction = QAction(cwstr.modify) delAction = QAction(cwstr.delete) subAction = QAction(cwstr.addSub) menu.possibleActions = [ titAction, None, bibAction, None, modAction, delAction, None, subAction, ] menu.fillMenu() action = menu.exec_(event.globalPos()) if action == bibAction: self.parent().reloadMainContent(pBDB.bibs.getByCat(idCat)) elif action == modAction: editCategory(self, self.parent(), idCat) elif action == delAction: deleteCategory(self, self.parent(), idCat, catName) elif action == subAction: editCategory(self, self.parent(), useParentCat=idCat) return def cellDoubleClick(self, index): """Process event when mouse double clicks an item. Opens a link if some columns Parameter: index: a `QModelIndex` instance """ if index.isValid(): row = index.row() col = index.column() else: return try: idString = self.proxyModel.sibling(row, 0, index).data() except AttributeError: pBLogger.debug("", exc_info=True) return try: idCat, catName = idString.split(": ") except AttributeError: pBLogger.debug("", exc_info=True) return idCat = idCat.strip() self.parent().reloadMainContent(pBDB.bibs.getByCat(idCat)) return def recreateTable(self): """Delete the previous widgets and recreate them with new data""" self.cleanLayout() self.createForm()
class RssTab(QWidget): new_torrents = Signal(int) def __init__(self): QWidget.__init__(self) layout = QVBoxLayout(self) self.splitter = QSplitter(self) self.cats = QTreeView(self) self.cats.setSortingEnabled(True) self.cat_model = RssCategoryModel() proxy = QSortFilterProxyModel() proxy.setSourceModel(self.cat_model) self.cats.setModel(proxy) self.splitter.addWidget(self.cats) self.t = QTableWidget(0, 4, self) self.splitter.addWidget(self.t) self.stats = [QLabel('{}'.format(datetime.now())) for _ in range(8)] stat: QLabel for stat in self.stats: stat.setFont(QFont(pointSize=14)) layout.addWidget(stat, 0, Qt.AlignTop) layout.addWidget(self.splitter, 0, Qt.AlignTop) self.ds = DataSource() self.f_model = ForumsModel(self.ds.get_forums()) self.forums = QTableView(self) self.forums.setModel(self.f_model) self.forums.resizeColumnsToContents() layout.addWidget(self.forums, 10, Qt.AlignTop) self.setLayout(layout) self.worker = RssWorker() self.worker_thread = QThread() self.worker_thread.started.connect(self.worker.run) self.worker.finished.connect(self.worker_thread.quit) self.worker.moveToThread(self.worker_thread) self.worker_thread.start() self.worker.processed.connect(self.processed) self.worker.current.connect(self.current) @Slot(str) def current(self, topic): for i in range(len(self.stats) - 1): self.stats[i].setText(self.stats[i + 1].text()) self.stats[len(self.stats) - 1].setText('{0} - {1}'.format( datetime.now(), topic)) @Slot(int, int) def processed(self, forum_id, torrents): print('\t\t\tRSS: ' + str(forum_id) + ', ' + str(torrents)) forum = self.ds.get_forum(forum_id) print('\t\t\tRSS FORUM: ' + str(forum)) cat = self.ds.get_category(forum['category']) print('\t\t\tRSS CAT: ' + str(cat)) self.cat_model.addCategory(cat['title'], torrents) def finish(self): self.worker.finish() self.worker_thread.quit() self.worker_thread.wait()
import sys from PySide2.QtWidgets import QApplication, QSplitter, QFileSystemModel, QTreeView, QListView from PySide2.QtCore import QDir if __name__ == '__main__': app = QApplication(sys.argv) splitter = QSplitter() model = QFileSystemModel() model.setRootPath("/") tree = QTreeView(splitter) tree.setModel(model) tree.setRootIndex(model.index("/")) list = QListView(splitter) list.setModel(model) list.setRootIndex(model.index("/")) splitter.setWindowTitle( "A model with two views or two views with one model") splitter.show() app.exec_()
def create_ui(self): """Setup main UI elements, dock widgets, UI-related elements, etc. """ log.debug('Loading UI') # Undo Stack self.undo_stack = QUndoStack(self) self.undo_stack.setUndoLimit(100) # Object navigation history self.obj_history = deque([], config.MAX_OBJ_HISTORY) app = QApplication.instance() base_font = QFont() base_font.fromString(self.prefs['base_font']) app.setFont(base_font) # Object class table widget # classTable = QTableView(self) classTable = classtable.TableView(self) classTable.setObjectName("classTable") classTable.setAlternatingRowColors(True) classTable.setFrameShape(QFrame.StyledPanel) classTable_font = QFont() classTable_font.fromString(self.prefs['class_table_font']) classTable.setFont(classTable_font) fm = classTable.fontMetrics() classTable.setWordWrap(True) classTable.setEditTriggers(QAbstractItemView.EditKeyPressed | QAbstractItemView.DoubleClicked | QAbstractItemView.AnyKeyPressed | QAbstractItemView.SelectedClicked) # classTable.horizontalHeader().setMovable(True) # classTable.verticalHeader().setMovable(False) classTable.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) classTable.verticalHeader().setSectionResizeMode(QHeaderView.Interactive) classTable.horizontalHeader().setDefaultSectionSize(self.prefs['default_column_width']) classTable.verticalHeader().setDefaultSectionSize(fm.height() + 0) classTable.setSelectionMode(QAbstractItemView.ExtendedSelection) classTable.setContextMenuPolicy(Qt.CustomContextMenu) classTable.customContextMenuRequested.connect(self.custom_table_context_menu) # Create table model and proxy layers for transposing and filtering self.classTableModel = classtable.IDFObjectTableModel(classTable) self.transposeableModel = classtable.TransposeProxyModel(self.classTableModel) self.transposeableModel.setSourceModel(self.classTableModel) self.sortableModel = classtable.SortFilterProxyModel(self.transposeableModel) self.sortableModel.setSourceModel(self.transposeableModel) # Assign model to table (enable sorting FIRST) # table.setSortingEnabled(True) # Disable for now, CRUD actions won't work! classTable.setModel(self.sortableModel) # Connect some signals selection_model = classTable.selectionModel() selection_model.selectionChanged.connect(self.table_selection_changed) scroll_bar = classTable.verticalScrollBar() scroll_bar.valueChanged.connect(self.scroll_changed) # These are currently broken # classTable.horizontalHeader().sectionMoved.connect(self.moveObject) # classTable.verticalHeader().sectionMoved.connect(self.moveObject) # Object class tree widget classTreeDockWidget = QDockWidget("Object Classes and Counts", self) classTreeDockWidget.setObjectName("classTreeDockWidget") classTreeDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) classTree = QTreeView(classTreeDockWidget) classTree.setUniformRowHeights(True) classTree.setAllColumnsShowFocus(True) classTree.setRootIsDecorated(False) classTree.setExpandsOnDoubleClick(True) classTree.setIndentation(15) classTree.setAnimated(True) classTree_font = QFont() classTree_font.fromString(self.prefs['class_tree_font']) classTree.setFont(classTree_font) classTree.setAlternatingRowColors(True) classTree.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) palette = classTree.palette() palette.setColor(QPalette.Highlight, Qt.darkCyan) classTree.setPalette(palette) class_tree_window = QWidget(classTreeDockWidget) class_tree_dock_layout_v = QVBoxLayout() class_tree_dock_layout_h = QHBoxLayout() class_tree_dock_layout_v.setContentsMargins(0, 8, 0, 0) class_tree_dock_layout_h.setContentsMargins(0, 0, 0, 0) class_tree_filter_edit = QLineEdit(classTreeDockWidget) class_tree_filter_edit.setPlaceholderText("Filter Classes") class_tree_filter_edit.textChanged.connect(self.treeFilterRegExpChanged) class_tree_filter_cancel = QPushButton("Clear", classTreeDockWidget) class_tree_filter_cancel.setMaximumWidth(45) class_tree_filter_cancel.clicked.connect(self.clearTreeFilterClicked) class_tree_dock_layout_h.addWidget(class_tree_filter_edit) class_tree_dock_layout_h.addWidget(class_tree_filter_cancel) class_tree_dock_layout_v.addLayout(class_tree_dock_layout_h) class_tree_dock_layout_v.addWidget(classTree) class_tree_window.setLayout(class_tree_dock_layout_v) classTreeDockWidget.setWidget(class_tree_window) classTreeDockWidget.setContentsMargins(0,0,0,0) # Comments widget commentDockWidget = QDockWidget("Comments", self) commentDockWidget.setObjectName("commentDockWidget") commentDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) commentView = UndoRedoTextEdit(commentDockWidget, self) commentView.setLineWrapMode(QTextEdit.FixedColumnWidth) commentView.setLineWrapColumnOrWidth(499) commentView.setFrameShape(QFrame.StyledPanel) commentView_font = QFont() commentView_font.fromString(self.prefs['comments_font']) commentView.setFont(commentView_font) commentDockWidget.setWidget(commentView) # Info and help widget infoDockWidget = QDockWidget("Info", self) infoDockWidget.setObjectName("infoDockWidget") infoDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) infoView = QTextEdit(infoDockWidget) infoView.setFrameShape(QFrame.StyledPanel) infoView.setReadOnly(True) infoDockWidget.setWidget(infoView) # Node list and jump menu widget refDockWidget = QDockWidget("Field References", self) refDockWidget.setObjectName("refDockWidget") refDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) ref_model = reftree.ReferenceTreeModel(None, refDockWidget) refView = QTreeView(refDockWidget) refView.setModel(ref_model) refView.setUniformRowHeights(True) refView.setRootIsDecorated(False) refView.setIndentation(15) refView.setColumnWidth(0, 160) refView.setFrameShape(QFrame.StyledPanel) refDockWidget.setWidget(refView) refView.doubleClicked.connect(self.ref_tree_double_clicked) # Logging and debugging widget logDockWidget = QDockWidget("Log Viewer", self) logDockWidget.setObjectName("logDockWidget") logDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) logView = QPlainTextEdit(logDockWidget) logView.setLineWrapMode(QPlainTextEdit.NoWrap) logView.setReadOnly(True) logView_font = QFont() logView_font.fromString(self.prefs['base_font']) logView.setFont(logView_font) logView.ensureCursorVisible() logDockWidget.setWidget(logView) # Undo view widget undoDockWidget = QDockWidget("Undo History", self) undoDockWidget.setObjectName("undoDockWidget") undoDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) undoView = QUndoView(self.undo_stack) undoDockWidget.setWidget(undoView) # Define corner docking behaviour self.setDockNestingEnabled(True) self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.TopRightCorner, Qt.RightDockWidgetArea) self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea) # Assign main widget and dock widgets to QMainWindow self.setCentralWidget(classTable) self.addDockWidget(Qt.LeftDockWidgetArea, classTreeDockWidget) self.addDockWidget(Qt.RightDockWidgetArea, commentDockWidget) self.addDockWidget(Qt.RightDockWidgetArea, infoDockWidget) self.addDockWidget(Qt.RightDockWidgetArea, refDockWidget) self.addDockWidget(Qt.RightDockWidgetArea, logDockWidget) self.addDockWidget(Qt.RightDockWidgetArea, undoDockWidget) # Store widgets for access by other objects self.classTable = classTable self.commentView = commentView self.infoView = infoView self.classTree = classTree self.logView = logView self.undoView = undoView self.refView = refView self.filterTreeBox = class_tree_filter_edit # Store docks for access by other objects self.commentDockWidget = commentDockWidget self.infoDockWidget = infoDockWidget self.classTreeDockWidget = classTreeDockWidget self.logDockWidget = logDockWidget self.undoDockWidget = undoDockWidget self.refDockWidget = refDockWidget # Perform other UI-related initialization tasks self.center() self.setUnifiedTitleAndToolBarOnMac(True) self.setWindowIcon(QIcon(':/images/logo.png')) # Status bar setup self.statusBar().showMessage('Status: Ready') self.unitsLabel = QLabel() self.unitsLabel.setAlignment(Qt.AlignCenter) self.unitsLabel.setMinimumSize(self.unitsLabel.sizeHint()) self.unitsLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.statusBar().addPermanentWidget(self.unitsLabel) self.pathLabel = QLabel() self.pathLabel.setAlignment(Qt.AlignCenter) self.pathLabel.setMinimumSize(self.pathLabel.sizeHint()) self.pathLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.statusBar().addPermanentWidget(self.pathLabel) self.versionLabel = QLabel() self.versionLabel.setAlignment(Qt.AlignCenter) self.versionLabel.setMinimumSize(self.versionLabel.sizeHint()) self.versionLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.statusBar().addPermanentWidget(self.versionLabel) self.progressBarIDF = QProgressBar() self.progressBarIDF.setAlignment(Qt.AlignCenter) self.progressBarIDF.setMaximumWidth(200) self.statusBar().addPermanentWidget(self.progressBarIDF) self.clipboard = QApplication.instance().clipboard() self.obj_clipboard = [] self.setStyleSheet(""" QToolTip { background-color: gray; color: white; border: black solid 1px } # QMenu { # background-color: rgbf(0.949020, 0.945098, 0.941176); # color: rgb(255,255,255); # } # QMenu::item::selected { # background-color: rgbf(0.949020, 0.945098, 0.941176); # } """)
class TriageFilePicker(QWidget): def __init__(self, context): super(TriageFilePicker, self).__init__() self.context = context self.actionHandler = UIActionHandler() self.actionHandler.setupActionHandler(self) self.contextMenu = Menu() self.contextMenuManager = ContextMenuManager(self) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.model = QFileSystemModel() self.model.setRootPath("") self.model.setFilter(QDir.AllEntries | QDir.Hidden | QDir.System) self.tree = QTreeView(self) self.tree.setModel(self.model) self.tree.setSelectionMode(QAbstractItemView.ExtendedSelection) self.tree.setColumnWidth(0, 500) layout.addWidget(self.tree, 1) self.setLayout(layout) self.tree.doubleClicked.connect(self.onDoubleClick) recentFile = QSettings().value("triage/recentFile", os.path.expanduser("~")) while len(recentFile) > 0: f = self.model.index(recentFile) if f.isValid(): self.tree.scrollTo(f) self.tree.setExpanded(f, True) break parentDir = os.path.dirname(recentFile) if parentDir == recentFile: break recentFile = parentDir self.actionHandler.bindAction("Open Selected Files", UIAction( lambda context: self.openSelectedFiles(), lambda context: self.areFilesSelected())) self.contextMenu.addAction("Open Selected Files", "Open") def contextMenuEvent(self, event): self.contextMenuManager.show(self.contextMenu, self.actionHandler) def onDoubleClick(self, index): self.openSelectedFiles() def openSelectedFiles(self): failedToOpen = [] files = set() for index in self.tree.selectionModel().selectedIndexes(): if self.model.fileInfo(index).isFile(): files.add(self.model.fileInfo(index).absoluteFilePath()) for filename in files: QSettings().setValue("triage/recentFile", filename) f = FileContext.openFilename(filename) if not f: failedToOpen.append(filename) continue f.createBinaryViews() for data in f.getAllDataViews(): Settings().set_string("analysis.mode", Settings().get_string("triage.analysisMode"), data) Settings().set_bool("triage.preferSummaryView", True, data) if data.view_type != "Raw": linearSweepMode = Settings().get_string("triage.linearSweep") if linearSweepMode == "none": Settings().set_bool("analysis.linearSweep.autorun", False, data) elif linearSweepMode == "partial": Settings().set_bool("analysis.linearSweep.autorun", True, data) Settings().set_bool("analysis.linearSweep.controlFlowGraph", False, data) elif linearSweepMode == "full": Settings().set_bool("analysis.linearSweep.autorun", True, data) Settings().set_bool("analysis.linearSweep.controlFlowGraph", True, data) self.context.openFileContext(f) if len(failedToOpen) > 0: QMessageBox.critical(self, "Error", "Unable to open:\n" + "\n".join(failedToOpen)) def areFilesSelected(self): return self.tree.selectionModel().hasSelection()
class MVCConfigurationGUI(MVCConfigurationBase): """ GUI implementation of MVCConfigurationBase """ def __init__(self, configuration): super().__init__(configuration) assertMainThread() srv = Services.getService("MainWindow") srv.aboutToClose.connect(self._aboutToClose) confMenu = srv.menuBar().addMenu("&Configuration") toolBar = srv.getToolBar() configuration.configNameChanged.connect(self._configNameChanged) configuration.dirtyChanged.connect(self._dirtyChanged) style = QApplication.style() self.actLoad = QAction( QIcon.fromTheme("document-open", style.standardIcon(QStyle.SP_DialogOpenButton)), "Open config", self) self.actLoad.triggered.connect(self._execLoad) self.actSave = QAction( QIcon.fromTheme("document-save", style.standardIcon(QStyle.SP_DialogSaveButton)), "Save config", self) self.actSave.triggered.connect(self._execSaveConfig) self.actSaveWithGuiState = QAction( QIcon.fromTheme("document-save", style.standardIcon(QStyle.SP_DialogSaveButton)), "Save config sync gui state", self) self.actSaveWithGuiState.triggered.connect( self._execSaveConfigWithGuiState) self.actNew = QAction( QIcon.fromTheme("document-new", style.standardIcon(QStyle.SP_FileIcon)), "New config", self) self.actNew.triggered.connect(self._execNew) self.actActivate = QAction( QIcon.fromTheme("arrow-up", style.standardIcon(QStyle.SP_ArrowUp)), "Initialize", self) self.actActivate.triggered.connect(self.activate) self.actDeactivate = QAction( QIcon.fromTheme("arrow-down", style.standardIcon(QStyle.SP_ArrowDown)), "Deinitialize", self) self.actDeactivate.triggered.connect(self.deactivate) confMenu.addAction(self.actLoad) confMenu.addAction(self.actSave) confMenu.addAction(self.actSaveWithGuiState) confMenu.addAction(self.actNew) confMenu.addAction(self.actActivate) confMenu.addAction(self.actDeactivate) toolBar.addAction(self.actLoad) toolBar.addAction(self.actSave) toolBar.addAction(self.actNew) toolBar.addAction(self.actActivate) toolBar.addAction(self.actDeactivate) self.recentConfigs = [QAction() for i in range(10)] self.recentConfigs[0].setShortcut(QKeySequence(Qt.CTRL + Qt.Key_R)) confMenu.addSeparator() recentMenu = confMenu.addMenu("Recent") for a in self.recentConfigs: a.setVisible(False) a.triggered.connect(self._openRecent) recentMenu.addAction(a) self.mainWidget = srv.newDockWidget("Configuration", None, Qt.LeftDockWidgetArea) self.treeView = QTreeView(self.mainWidget) self.treeView.setHeaderHidden(False) self.treeView.setSelectionMode(QAbstractItemView.NoSelection) self.treeView.setEditTriggers(self.treeView.EditKeyPressed | self.treeView.AnyKeyPressed) self.treeView.setAllColumnsShowFocus(True) self.treeView.setExpandsOnDoubleClick(False) self.treeView.setDragEnabled(True) self.treeView.setDropIndicatorShown(True) self.treeView.setDragDropMode(QAbstractItemView.DragOnly) self.mainWidget.setWidget(self.treeView) self.treeView.setModel(self.model) self.treeView.header().setStretchLastSection(True) self.treeView.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.treeView.doubleClicked.connect(self._onItemDoubleClicked) self.treeView.setContextMenuPolicy(Qt.CustomContextMenu) self.treeView.customContextMenuRequested.connect( self._execTreeViewContextMenu) # expand applications by default self.treeView.setExpanded(self.model.index(1, 0), True) self.delegate = PropertyDelegate(self.model, ITEM_ROLE, ConfigurationModel.PropertyContent, self) self.treeView.setItemDelegate(self.delegate) self.restoreState() srv.aboutToClose.connect(self.saveState) # a list of dock widgets displaying subgraphs self._graphViews = [] # make sure that the graph views are closed when the config is closed self._configuration.subConfigRemoved.connect(self._subConfigRemoved) self._waitForActivated = None self._waitForOpenState = None def _execLoad(self): assertMainThread() if self._checkDirty(): return fn, _ = QFileDialog.getOpenFileName(self.mainWidget, "Load configuration", self.cfgfile, filter="*.json") if fn is not None and fn != "": logger.debug("Loading config file %s", fn) try: self.loadConfig(fn) except Exception as e: # pylint: disable=broad-except logger.exception("Error while loading configuration %s: %s", fn, str(e)) QMessageBox.warning(self.mainWidget, "Error while loading configuration", str(e)) def _openRecent(self): """ Called when the user clicks on a recent config. :return: """ if self._checkDirty(): return action = self.sender() fn = action.data() try: self.loadConfig(fn) except Exception as e: # pylint: disable=broad-except # catching general exception is wanted here. logger.exception("Error while loading configuration %s: %s", fn, str(e)) QMessageBox.warning(self.mainWidget, "Error while loading configuration", str(e)) def _checkDirty(self): if self._configuration.dirty(): ans = QMessageBox.question( None, "Save changes?", "There are unsaved changes. Do you want to save them?", buttons=QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, defaultButton=QMessageBox.Save) if ans == QMessageBox.Save: self.saveConfig() return False if ans == QMessageBox.Cancel: return True return False def _aboutToClose(self, mainWindow): if self._checkDirty(): mainWindow.ignoreCloseEvent() def _execNew(self): assertMainThread() if self._checkDirty(): return fn, _ = QFileDialog.getSaveFileName(self.mainWidget, "New configuration", ".", filter="*.json") if fn is not None and fn != "": logger.debug("Creating config file %s", fn) self.newConfig(fn) def _execSaveConfig(self): if self.configuration().filename() is None: self._execSaveConfigAs() else: self.saveConfig() def _execSaveConfigWithGuiState(self): if self.configuration().filename() is None: self._execSaveConfigAs() else: self.saveConfigWithGuiState() def _execSaveConfigAs(self): """ Opens a file dialog to get the save file name and calls saveConfig. :return: """ assertMainThread() fn, _ = QFileDialog.getSaveFileName(self.mainWidget, "Save configuration as", ".", "*.json") if fn is not None and fn != "": self.saveConfigAs(fn) def _addGraphView(self, subConfig): g = subConfig.getGraph() # remove already deleted graph views from internal list valid_graphViews = [] for gv in self._graphViews: if shiboken2.isValid(gv): # pylint: disable=no-member valid_graphViews.append(gv) self._graphViews = valid_graphViews # check if graph view is already there for gv in self._graphViews: if gv.widget().scene().graph == g: logger.info("Graph view already exists.") return # create new graph view srv = Services.getService("MainWindow") graphDw = srv.newDockWidget("Graph (%s)" % (subConfig.getName()), parent=None, defaultArea=Qt.RightDockWidgetArea, allowedArea=Qt.RightDockWidgetArea | Qt.BottomDockWidgetArea) graphDw.setAttribute(Qt.WA_DeleteOnClose, True) assert isinstance(graphDw, QDockWidget) graphView = GraphEditorView(graphDw) graphView.setScene(GraphScene(subConfig.getGraph(), graphDw)) graphDw.setWidget(graphView) self._graphViews.append(graphDw) graphDw.visibleChanged.connect(self._removeGraphViewFromList) def _subConfigRemoved(self, subConfigName, configType): g = self._configuration.subConfigByNameAndTye(subConfigName, configType).getGraph() for gv in self._graphViews: if gv.widget().scene().graph == g: logger.debug("deleting graph view for subconfig %s", subConfigName) gv.deleteLater() def _removeGraphViewFromList(self, visible): if visible: return gv = self.sender() try: self._graphViews.remove(gv) logger.debug("removed graphview from list") except ValueError: logger.debug("graphview not in list, ignored") def _execTreeViewContextMenu(self, point): index = self.treeView.indexAt(point) item = self.model.data(index, ITEM_ROLE) if isinstance(item, ConfigurationModel.SubConfigContent): m = QMenu() a1 = QAction("Edit graph") m.addAction(a1) a1.triggered.connect(lambda: self._addGraphView(item.subConfig)) if self.model.isApplication(index): a2 = QAction("Select Application") a2.triggered.connect(lambda: self.changeActiveApp( self.model.data(index, Qt.DisplayRole))) a3 = QAction("Init Application") a3.triggered.connect(lambda: self._changeActiveAppAndInit( self.model.data(index, Qt.DisplayRole))) m.addActions([a2, a3]) pbsrv = Services.getService("PlaybackControl") m2 = m.addMenu("Init and load sequence") m3 = m.addMenu("Init, load and play") s1 = [] s2 = [] for a in pbsrv.recentSeqs: assert isinstance(a, QAction) if a.isVisible(): # pylint: disable=cell-var-from-loop # the below statements are tested and work aseq = QAction(a.text()) aseq.triggered.connect(lambda arg1=a.data( ), seq=a.data(): self._changeActiveAppInitAndLoad( self.model.data(index, Qt.DisplayRole), seq, False) ) s1.append(aseq) aseq = QAction(a.text()) aseq.triggered.connect(lambda arg1=a.data( ), seq=a.data(): self._changeActiveAppInitAndLoad( self.model.data(index, Qt.DisplayRole), seq, True)) # pylint: enable=cell-var-from-loop s2.append(aseq) m2.addActions(s1) m3.addActions(s2) m.exec_(self.treeView.mapToGlobal(point)) return if self.model.isSubConfigParent( index) == Configuration.CONFIG_TYPE_APPLICATION: m = QMenu() a = QAction("Add application") m.addAction(a) a = m.exec_(self.treeView.mapToGlobal(point)) if a is not None: self._configuration.addNewApplication() return if self.model.isSubConfigParent( index) == Configuration.CONFIG_TYPE_COMPOSITE: m = QMenu() a = QAction("Add composite filter") m.addAction(a) a = m.exec_(self.treeView.mapToGlobal(point)) if a is not None: self._configuration.addNewCompositeFilter() return def _configNameChanged(self, cfgfile): logger.debug("_configNameChanged: %s", cfgfile) assertMainThread() self.cfgfile = cfgfile self._dirtyChanged(self._configuration.dirty()) foundIdx = None for i, a in enumerate(self.recentConfigs): if a.data() == cfgfile: foundIdx = i if foundIdx is None: foundIdx = len(self.recentConfigs) - 1 for i in range(foundIdx, 0, -1): self.recentConfigs[i].setText(self.recentConfigs[i - 1].text()) self.recentConfigs[i].setData(self.recentConfigs[i - 1].data()) self.recentConfigs[i].setVisible( self.recentConfigs[i - 1].data() is not None) self.recentConfigs[0].setText(cfgfile) self.recentConfigs[0].setData(cfgfile) self.recentConfigs[0].setVisible(True) def _dirtyChanged(self, dirty): srv = Services.getService("MainWindow") if self.cfgfile is None: title = "nexxT: <unnamed>" else: title = "nexxT: " + self.cfgfile if dirty: title += " *" srv.setWindowTitle(title) def _onItemDoubleClicked(self, index): assertMainThread() if self.model.isApplication(index): app = self.model.data(index, Qt.DisplayRole) self.changeActiveApp(app) else: self.treeView.edit(index) def _changeActiveAppAndInit(self, app): """ Call this slot to activate and init an application :param app: can be either an Application instance or the name of an application :return: """ assertMainThread() if isinstance(app, str): app = self.configuration().applicationByName(app) currentApp = Application.activeApplication if currentApp is not None: currentApp = currentApp.getApplication() self._waitForActivated = app self.changeActiveApp(app.getName()) def _changeActiveAppInitAndLoad(self, app, sequence, startPlay): self._waitForOpenState = (app, sequence, startPlay) self._changeActiveAppAndInit(app) def appActivated(self, name, app): # pylint: disable=unused-argument """ Called when the application is activated. :param name: the application name :param app: An ActiveApplication instance. :return: """ assertMainThread() if app is not None: self.activeAppStateChange(app.getState()) app.stateChanged.connect(self.activeAppStateChange) if self._waitForActivated == app.getApplication(): MethodInvoker(self.activate, Qt.QueuedConnection) else: self.actActivate.setEnabled(False) self.actDeactivate.setEnabled(False) self._waitForActivated = None def _disconnectSingleShotPlay(self): assertMainThread() pbsrv = Services.getService("PlaybackControl") try: pbsrv.playbackPaused.disconnect(self._singleShotPlay) except RuntimeError: # we are already disconnected. pass def _singleShotPlay(self): assertMainThread() pbsrv = Services.getService("PlaybackControl") MethodInvoker(pbsrv.startPlayback, Qt.QueuedConnection) self._disconnectSingleShotPlay() def activeAppStateChange(self, newState): """ Called when the active application changes its state. :param newState: the new application's state (see FilterState) :return: """ assertMainThread() if newState == FilterState.CONSTRUCTED: self.actActivate.setEnabled(True) else: self.actActivate.setEnabled(False) if newState == FilterState.ACTIVE: if self._waitForOpenState is not None: app, pbfile, startPlay = self._waitForOpenState self._waitForOpenState = None if app == Application.activeApplication.getApplication( ).getName(): pbsrv = Services.getService("PlaybackControl") if startPlay: pbsrv.playbackPaused.connect(self._singleShotPlay) QTimer.singleShot(2000, self._disconnectSingleShotPlay) MethodInvoker(pbsrv.browser.setActive, Qt.QueuedConnection, pbfile) self.actDeactivate.setEnabled(True) self.actSaveWithGuiState.setEnabled(False) else: self.actDeactivate.setEnabled(False) self.actSaveWithGuiState.setEnabled(True) def restoreState(self): """ Restore the state of the configuration gui service (namely the recently open config files). This is saved in QSettings because it is used across config files. :return: """ logger.debug("restoring config state ...") settings = QSettings() v = settings.value("ConfigurationRecentFiles") if v is not None and isinstance(v, QByteArray): ds = QDataStream(v) recentFiles = ds.readQStringList() idx = 0 for f in recentFiles: if f != "" and f is not None: self.recentConfigs[idx].setData(f) self.recentConfigs[idx].setText(f) self.recentConfigs[idx].setVisible(True) idx += 1 if idx >= len(self.recentConfigs): break logger.debug("restoring config state done") def saveState(self): """ Save the state of the configuration gui service (namely the recently open config files). This is saved in QSettings because it is used across config files. :return: """ logger.debug("saving config state ...") settings = QSettings() b = QByteArray() ds = QDataStream(b, QIODevice.WriteOnly) l = [ rc.data() for rc in self.recentConfigs if rc.isVisible() and rc.data() is not None and rc.data() != "" ] ds.writeQStringList(l) settings.setValue("ConfigurationRecentFiles", b) logger.debug("saving config state done (%s)", l)
class Snippets(QDialog): def __init__(self, context, parent=None): super(Snippets, self).__init__(parent) # Create widgets self.setWindowModality(Qt.ApplicationModal) self.title = QLabel(self.tr("Snippet Editor")) self.saveButton = QPushButton(self.tr("&Save")) self.saveButton.setShortcut(QKeySequence(self.tr("Ctrl+S"))) self.runButton = QPushButton(self.tr("&Run")) self.runButton.setShortcut(QKeySequence(self.tr("Ctrl+R"))) self.closeButton = QPushButton(self.tr("Close")) self.clearHotkeyButton = QPushButton(self.tr("Clear Hotkey")) self.setWindowTitle(self.title.text()) #self.newFolderButton = QPushButton("New Folder") self.browseButton = QPushButton("Browse Snippets") self.browseButton.setIcon(QIcon.fromTheme("edit-undo")) self.deleteSnippetButton = QPushButton("Delete") self.newSnippetButton = QPushButton("New Snippet") self.edit = QCodeEditor(HIGHLIGHT_CURRENT_LINE=False, SyntaxHighlighter=PythonHighlighter) self.edit.setPlaceholderText("python code") self.resetting = False self.columns = 3 self.context = context self.keySequenceEdit = QKeySequenceEdit(self) self.currentHotkey = QKeySequence() self.currentHotkeyLabel = QLabel("") self.currentFileLabel = QLabel() self.currentFile = "" self.snippetDescription = QLineEdit() self.snippetDescription.setPlaceholderText("optional description") #Set Editbox Size font = getMonospaceFont(self) self.edit.setFont(font) font = QFontMetrics(font) self.edit.setTabStopWidth(4 * font.width(' ')); #TODO, replace with settings API #Files self.files = QFileSystemModel() self.files.setRootPath(snippetPath) self.files.setNameFilters(["*.py"]) #Tree self.tree = QTreeView() self.tree.setModel(self.files) self.tree.setSortingEnabled(True) self.tree.hideColumn(2) self.tree.sortByColumn(0, Qt.AscendingOrder) self.tree.setRootIndex(self.files.index(snippetPath)) for x in range(self.columns): #self.tree.resizeColumnToContents(x) self.tree.header().setSectionResizeMode(x, QHeaderView.ResizeToContents) treeLayout = QVBoxLayout() treeLayout.addWidget(self.tree) treeButtons = QHBoxLayout() #treeButtons.addWidget(self.newFolderButton) treeButtons.addWidget(self.browseButton) treeButtons.addWidget(self.newSnippetButton) treeButtons.addWidget(self.deleteSnippetButton) treeLayout.addLayout(treeButtons) treeWidget = QWidget() treeWidget.setLayout(treeLayout) # Create layout and add widgets buttons = QHBoxLayout() buttons.addWidget(self.clearHotkeyButton) buttons.addWidget(self.keySequenceEdit) buttons.addWidget(self.currentHotkeyLabel) buttons.addWidget(self.closeButton) buttons.addWidget(self.runButton) buttons.addWidget(self.saveButton) description = QHBoxLayout() description.addWidget(QLabel(self.tr("Description: "))) description.addWidget(self.snippetDescription) vlayoutWidget = QWidget() vlayout = QVBoxLayout() vlayout.addLayout(description) vlayout.addWidget(self.edit) vlayout.addLayout(buttons) vlayoutWidget.setLayout(vlayout) hsplitter = QSplitter() hsplitter.addWidget(treeWidget) hsplitter.addWidget(vlayoutWidget) hlayout = QHBoxLayout() hlayout.addWidget(hsplitter) self.showNormal() #Fixes bug that maximized windows are "stuck" #Because you can't trust QT to do the right thing here if (sys.platform == "darwin"): self.settings = QSettings("Vector35", "Snippet Editor") else: self.settings = QSettings("Vector 35", "Snippet Editor") if self.settings.contains("ui/snippeteditor/geometry"): self.restoreGeometry(self.settings.value("ui/snippeteditor/geometry")) else: self.edit.setMinimumWidth(80 * font.averageCharWidth()) self.edit.setMinimumHeight(30 * font.lineSpacing()) # Set dialog layout self.setLayout(hlayout) # Add signals self.saveButton.clicked.connect(self.save) self.closeButton.clicked.connect(self.close) self.runButton.clicked.connect(self.run) self.clearHotkeyButton.clicked.connect(self.clearHotkey) self.tree.selectionModel().selectionChanged.connect(self.selectFile) self.newSnippetButton.clicked.connect(self.newFileDialog) self.deleteSnippetButton.clicked.connect(self.deleteSnippet) #self.newFolderButton.clicked.connect(self.newFolder) self.browseButton.clicked.connect(self.browseSnippets) if self.settings.contains("ui/snippeteditor/selected"): selectedName = self.settings.value("ui/snippeteditor/selected") self.tree.selectionModel().select(self.files.index(selectedName), QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) if self.tree.selectionModel().hasSelection(): self.selectFile(self.tree.selectionModel().selection(), None) self.edit.setFocus() cursor = self.edit.textCursor() cursor.setPosition(self.edit.document().characterCount()-1) self.edit.setTextCursor(cursor) else: self.readOnly(True) else: self.readOnly(True) @staticmethod def registerAllSnippets(): for action in list(filter(lambda x: x.startswith("Snippets\\"), UIAction.getAllRegisteredActions())): if action == "Snippets\\Snippet Editor...": continue UIActionHandler.globalActions().unbindAction(action) Menu.mainMenu("Tools").removeAction(action) UIAction.unregisterAction(action) for snippet in includeWalk(snippetPath, ".py"): snippetKeys = None (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(snippet) actionText = actionFromSnippet(snippet, snippetDescription) if snippetCode: if snippetKeys == None: UIAction.registerAction(actionText) else: UIAction.registerAction(actionText, snippetKeys) UIActionHandler.globalActions().bindAction(actionText, UIAction(makeSnippetFunction(snippetCode))) Menu.mainMenu("Tools").addAction(actionText, "Snippets") def clearSelection(self): self.keySequenceEdit.clear() self.currentHotkey = QKeySequence() self.currentHotkeyLabel.setText("") self.currentFileLabel.setText("") self.snippetDescription.setText("") self.edit.clear() self.tree.clearSelection() self.currentFile = "" def reject(self): self.settings.setValue("ui/snippeteditor/geometry", self.saveGeometry()) if self.snippetChanged(): question = QMessageBox.question(self, self.tr("Discard"), self.tr("You have unsaved changes, quit anyway?")) if question != QMessageBox.StandardButton.Yes: return self.accept() def browseSnippets(self): url = QUrl.fromLocalFile(snippetPath) QDesktopServices.openUrl(url); def newFolder(self): (folderName, ok) = QInputDialog.getText(self, self.tr("Folder Name"), self.tr("Folder Name: ")) if ok and folderName: index = self.tree.selectionModel().currentIndex() selection = self.files.filePath(index) if QFileInfo(selection).isDir(): QDir(selection).mkdir(folderName) else: QDir(snippetPath).mkdir(folderName) def selectFile(self, new, old): if (self.resetting): self.resetting = False return if len(new.indexes()) == 0: self.clearSelection() self.currentFile = "" self.readOnly(True) return newSelection = self.files.filePath(new.indexes()[0]) self.settings.setValue("ui/snippeteditor/selected", newSelection) if QFileInfo(newSelection).isDir(): self.readOnly(True) self.clearSelection() self.currentFile = "" return if old and old.length() > 0: oldSelection = self.files.filePath(old.indexes()[0]) if not QFileInfo(oldSelection).isDir() and self.snippetChanged(): question = QMessageBox.question(self, self.tr("Discard"), self.tr("Snippet changed. Discard changes?")) if question != QMessageBox.StandardButton.Yes: self.resetting = True self.tree.selectionModel().select(old, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) return False self.currentFile = newSelection self.loadSnippet() def loadSnippet(self): self.currentFileLabel.setText(QFileInfo(self.currentFile).baseName()) (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile) self.snippetDescription.setText(snippetDescription) if snippetDescription else self.snippetDescription.setText("") self.keySequenceEdit.setKeySequence(snippetKeys) if snippetKeys else self.keySequenceEdit.setKeySequence(QKeySequence("")) self.edit.setPlainText(snippetCode) if snippetCode else self.edit.setPlainText("") self.readOnly(False) def newFileDialog(self): (snippetName, ok) = QInputDialog.getText(self, self.tr("Snippet Name"), self.tr("Snippet Name: ")) if ok and snippetName: if not snippetName.endswith(".py"): snippetName += ".py" index = self.tree.selectionModel().currentIndex() selection = self.files.filePath(index) if QFileInfo(selection).isDir(): path = os.path.join(selection, snippetName) else: path = os.path.join(snippetPath, snippetName) self.readOnly(False) open(path, "w").close() self.tree.setCurrentIndex(self.files.index(path)) log_debug("Snippet %s created." % snippetName) def readOnly(self, flag): self.keySequenceEdit.setEnabled(not flag) self.snippetDescription.setReadOnly(flag) self.edit.setReadOnly(flag) if flag: self.snippetDescription.setDisabled(True) self.edit.setDisabled(True) else: self.snippetDescription.setEnabled(True) self.edit.setEnabled(True) def deleteSnippet(self): selection = self.tree.selectedIndexes()[::self.columns][0] #treeview returns each selected element in the row snippetName = self.files.fileName(selection) question = QMessageBox.question(self, self.tr("Confirm"), self.tr("Confirm deletion: ") + snippetName) if (question == QMessageBox.StandardButton.Yes): log_debug("Deleting snippet %s." % snippetName) self.clearSelection() self.files.remove(selection) self.registerAllSnippets() def snippetChanged(self): if (self.currentFile == "" or QFileInfo(self.currentFile).isDir()): return False (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile) if snippetKeys == None and not self.keySequenceEdit.keySequence().isEmpty(): return True if snippetKeys != None and snippetKeys != self.keySequenceEdit.keySequence().toString(): return True return self.edit.toPlainText() != snippetCode or \ self.snippetDescription.text() != snippetDescription def save(self): log_debug("Saving snippet %s" % self.currentFile) outputSnippet = codecs.open(self.currentFile, "w", "utf-8") outputSnippet.write("#" + self.snippetDescription.text() + "\n") outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n") outputSnippet.write(self.edit.toPlainText()) outputSnippet.close() self.registerAllSnippets() def run(self): if self.context == None: log_warn("Cannot run snippets outside of the UI at this time.") return if self.snippetChanged(): question = QMessageBox.question(self, self.tr("Confirm"), self.tr("You have unsaved changes, must save first. Save?")) if (question == QMessageBox.StandardButton.No): return else: self.save() actionText = actionFromSnippet(self.currentFile, self.snippetDescription.text()) UIActionHandler.globalActions().executeAction(actionText, self.context) log_debug("Saving snippet %s" % self.currentFile) outputSnippet = codecs.open(self.currentFile, "w", "utf-8") outputSnippet.write("#" + self.snippetDescription.text() + "\n") outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n") outputSnippet.write(self.edit.toPlainText()) outputSnippet.close() self.registerAllSnippets() def clearHotkey(self): self.keySequenceEdit.clear()
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.proxyModel = SortFilterProxyModel() self.proxyModel.setDynamicSortFilter(True) self.sourceGroupBox = QGroupBox("Original Model") self.proxyGroupBox = QGroupBox("Sorted/Filtered Model") self.sourceView = QTreeView() self.sourceView.setRootIsDecorated(False) self.sourceView.setAlternatingRowColors(True) self.proxyView = QTreeView() self.proxyView.setRootIsDecorated(False) self.proxyView.setAlternatingRowColors(True) self.proxyView.setModel(self.proxyModel) self.proxyView.setSortingEnabled(True) self.sortCaseSensitivityCheckBox = QCheckBox("Case sensitive sorting") self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter") self.filterPatternLineEdit = QLineEdit() self.filterPatternLabel = QLabel("&Filter pattern:") self.filterPatternLabel.setBuddy(self.filterPatternLineEdit) self.filterSyntaxComboBox = QComboBox() self.filterSyntaxComboBox.addItem("Regular expression", QRegExp.RegExp) self.filterSyntaxComboBox.addItem("Wildcard", QRegExp.Wildcard) self.filterSyntaxComboBox.addItem("Fixed string", QRegExp.FixedString) self.filterSyntaxLabel = QLabel("Filter &syntax:") self.filterSyntaxLabel.setBuddy(self.filterSyntaxComboBox) self.filterColumnComboBox = QComboBox() self.filterColumnComboBox.addItem("Subject") self.filterColumnComboBox.addItem("Sender") self.filterColumnComboBox.addItem("Date") self.filterColumnLabel = QLabel("Filter &column:") self.filterColumnLabel.setBuddy(self.filterColumnComboBox) self.filterPatternLineEdit.textChanged.connect( self.filterRegExpChanged) self.filterSyntaxComboBox.currentIndexChanged.connect( self.filterRegExpChanged) self.filterColumnComboBox.currentIndexChanged.connect( self.filterColumnChanged) self.filterCaseSensitivityCheckBox.toggled.connect( self.filterRegExpChanged) self.sortCaseSensitivityCheckBox.toggled.connect(self.sortChanged) sourceLayout = QHBoxLayout() sourceLayout.addWidget(self.sourceView) self.sourceGroupBox.setLayout(sourceLayout) proxyLayout = QGridLayout() proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3) proxyLayout.addWidget(self.filterPatternLabel, 1, 0) proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1, 1, 2) proxyLayout.addWidget(self.filterSyntaxLabel, 2, 0) proxyLayout.addWidget(self.filterSyntaxComboBox, 2, 1, 1, 2) proxyLayout.addWidget(self.filterColumnLabel, 3, 0) proxyLayout.addWidget(self.filterColumnComboBox, 3, 1, 1, 2) proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 4, 0, 1, 2) proxyLayout.addWidget(self.sortCaseSensitivityCheckBox, 4, 2) self.proxyGroupBox.setLayout(proxyLayout) mainLayout = QVBoxLayout() mainLayout.addWidget(self.sourceGroupBox) mainLayout.addWidget(self.proxyGroupBox) self.setLayout(mainLayout) self.setWindowTitle("Basic Sort/Filter Model") self.resize(500, 450) self.proxyView.sortByColumn(SENDER, Qt.AscendingOrder) self.filterColumnComboBox.setCurrentIndex(SENDER) self.filterPatternLineEdit.setText("Andy|Grace") self.filterCaseSensitivityCheckBox.setChecked(True) self.sortCaseSensitivityCheckBox.setChecked(True) def setSourceModel(self, model): self.proxyModel.setSourceModel(model) self.sourceView.setModel(model) def filterRegExpChanged(self): syntax_nr = self.filterSyntaxComboBox.itemData( self.filterSyntaxComboBox.currentIndex()) syntax = QRegExp.PatternSyntax(syntax_nr) if self.filterCaseSensitivityCheckBox.isChecked(): caseSensitivity = Qt.CaseSensitive else: caseSensitivity = Qt.CaseInsensitive regExp = QRegExp(self.filterPatternLineEdit.text(), caseSensitivity, syntax) self.proxyModel.setFilterRegExp(regExp) def filterColumnChanged(self): self.proxyModel.setFilterKeyColumn( self.filterColumnComboBox.currentIndex()) def sortChanged(self): if self.sortCaseSensitivityCheckBox.isChecked(): caseSensitivity = Qt.CaseSensitive else: caseSensitivity = Qt.CaseInsensitive self.proxyModel.setSortCaseSensitivity(caseSensitivity)
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.proxyModel = QSortFilterProxyModel() self.proxyModel.setDynamicSortFilter(True) self.sourceGroupBox = QGroupBox("Original Model") self.proxyGroupBox = QGroupBox("Sorted/Filtered Model") self.sourceView = QTreeView() self.sourceView.setRootIsDecorated(False) self.sourceView.setAlternatingRowColors(True) self.proxyView = QTreeView() self.proxyView.setRootIsDecorated(False) self.proxyView.setAlternatingRowColors(True) self.proxyView.setModel(self.proxyModel) self.proxyView.setSortingEnabled(True) self.sortCaseSensitivityCheckBox = QCheckBox("Case sensitive sorting") self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter") self.filterPatternLineEdit = QLineEdit() self.filterPatternLineEdit.setClearButtonEnabled(True) self.filterPatternLabel = QLabel("&Filter pattern:") self.filterPatternLabel.setBuddy(self.filterPatternLineEdit) self.filterSyntaxComboBox = QComboBox() self.filterSyntaxComboBox.addItem("Regular expression", REGULAR_EXPRESSION) self.filterSyntaxComboBox.addItem("Wildcard", WILDCARD) self.filterSyntaxComboBox.addItem("Fixed string", FIXED_STRING) self.filterSyntaxLabel = QLabel("Filter &syntax:") self.filterSyntaxLabel.setBuddy(self.filterSyntaxComboBox) self.filterColumnComboBox = QComboBox() self.filterColumnComboBox.addItem("Subject") self.filterColumnComboBox.addItem("Sender") self.filterColumnComboBox.addItem("Date") self.filterColumnLabel = QLabel("Filter &column:") self.filterColumnLabel.setBuddy(self.filterColumnComboBox) self.filterPatternLineEdit.textChanged.connect(self.filterRegExpChanged) self.filterSyntaxComboBox.currentIndexChanged.connect(self.filterRegExpChanged) self.filterColumnComboBox.currentIndexChanged.connect(self.filterColumnChanged) self.filterCaseSensitivityCheckBox.toggled.connect(self.filterRegExpChanged) self.sortCaseSensitivityCheckBox.toggled.connect(self.sortChanged) sourceLayout = QHBoxLayout() sourceLayout.addWidget(self.sourceView) self.sourceGroupBox.setLayout(sourceLayout) proxyLayout = QGridLayout() proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3) proxyLayout.addWidget(self.filterPatternLabel, 1, 0) proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1, 1, 2) proxyLayout.addWidget(self.filterSyntaxLabel, 2, 0) proxyLayout.addWidget(self.filterSyntaxComboBox, 2, 1, 1, 2) proxyLayout.addWidget(self.filterColumnLabel, 3, 0) proxyLayout.addWidget(self.filterColumnComboBox, 3, 1, 1, 2) proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 4, 0, 1, 2) proxyLayout.addWidget(self.sortCaseSensitivityCheckBox, 4, 2) self.proxyGroupBox.setLayout(proxyLayout) mainLayout = QVBoxLayout() mainLayout.addWidget(self.sourceGroupBox) mainLayout.addWidget(self.proxyGroupBox) self.setLayout(mainLayout) self.setWindowTitle("Basic Sort/Filter Model") self.resize(500, 450) self.proxyView.sortByColumn(1, Qt.AscendingOrder) self.filterColumnComboBox.setCurrentIndex(1) self.filterPatternLineEdit.setText("Andy|Grace") self.filterCaseSensitivityCheckBox.setChecked(True) self.sortCaseSensitivityCheckBox.setChecked(True) def setSourceModel(self, model): self.proxyModel.setSourceModel(model) self.sourceView.setModel(model) def filterRegExpChanged(self): syntax_nr = self.filterSyntaxComboBox.currentData() pattern = self.filterPatternLineEdit.text() if syntax_nr == WILDCARD: pattern = QRegularExpression.wildcardToRegularExpression(pattern) elif syntax_nr == FIXED_STRING: pattern = QRegularExpression.escape(pattern) regExp = QRegularExpression(pattern) if not self.filterCaseSensitivityCheckBox.isChecked(): options = regExp.patternOptions() options |= QRegularExpression.CaseInsensitiveOption regExp.setPatternOptions(options) self.proxyModel.setFilterRegularExpression(regExp) def filterColumnChanged(self): self.proxyModel.setFilterKeyColumn(self.filterColumnComboBox.currentIndex()) def sortChanged(self): if self.sortCaseSensitivityCheckBox.isChecked(): caseSensitivity = Qt.CaseSensitive else: caseSensitivity = Qt.CaseInsensitive self.proxyModel.setSortCaseSensitivity(caseSensitivity)
class MainWindow(QMainWindow): def __init__(self, app): super(MainWindow, self).__init__() self._app = app self._selectedIndex = None # model nodeFactory = NodeFactory() rootNode = nodeFactory.create(NodeType.General, 'Root') # for i in range(10000): # for testing childNode0 = nodeFactory.create(NodeType.General, 'RightPirateLeg', rootNode) childNode1 = nodeFactory.create(NodeType.General, 'RightPirateLeg_END', childNode0) childNode2 = nodeFactory.create(NodeType.General, 'LeftFemur', rootNode) childNode3 = nodeFactory.create(NodeType.Sphere, 'LeftTibia', childNode2) childNode4 = nodeFactory.create(NodeType.Sphere, 'LeftFoot', childNode3) transform = childNode4.component(ComponentType.Transform) qTransform = transform.component() translation = qTransform.translation() translation.setX(5) qTransform.setTranslation(translation) # childNode5 = nodeFactory.create(NodeType.Box, 'LeftFoot_END', childNode4) self._model = SceneGraphModel(rootNode) self._sceneView = SceneView(rootNode.entity()) self._container = self.createWindowContainer(self._sceneView) # scene graph view self._treeView = QTreeView() self._treeView.setModel(self._model) self._treeView.setHeaderHidden(True) self._treeView.setAlternatingRowColors(True) dockWidget = QDockWidget() dockWidget.setWidget(self._treeView) dockWidget.setWindowTitle('Scene Graph') dockWidget.setObjectName('sceneGraph') sceneGraphToggleAction = dockWidget.toggleViewAction() self.addDockWidget(Qt.LeftDockWidgetArea, dockWidget) # property editor propertyEditor = PropertyEditor(self._model, nodeFactory, FieldFactory()) dockWidget = QDockWidget() dockWidget.setWidget(propertyEditor) dockWidget.setWindowTitle('Property Editor') dockWidget.setObjectName('propertyEditor') propertyEditorToggleAction = dockWidget.toggleViewAction() self.addDockWidget(Qt.RightDockWidgetArea, dockWidget) # menu menuBar = self.menuBar() menu = menuBar.addMenu('&File') exitAction = menu.addAction('E&xit') exitAction.triggered.connect(self.close) menu.addAction(exitAction) menu = menuBar.addMenu('&Windows') menu.addAction(sceneGraphToggleAction) menu.addAction(propertyEditorToggleAction) menuBar.addMenu(menu) # central widget #button = QPushButton() self.setCentralWidget(self._container) # selection change event selectionModel = self._treeView.selectionModel() selectionModel.currentChanged.connect(propertyEditor.changeSelection) # click event #button.clicked.connect(self.buttonClicked) def selectedNode(self): indices = self._treeView.selectedIndexes() result = None if len(indices) == 1: self._selectedIndex = indices[0] result = self._selectedIndex.internalPointer() return result def commitChange(self): self._model.dataChanged.emit(self._selectedIndex, self._selectedIndex) def closeEvent(self, event): self._app.exit() # data change example def buttonClicked(self): node = self.selectedNode() if not node: return general = node.component(ComponentType.General) general.setName('CLICKED') self.commitChange()
class MyWindow(QWidget): def __init__(self, *args): super().__init__() self.setFixedSize(930, 631) self.setLocale(QLocale(QLocale.English, QLocale.UnitedStates)) self.setWindowTitle( QCoreApplication.translate("MainWindow", "PyInspect")) self.central_widget = QWidget(self) self.comboBox = QComboBox(self.central_widget) self.comboBox.setGeometry(QRect(10, 10, 451, 22)) self.comboBox.setMouseTracking(False) self.comboBox.setMaxVisibleItems(5) self.comboBox.setObjectName("comboBox") for _backend in backend.registry.backends.keys(): self.comboBox.addItem(_backend) self.tree_view = QTreeView(self.central_widget) self.tree_view.setGeometry(QRect(10, 40, 451, 581)) self.tree_view.setColumnWidth(0, 150) self.comboBox.setCurrentText('uia') self.__show_tree() self.table_view = QTableView(self.central_widget) self.table_view.setGeometry(QRect(470, 40, 451, 581)) self.comboBox.activated[str].connect(self.__show_tree) self.tree_view.clicked.connect(self.__show_property) def __show_tree(self, _backend='uia'): self.__initialize_calc(_backend) def __initialize_calc(self, _backend): self.element_info = backend.registry.backends[ _backend].element_info_class() self.tree_model = MyTreeModel(self.element_info, _backend) self.tree_model.setHeaderData(0, Qt.Horizontal, 'Controls') self.tree_view.setModel(self.tree_model) def __show_property(self, index=None): data = index.data() self.table_model = MyTableModel(self.tree_model.props_dict.get(data), self) self.table_view.wordWrap() self.table_view.setModel(self.table_model) self.table_view.setColumnWidth(1, 320) element = self.tree_model.element_dict.get(data, None) if element is not None: with contextlib.suppress(Exception): element.set_focus() locate_element = center_locate_element(element) if locate_element is not None: pyperclip.copy('{0}, {1}'.format(*locate_element)) else: pyperclip.copy('Retângulo não identificado.') try: im: Image = element.capture_as_image() im.show() except: alert( title='Atenção', text= 'O elemento selecionado não é uma imagem ou não contém o atributo show.' ) element.draw_outline(colour='green', thickness=4)
class NavigationWidget(QWidget): def __init__(self, index_filename): QWidget.__init__(self) self.data_index = pd.DataFrame() self.data_info = aecg.tools.indexer.StudyInfo() self.data_index_stats = aecg.tools.indexer.StudyStats( self.data_info, self.data_index) # Getting the Models self.project_loaded = '' self.projectmodel = ProjectTreeModel() # Creating a QTreeView for displaying the selected project index self.project_treeview = QTreeView() self.project_treeview.setModel(self.projectmodel) self.phorizontal_header = self.project_treeview.header() self.phorizontal_header.setSectionResizeMode( QHeaderView.ResizeToContents) self.phorizontal_header.setStretchLastSection(True) self.sp_right = QSplitter(Qt.Orientation.Horizontal) self.sp_left = QSplitter(Qt.Orientation.Vertical, self.sp_right) # NavigationWidget Layout self.main_layout = QVBoxLayout() size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) # Left side # Left side - Top layout # Left side - Bottom Layout size.setVerticalStretch(4) self.project_treeview.setSizePolicy(size) self.sp_left.addWidget(self.project_treeview) # Right side size = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) size.setHorizontalStretch(2) size.setHeightForWidth(False) self.tabdisplays = TabDisplays(self) self.sp_right.addWidget(self.tabdisplays) self.tabdisplays.validator_data_ready.connect( self.load_projectindex_after_validation) self.sp_right.setSizePolicy(size) # Set the layout to the QWidget self.main_layout.addWidget(self.sp_right) self.setLayout(self.main_layout) self.tabdisplays.setCurrentWidget(self.tabdisplays.validator) # Load study index if index_filename and (index_filename != ""): if os.path.exists(index_filename): self.load_projectindex(os.path.normpath(index_filename)) else: QMessageBox.warning(self, f"Study index file not found", f"{index_filename} not found") projectindex_loaded = Signal() projectindexstats_loaded = Signal() def load_projectindex_after_validation(self): self.load_projectindex( os.path.normpath(self.tabdisplays.study_info_file.text())) def load_projectindex(self, project_idx_file): index_loaded = False stats_loaded = False if project_idx_file != "": if os.path.exists(project_idx_file): try: self.parent().status.showMessage( f"Loading {project_idx_file}") progress = QProgressDialog("Loading index file...", "Cancel", 0, 3, self) progress.setWindowTitle("Loading study index") progress.setWindowModality(Qt.WindowModal) progress.setMinimumWidth(300) progress.setMinimumDuration(0) progress.forceShow() progress.setValue(0) wb = load_workbook(project_idx_file, read_only=True) progress.setValue(1) ws = wb['Index'] ws.reset_dimensions() data = ws.values cols = next(data) data = list(data) progress.setValue(2) self.data_index = pd.DataFrame(data, columns=cols).fillna("") progress.setValue(3) progress.close() # Parse index to the tree num_ecgs = 0 if "EGREFID" in self.data_index.columns: num_ecgs = self.data_index[[ "ZIPFILE", "AECGXML", "EGREFID", "WFTYPE" ]].drop_duplicates().shape[0] progress = QProgressDialog("Parsing index ...", "Cancel", 0, num_ecgs, self) progress.setWindowTitle("Loading study index") progress.setLabelText("Parsing index...") progress.setWindowModality(Qt.WindowModal) progress.setMinimumWidth(300) progress.setMinimumDuration(0) progress.forceShow() progress.setValue(0) self.projectmodel = ProjectTreeModel( self.data_index, progress_dialog=progress) self.project_treeview.setModel(self.projectmodel) self.project_treeview.selectionModel().\ selectionChanged.connect(self.load_data) progress.close() else: QMessageBox.warning( self, "EGREFID missing", f"EGREFID column missing Index sheet of " f"{project_idx_file}") self.project_loaded = project_idx_file # Reset aECG display self.tabdisplays.aecg_display.aecg_data = None self.tabdisplays.aecg_display.plot_aecg() # Populate study information/validator tab self.tabdisplays.load_study_info(project_idx_file) self.data_index_info = self.tabdisplays.studyindex_info index_loaded = True try: progress = QProgressDialog( "Loading study index stats...", "Cancel", 0, 3, self) progress.setWindowTitle("Loading study index stats") progress.setWindowModality(Qt.WindowModal) progress.setMinimumWidth(300) progress.setMinimumDuration(0) progress.forceShow() progress.setValue(0) ws = wb['Stats'] ws.reset_dimensions() data = ws.values cols = next(data) data = list(data) progress.setValue(1) progress.forceShow() statsdf = pd.DataFrame(data, columns=cols).fillna("") progress.setValue(2) progress.forceShow() self.data_index_stats = aecg.tools.indexer.StudyStats() self.data_index_stats.__dict__.update( statsdf.set_index("Property").transpose(). reset_index(drop=True).to_dict('index')[0]) progress.setValue(3) progress.forceShow() progress.close() stats_loaded = True except Exception as ex: QMessageBox.warning( self, f"Error loading study index stats", f"Error loading study index stats from" f"{project_idx_file}\n{str(ex)}") except Exception as ex: QMessageBox.warning( self, f"Error loading study index", f"Error loading study index from {project_idx_file}" f"\n{str(ex)}") else: QMessageBox.warning(self, f"Study index file not found", f"{project_idx_file} not found") if index_loaded: self.projectindex_loaded.emit() if stats_loaded: self.projectindexstats_loaded.emit() self.parentWidget().status.clearMessage() def load_data(self, selected, deselected): self.tabdisplays.setCurrentWidget(self.tabdisplays.waveforms) rhythm = self.projectmodel.itemData(selected.indexes()[2])[0] derived = self.projectmodel.itemData(selected.indexes()[3])[0] # Get study directory provided in the GUI studydir = self.tabdisplays.effective_aecgs_dir(self, silent=True) # Calculate effective study dir aecg_xml_file = self.projectmodel.itemData(selected.indexes()[5])[0] if aecg_xml_file != "": zipfile = self.projectmodel.itemData(selected.indexes()[4])[0] if zipfile != "": zipfile = os.path.join(studydir, zipfile) else: aecg_xml_file = os.path.join(studydir, aecg_xml_file) # Load aECG file aecg_data = aecg.io.read_aecg(aecg_xml_file, zipfile, include_digits=True, in_memory_xml=True, log_validation=False) if aecg_data.xmlfound: # Plot aECG self.tabdisplays.aecg_display.set_aecg(aecg_data) self.tabdisplays.aecg_display.plot_aecg( rhythm, derived, ecg_layout=aecg.utils.ECG_plot_layout( self.tabdisplays.cbECGLayout.currentIndex() + 1)) # Populate XML viewer self.tabdisplays.xml_display.setText(aecg_data.xmlstring()) else: QMessageBox.warning(self, "aECG XML file not found", f"aECG XML {aecg_xml_file} not found") self.parentWidget().update_status_bar()
class MP3TagManipulator(QWidget): last_directory = '.' song = None def __init__(self): super().__init__() self.title = 'MP3TagManipulator v1.0' self.top = 100 self.left = 100 self.height = 400 self.width = 640 self.init_ui() def init_ui(self): self.setWindowTitle(self.title) self.setGeometry(self.top, self.left, self.width, self.height) # layout root of application main_layout = QVBoxLayout(self) self.setLayout(main_layout) # to set the model of tree model = QFileSystemModel() model.setRootPath("/home/denison/Downloads/music") model.setNameFilters(['*.mp3', '*.m4a', '*.flac']) model.setNameFilterDisables(False) self.tree = QTreeView() self.tree.setModel(model) self.tree.setAnimated(True) self.tree.setColumnWidth(0, 500) file_layout = QHBoxLayout() label_file = QLabel('file/directory') text_file = QLineEdit() btn_load = QPushButton('load') file_layout.addWidget(label_file) file_layout.addWidget(text_file) file_layout.addWidget(btn_load) grid_info_layout = QGridLayout() # strings to labels self.labels = [ 'ARTIST', 'ALBUMARTIST', 'ALBUM', 'TITLE', 'GENRE', 'DATE' ] # line edits to tags self.text_artist = QLineEdit('ARTIST') self.text_album = QLineEdit('ALBUM') self.text_album_artist = QLineEdit('ALBUMARTIST') self.text_title = QLineEdit('TITLE') self.text_genre = QLineEdit('GENRE') self.text_date = QLineEdit('DATE') self.text_tags = [ self.text_artist, self.text_album_artist, self.text_album, self.text_title, self.text_genre, self.text_date ] for text in self.text_tags: text.setEnabled(False) #text.textChanged.connect(self.enable_save) # labels for label, i in zip(self.labels, range(6)): grid_info_layout.addWidget(QLabel(label), i, 0) # cb_artist = QCheckBox() # cb_album_artist = QCheckBox() # cb_album = QCheckBox() # cb_title = QCheckBox() # cb_genre = QCheckBox() # cb_date = QCheckBox() # self.checkboxes = [ # cb_artist, cb_album_artist, cb_album, # cb_title, cb_genre, cb_date # ] # for cb in self.checkboxes: # cb.setText('editar') # cb_artist.stateChanged.connect(lambda: self.enable_tag_edit(self.text_artist)) # cb_album_artist.stateChanged.connect(lambda: self.enable_tag_edit(self.text_album_artist)) # cb_album.stateChanged.connect(lambda: self.enable_tag_edit(self.text_album)) # cb_title.stateChanged.connect(lambda: self.enable_tag_edit(self.text_title)) # cb_genre.stateChanged.connect(lambda: self.enable_tag_edit(self.text_genre)) # cb_date.stateChanged.connect(lambda: self.enable_tag_edit(self.text_date)) for i, text in zip(range(6), self.text_tags): grid_info_layout.addWidget(text, i, 1) # for cb, i in zip(self.checkboxes, range(6)) : # grid_info_layout.addWidget(cb, i, 2) action_layout = QHBoxLayout() btn_exit = QPushButton('Exit') self.btn_save = QPushButton('save changes') self.btn_save.setDisabled(True) action_layout.addWidget(btn_exit) action_layout.addWidget(self.btn_save) #main_layout.addLayout(file_layout) main_layout.addWidget(self.tree) main_layout.addLayout(grid_info_layout) main_layout.addLayout(action_layout) btn_load.clicked.connect(self.open_file) btn_exit.clicked.connect(self.close_application) self.btn_save.clicked.connect(self.edit_tags) self.tree.doubleClicked.connect(self.get_selected_file) self.show() def enable_edit_text(self): if self.song: for t in self.text_tags: t.setEnabled(True) self.enable_save() else: for t in self.text_tags: t.setEnabled(False) def enable_save(self): self.btn_save.setEnabled(True) def close_application(self): if self.song: self.song.close() print('vazando...;-)') self.close() def enable_tag_edit(self, txt_edit): txt_edit.setEnabled(not txt_edit.isEnabled()) for edit in self.text_tags: edit.textChanged.connect(self.enable_save) print('executou self.enable_tag_edit()') print('não sei o q tá acontecendo :(') def get_selected_file(self): print('executou self.get_selected_file()') selected = self.tree.selectedIndexes()[0] print(selected.model().filePath(selected)) self.song = taglib.File(selected.model().filePath(selected)) self.enable_edit_text() self.load_song_info(self.song) return self.song def edit_tags(self): print("não tá funcionando 8'-(") self.song.tags['ARTIST'] = self.text_artist.text() self.song.tags['ALBUMARTIST'] = self.text_album_artist.text() self.song.tags['ALBUM'] = self.text_album.text() self.song.tags['TITLE'] = self.text_title.text() self.song.tags['GENRE'] = self.text_genre.text() self.song.tags['DATE'] = self.text_date.text() self.song.save() print(self.song.tags) self.btn_save.setDisabled(True) self.song.close() def open_file(self): print('*** quase lá *** ') dialog = QFileDialog(self) dialog.setViewMode(QFileDialog.Detail) file = dialog.getOpenFileName(self, 'load...', self.last_directory, 'songs (*.mp3 *.m4a *.flac *.wma)') song = taglib.File(file[0]) # print(song.tags) self.show_song_info(song) song.close() def load_song_info(self, song): print('executou self.load_song_info()') for t, tag in zip(self.text_tags, self.labels): try: t.setText(song.tags[tag][0]) except KeyError: t.setText('none')
## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ## ## $QT_END_LICENSE$ ## ############################################################################# """PySide2 port of the remoteobjects/modelviewclient example from Qt v5.x""" import sys from PySide2.QtCore import QUrl from PySide2.QtWidgets import (QApplication, QTreeView) from PySide2.QtRemoteObjects import QRemoteObjectNode if __name__ == '__main__': app = QApplication(sys.argv) node = QRemoteObjectNode(QUrl("local:registry")) node.setHeartbeatInterval(1000) view = QTreeView() view.setWindowTitle("RemoteView") view.resize(640, 480) model = node.acquireModel("RemoteModel") view.setModel(model) view.show() sys.exit(app.exec_())
class ProjectWidget(QSplitter): treeCurrentItemChanged = Signal(Container) def __init__(self, parent, f=...): super().__init__(parent, f) self.project = Project() self.current_tree_item: Optional[Container] = None self.main_splitter = self # inheritance from QSplitter may not be preserved self.main_splitter.setOrientation(Qt.Horizontal) self.left_splitter = QSplitter(Qt.Vertical) self.right_splitter = QSplitter(Qt.Vertical) self.main_splitter.addWidget(self.left_splitter) self.main_splitter.addWidget(self.right_splitter) # self.w_parameters = ParametersWidget(self.project.bundle) self.w_parameters_tree = QTreeView() self.w_parameters_tree.setModel(self.project.parameters_model) delegate = ParametersTableDelegate() self.w_parameters_tree.setItemDelegate(delegate) self.selection_model_parameters = self.w_parameters_tree.selectionModel( ) self.selection_model_parameters.currentRowChanged.connect( self._on_tree_row_changed) self.w_curves_tree = QTreeView() self.w_curves_tree.setModel(self.project.curves_model) delegate = CurvesTableDelegate() self.w_curves_tree.setItemDelegate(delegate) self.selection_model_curves = self.w_curves_tree.selectionModel() self.selection_model_curves.currentRowChanged.connect( self._on_tree_row_changed) self.w_light_plot = LightPlotWidget(self.project.curves_model) self.w_rv_plot = RvPlotWidget(self.project.curves_model) self.left_splitter.addWidget(self.w_parameters_tree) self.left_splitter.addWidget(self.w_curves_tree) self.right_splitter.addWidget(self.w_light_plot) self.right_splitter.addWidget(self.w_rv_plot) def resizeEvent(self, arg__1: PySide2.QtGui.QResizeEvent): super().resizeEvent(arg__1) def save_session(self, settings: QSettings): settings.beginGroup('project_splitter') settings.setValue('horiz', self.main_splitter.saveState()) settings.setValue('vert_trees', self.left_splitter.saveState()) settings.setValue('vert_plots', self.right_splitter.saveState()) settings.endGroup() settings.beginGroup('tree_columns') settings.setValue('params', self.w_parameters_tree.header().saveState()) settings.setValue('curves', self.w_curves_tree.header().saveState()) settings.endGroup() def restore_session(self, settings: QSettings): settings.beginGroup('project_splitter') try: self.main_splitter.restoreState(settings.value("horiz")) self.left_splitter.restoreState(settings.value("vert_trees")) self.right_splitter.restoreState(settings.value("vert_plots")) except AttributeError: pass settings.endGroup() settings.beginGroup('tree_columns') try: self.w_parameters_tree.header().restoreState( settings.value("params")) self.w_curves_tree.header().restoreState(settings.value("curves")) except AttributeError: pass settings.endGroup() @Slot(QModelIndex, QModelIndex) def _on_tree_row_changed(self, current: QModelIndex, previous: QModelIndex): model = current.model() prev_item, prev_column = model.item_and_column(previous) curr_item, curr_column = model.item_and_column(current) logger().info(f'Selection changed {prev_item} -> {curr_item}') self.current_tree_item = curr_item self.treeCurrentItemChanged.emit(curr_item) @Slot() def add_lc(self): self.project.add_lc() @Slot() def add_rv(self): self.project.add_rv() @Slot() def del_curve(self): to_del = self.current_tree_item while to_del is not None and not isinstance(to_del, CurveContainer): to_del = to_del.parent() if QMessageBox.question(self, 'Deleting Curve Confirmation', f'Delete the curve "{to_del.objectName()}"?') \ != QMessageBox.Yes: return self.project.delete_curve(to_del)