class SALErrorCodeWidget(QWidget): """Displays errorCode messages.""" def __init__(self, comm): super().__init__() layout = QVBoxLayout() self.plainText = QPlainTextEdit() self.plainText.setReadOnly(True) self.plainText.setLineWrapMode(QPlainTextEdit.NoWrap) self.plainText.setCenterOnScroll(True) font = QFont("Monospace") font.setStyleHint(QFont.TypeWriter) self.plainText.setFont(font) layout.addWidget(self.plainText) self.setLayout(layout) comm.errorCode.connect(self.errorCode) @Slot() def errorCode(self, data): date = datetime.fromtimestamp(data.private_sndStamp).isoformat( sep=" ", timespec="milliseconds") self.plainText.appendHtml( f"{date} [<b>{data.errorCode:06X}</b>] <span style='color:{'green' if data.errorCode==0 else 'red'}'>{data.errorReport}</span>" ) self.plainText.ensureCursorVisible()
def _create_raw_widget(self, ) -> QWidget: widget = QPlainTextEdit(self) font = QFontDatabase.systemFont(QFontDatabase.FixedFont) font.setFixedPitch(True) metrics = QFontMetrics(font) widget.setFont(font) widget.setTabStopDistance(2 * metrics.width(' ')) as_signal_instance(widget.textChanged).connect(self._on_raw_changed, ) return widget
def add_new_script(self): code_text_edit = QPlainTextEdit() code_text_edit.setPlainText('test code') code_text_edit.setFont(self.code_font) # code_text_edit.setStyleSheet('background: black; color: grey;') self.code_text_edits.append(code_text_edit) # print('before: ', self.height()) self.layout().addWidget(code_text_edit) # print('after: ', self.height()) self.parent_node_instance.update_shape()
class MainWindow(QWidget): def __init__(self): super(MainWindow, self).__init__() self.layout = QVBoxLayout() self.setLayout(self.layout) self.functionSelector = OptionsSelector( parent=self, option_manager=function_describing_options()) self.layout.addWidget(self.functionSelector) self.testcaseSelector = OptionsSelector( parent=self, option_manager=testcase_describing_options()) self.layout.addWidget(self.testcaseSelector) self.plainTextEdit = QPlainTextEdit() self.plainTextEdit.setReadOnly(True) self.layout.addWidget(self.plainTextEdit) self.plainTextEdit.setFont(PySide2.QtGui.QFont("Fira Code", 8)) # temp self.functionSelector.optionsSelected.connect( lambda o: self._handle_function_change()) self.testcaseSelector.optionsSelected.connect( lambda o: self._handle_testcase_change()) self._handle_function_change() def _handle_function_change(self): options = self.functionSelector.get_current_option_set() if m := skip_function_description(options): self.plainTextEdit.setPlainText(m) return options_to_disable = disabled_testcase_describing_options(options) self.testcaseSelector.set_disabled_options(options_to_disable) options.update(self.testcaseSelector.get_current_option_set()) if m := skip_testcase_description(options): self.plainTextEdit.setPlainText(m) return
class MainWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self._highlighter = Highlighter() self.setup_file_menu() self.setup_editor() self.setCentralWidget(self._editor) self.setWindowTitle(self.tr("Syntax Highlighter")) def new_file(self): self._editor.clear() def open_file(self, path=""): file_name = path if not file_name: file_name, _ = QFileDialog.getOpenFileName( self, self.tr("Open File"), "", "qmake Files (*.pro *.prf *.pri)") if file_name: inFile = QFile(file_name) if inFile.open(QFile.ReadOnly | QFile.Text): stream = QTextStream(inFile) self._editor.setPlainText(stream.readAll()) def setup_editor(self): variable_format = QTextCharFormat() variable_format.setFontWeight(QFont.Bold) variable_format.setForeground(Qt.blue) self._highlighter.add_mapping("\\b[A-Z_]+\\b", variable_format) single_line_comment_format = QTextCharFormat() single_line_comment_format.setBackground(QColor("#77ff77")) self._highlighter.add_mapping("#[^\n]*", single_line_comment_format) quotation_format = QTextCharFormat() quotation_format.setBackground(Qt.cyan) quotation_format.setForeground(Qt.blue) self._highlighter.add_mapping("\".*\"", quotation_format) function_format = QTextCharFormat() function_format.setFontItalic(True) function_format.setForeground(Qt.blue) self._highlighter.add_mapping("\\b[a-z0-9_]+\\(.*\\)", function_format) font = QFont() font.setFamily("Courier") font.setFixedPitch(True) font.setPointSize(10) self._editor = QPlainTextEdit() self._editor.setFont(font) self._highlighter.setDocument(self._editor.document()) def setup_file_menu(self): file_menu = self.menuBar().addMenu(self.tr("&File")) new_file_act = file_menu.addAction(self.tr("&New...")) new_file_act.setShortcut(QKeySequence(QKeySequence.New)) new_file_act.triggered.connect(self.new_file) open_file_act = file_menu.addAction(self.tr("&Open...")) open_file_act.setShortcut(QKeySequence(QKeySequence.Open)) open_file_act.triggered.connect(self.open_file) quit_act = file_menu.addAction(self.tr("E&xit")) quit_act.setShortcut(QKeySequence(QKeySequence.Quit)) quit_act.triggered.connect(self.close) help_menu = self.menuBar().addMenu("&Help") help_menu.addAction("About &Qt", qApp.aboutQt)
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) layout = QVBoxLayout() self.editor = QPlainTextEdit( ) # Could also use a QTextEdit and set self.editor.setAcceptRichText(False) # Setup the QTextEdit editor configuration fixedfont = QFontDatabase.systemFont(QFontDatabase.FixedFont) fixedfont.setPointSize(12) self.editor.setFont(fixedfont) # self.path holds the path of the currently open file. # If none, we haven't got a file open yet (or creating new). self.path = None layout.addWidget(self.editor) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.status = QStatusBar() self.setStatusBar(self.status) file_toolbar = QToolBar("File") file_toolbar.setIconSize(QSize(14, 14)) self.addToolBar(file_toolbar) file_menu = self.menuBar().addMenu("&File") open_file_action = QAction( QIcon(os.path.join('images', 'blue-folder-open-document.png')), "Open file...", self) open_file_action.setStatusTip("Open file") open_file_action.triggered.connect(self.file_open) file_menu.addAction(open_file_action) file_toolbar.addAction(open_file_action) save_file_action = QAction(QIcon(os.path.join('images', 'disk.png')), "Save", self) save_file_action.setStatusTip("Save current page") save_file_action.triggered.connect(self.file_save) file_menu.addAction(save_file_action) file_toolbar.addAction(save_file_action) saveas_file_action = QAction( QIcon(os.path.join('images', 'disk--pencil.png')), "Save As...", self) saveas_file_action.setStatusTip("Save current page to specified file") saveas_file_action.triggered.connect(self.file_saveas) file_menu.addAction(saveas_file_action) file_toolbar.addAction(saveas_file_action) print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.file_print) file_menu.addAction(print_action) file_toolbar.addAction(print_action) edit_toolbar = QToolBar("Edit") edit_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(edit_toolbar) edit_menu = self.menuBar().addMenu("&Edit") undo_action = QAction( QIcon(os.path.join('images', 'arrow-curve-180-left.png')), "Undo", self) undo_action.setStatusTip("Undo last change") undo_action.triggered.connect(self.editor.undo) edit_menu.addAction(undo_action) redo_action = QAction(QIcon(os.path.join('images', 'arrow-curve.png')), "Redo", self) redo_action.setStatusTip("Redo last change") redo_action.triggered.connect(self.editor.redo) edit_toolbar.addAction(redo_action) edit_menu.addAction(redo_action) edit_menu.addSeparator() cut_action = QAction(QIcon(os.path.join('images', 'scissors.png')), "Cut", self) cut_action.setStatusTip("Cut selected text") cut_action.triggered.connect(self.editor.cut) edit_toolbar.addAction(cut_action) edit_menu.addAction(cut_action) copy_action = QAction( QIcon(os.path.join('images', 'document-copy.png')), "Copy", self) copy_action.setStatusTip("Copy selected text") copy_action.triggered.connect(self.editor.copy) edit_toolbar.addAction(copy_action) edit_menu.addAction(copy_action) paste_action = QAction( QIcon(os.path.join('images', 'clipboard-paste-document-text.png')), "Paste", self) paste_action.setStatusTip("Paste from clipboard") paste_action.triggered.connect(self.editor.paste) edit_toolbar.addAction(paste_action) edit_menu.addAction(paste_action) select_action = QAction( QIcon(os.path.join('images', 'selection-input.png')), "Select all", self) select_action.setStatusTip("Select all text") select_action.triggered.connect(self.editor.selectAll) edit_menu.addAction(select_action) edit_menu.addSeparator() wrap_action = QAction( QIcon(os.path.join('images', 'arrow-continue.png')), "Wrap text to window", self) wrap_action.setStatusTip("Toggle wrap text to window") wrap_action.setCheckable(True) wrap_action.setChecked(True) wrap_action.triggered.connect(self.edit_toggle_wrap) edit_menu.addAction(wrap_action) self.update_title() self.show() def dialog_critical(self, s): dlg = QMessageBox(self) dlg.setText(s) dlg.setIcon(QMessageBox.Critical) dlg.show() def file_open(self): path, _ = QFileDialog.getOpenFileName( self, "Open file", "", "Text documents (*.txt);All files (*.*)") if path: try: with open(path, 'rU') as f: text = f.read() except Exception as e: self.dialog_critical(str(e)) else: self.path = path self.editor.setPlainText(text) self.update_title() def file_save(self): if self.path is None: # If we do not have a path, we need to use Save As. return self.file_saveas() self._save_to_path(self.path) def file_saveas(self): path, _ = QFileDialog.getSaveFileName( self, "Save file", "", "Text documents (*.txt);All files (*.*)") if not path: # If dialog is cancelled, will return '' return self._save_to_path(path) def _save_to_path(self, path): text = self.editor.toPlainText() try: with open(path, 'w') as f: f.write(text) except Exception as e: self.dialog_critical(str(e)) else: self.path = path self.update_title() def file_print(self): dlg = QPrintDialog() if dlg.exec_(): self.editor.print_(dlg.printer()) def update_title(self): self.setWindowTitle( "%s - No2Pads" % (os.path.basename(self.path) if self.path else "Untitled")) def edit_toggle_wrap(self): self.editor.setLineWrapMode(1 if self.editor.lineWrapMode() == 0 else 0)
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 SourceryPane(QWidget, DockContextHandler): def __init__(self, parent, name): global panes panes.append(self) QWidget.__init__(self, parent) DockContextHandler.__init__(self, self, name) self.actionHandler = UIActionHandler() self.actionHandler.setupActionHandler(self) # Top: Headers with line info header_layout = QFormLayout() self.function_info = QLabel("") self.line_info = QLabel("") header_layout.addRow(self.tr("Function:"), self.function_info) header_layout.addRow(self.tr("Line:"), self.line_info) # Middle: main source display pane textbox_layout = QVBoxLayout() self.textbox = QPlainTextEdit() self.textbox.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap) self.textbox.setReadOnly(True) font = getMonospaceFont(self) self.textbox.setFont(font) font = QFontMetrics(font) self.textbox.setMinimumWidth(40 * font.averageCharWidth()) self.textbox.setMinimumHeight(30 * font.lineSpacing()) textbox_layout.addWidget(self.textbox) # Bottom: buttons for stop/start, and substitution paths footer_layout = QVBoxLayout() sync_button_layout = QHBoxLayout() self.sync_button = QPushButton("Turn Source Sync Off") sync_button_layout.addWidget(self.sync_button) path_layout = QFormLayout() self.original_path = QLineEdit() self.substitute_path = QLineEdit() self.substitute_path_button = QPushButton("Do Path Substitution") path_layout.addRow(self.tr("Original Path:"), self.original_path) path_layout.addRow(self.substitute_path_button, self.substitute_path) footer_layout.addLayout(sync_button_layout) footer_layout.addLayout(path_layout) # Putting all the child layouts together layout = QVBoxLayout() layout.addLayout(header_layout) layout.addLayout(textbox_layout) layout.addLayout(footer_layout) self.setLayout(layout) # Set up button signals self.substitute_path_button.clicked.connect(self.do_path_substitution) self.sync_button.clicked.connect(self.toggle_sync) # Actual storage variables self.bv = None self.filename = None self.do_sync = True self.path_substitutions = {} self.failed_substitutions = [] def do_path_substitution(self): original_path = self.original_path.text() new_path = self.substitute_path.text() if isinstance(original_path, bytes): original_path = original_path.decode() new_path = new_path() if original_path == "": log_warn("Path substitution error: Original path can't be blank") elif new_path == "": if original_path in self.path_substitutions: old_sub = self.path_substitutions.pop(original_path) log_info("Removed path substitution: %s -> %s" % (original_path, old_sub)) else: log_warn( "Path substitution error: New substitute path can't be blank" ) else: self.path_substitutions[original_path] = new_path log_info("Added path substitution: %s -> %s" % (original_path, new_path)) self.failed_substitutions = [ ] # clear failures when new path added def toggle_sync(self): if self.do_sync is True: self.do_sync = False self.sync_button.setText("Turn Source Sync On") else: # self.do_sync is False: self.do_sync = True self.sync_button.setText("Turn Source Sync Off") def set_text(self, text): self.textbox.setPlainText(text) def set_line(self, text): self.line_info.setText(text) def set_function(self, text): self.function_info.setText(text) def check_path_substitution(self, path): """Checks for files using path substitutions, going from longest to shortest original path""" sorted_original_paths = sorted(self.path_substitutions.keys(), key=lambda k: len(k), reverse=True) candidate_matches = [] for candidate_path in sorted_original_paths: if candidate_path in path: substitute_pattern = self.path_substitutions[candidate_path] substitute_path = path.replace(candidate_path, substitute_pattern) substitute_path = os.path.expanduser(substitute_path) candidate_matches.append(substitute_path) if os.path.exists(substitute_path): return substitute_path # Only log_warn once per file, and only if the user has tried to add translations if path not in self.failed_substitutions: if len(self.path_substitutions) > 0: log_warn("Failed to find substitution for %s" % path) log_info("Current substitution paths:") for orig_path, sub_path in self.path_substitutions.items(): log_info(" %s => %s" % (orig_path, sub_path)) log_info("Matching patterns' failed substitute paths:") for candidate in candidate_matches: log_info(" %s" % candidate) self.failed_substitutions.append(path) return "" def update_source(self, current_location): source_line = addr2line(self.filename, current_location) line_number_int = -1 text = "" function_name = "" if source_line.startswith("?"): line_text = "No source mapping for address 0x%x" % current_location elif source_line.startswith("ERROR:"): line_text = "%s" % source_line else: filepath, line_number_str, function_name = source_line.split(":") # handle lines like: "16 (discriminator 1)" line_number_int = int(line_number_str.split(' ')[0]) line_text = "%s:%s" % (filepath, line_number_str) # Check for the file, then for subsitutions if not os.path.exists(filepath): new_path = self.check_path_substitution(filepath) if new_path == "": self.textbox.setLineWrapMode( QPlainTextEdit.LineWrapMode.WidgetWidth) text = '[!] Source file "%s" not found\n' % filepath text += '[*] Associated line info: "%s"' % source_line else: filepath = new_path # If we still don't have a good path, the text is set to the correct error if os.path.exists(filepath): self.textbox.setLineWrapMode( QPlainTextEdit.LineWrapMode.NoWrap) with open(filepath, "r") as f: text = f.read() self.set_text(text) self.set_line(line_text) self.set_function(function_name) if line_number_int != -1: self.set_cursor(line_number_int) else: self.reset_cursor() def reset_cursor(self): doc = self.textbox.document() cursor = QTextCursor(doc) cursor.movePosition(QTextCursor.Start) self.textbox.setTextCursor(cursor) def set_cursor(self, line_number): doc = self.textbox.document() cursor = QTextCursor(doc) cursor.movePosition(QTextCursor.Start) for _ in range(line_number - 1): cursor.movePosition(QTextCursor.Down) cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) self.textbox.setTextCursor(cursor) self.textbox.centerCursor() def notifyOffsetChanged(self, offset): if self.filename: if self.do_sync: self.update_source(offset) def shouldBeVisible(self, view_frame): if view_frame is None: return False else: return True def notifyViewChanged(self, view_frame): if view_frame is None: pass else: self.bv = view_frame.actionContext().binaryView self.filename = self.bv.file.original_filename def contextMenuEvent(self, event): self.m_contextMenuManager.show(self.m_menu, self.actionHandler) @staticmethod def create_widget(name, parent, data=None): return SourceryPane(parent, name)
class SummaryWidget(QWidget): def __init__(self, plugin_manager): super(SummaryWidget, self).__init__() self.plugin_manager = plugin_manager icon_path = os.path.join(c.ICON_PATH, "neutral", "quote.png") self.setWindowIcon(QIcon(icon_path)) self.setWindowTitle("Summary") self.settings = QSettings(c.SETTINGS_PATH, QSettings.IniFormat) self.font = QFont(self.settings.value(c.FONT, defaultValue="Arial", type=str)) self.font.setPointSize(12) self.summary_text = QPlainTextEdit() self.summary_text.setReadOnly(True) self.summary_text.setFont(self.font) self.refresh_btn = QPushButton("Refresh") self.refresh_btn.clicked.connect(self.get_summary) self.threshold_value = 1.2 self.threshold = QLineEdit(str(self.threshold_value)) self.threshold.setFixedSize(40, 25) self.validator = QDoubleValidator() self.validator.setLocale(QLocale.English) self.threshold.setValidator(self.validator) self.threshold.textChanged.connect(self.threshold_changed) self.h_box = QHBoxLayout() self.h_box.addWidget(self.refresh_btn) self.h_box.addWidget(self.threshold) self.v_box = QVBoxLayout() self.v_box.addWidget(self.summary_text) self.v_box.addLayout(self.h_box) self.setLayout(self.v_box) self.resize(500, 500) def get_summary(self): if "de" in self.plugin_manager.get_language(): language = "german" elif "en" in self.plugin_manager.get_language(): language = "english" else: return text = self.plugin_manager.get_text() stop_words = set(stopwords.words(language)) words = word_tokenize(text=text, language=language) # count word frequency freq = dict() for word in words: word = word.lower() if word in stop_words: continue if word in freq: freq[word] += 1 else: freq[word] = 1 sentences = sent_tokenize(text, language) sentence_freq = dict() # sum frequency of words in each sentence for word, freq in freq.items(): for sentence in sentences: if word in sentence.lower(): if sentence in sentence_freq: sentence_freq[sentence] += freq else: sentence_freq[sentence] = freq # calculate avg freq per word in sentence so long sentences dont have an advantage for sentence in sentences: sentence_freq[sentence] = sentence_freq[sentence] / len(sentence.split()) # calc average word-frequency per sentence sum_sentece_freq = 0 for sentence in sentence_freq: sum_sentece_freq += sentence_freq[sentence] avg = int(sum_sentece_freq / len(sentence_freq)) # filter sentences that doesn't reach the threshold summary = "" for sentence in sentence_freq: if sentence_freq[sentence] > (self.threshold_value * avg): summary += " " + sentence self.summary_text.setPlainText(summary.strip()) def threshold_changed(self): validator = self.threshold.validator() validator_state = validator.validate(self.threshold.text(), 0)[0] if validator_state == QValidator.Acceptable: color = '#006600' self.threshold_value = float(self.threshold.text()) else: color = '#800000' self.threshold.setStyleSheet('QLineEdit { background-color: %s }' % color) def show(self): super(SummaryWidget, self).show() self.get_summary()
class Notepad(QMainWindow): def __init__(self, *args, **kwargs): super(Notepad, self).__init__(*args, **kwargs) self.setWindowIcon(QIcon('arti.PNG')) self.setGeometry(200, 100, 700, 400) layout = QVBoxLayout() self.editor = QPlainTextEdit() self.editor.setFont(QFont('Roboto', 12)) # self.path holds the path of the currently open file. # If none, we haven't got a file open yet (or creating new). self.path = None layout.addWidget(self.editor) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.status = QStatusBar() self.setStatusBar(self.status) file_toolbar = QToolBar("File") file_toolbar.setIconSize(QSize(14, 14)) self.addToolBar(file_toolbar) file_menu = self.menuBar().addMenu("&File") open_file_action = QAction(QIcon(), "Open file...", self) open_file_action.setStatusTip("Open file") open_file_action.triggered.connect(self.file_open) file_menu.addAction(open_file_action) file_toolbar.addAction(open_file_action) open_file_action.setShortcut(QKeySequence('Ctrl+O')) save_file_action = QAction(QIcon(), "Save", self) save_file_action.setStatusTip("Save current page") save_file_action.triggered.connect(self.file_save) file_menu.addAction(save_file_action) file_toolbar.addAction(save_file_action) save_file_action.setShortcut(QKeySequence('Ctrl+S')) saveas_file_action = QAction(QIcon(), "Save As...", self) saveas_file_action.setStatusTip("Save current page to specified file") saveas_file_action.triggered.connect(self.file_saveas) file_menu.addAction(saveas_file_action) file_toolbar.addAction(saveas_file_action) saveas_file_action.setShortcut(QKeySequence('Ctrl+Shift+S')) print_action = QAction(QIcon(), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.file_print) file_menu.addAction(print_action) file_toolbar.addAction(print_action) print_action.setShortcut(QKeySequence('Ctrl+P')) edit_toolbar = QToolBar("Edit") edit_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(edit_toolbar) edit_menu = self.menuBar().addMenu("&Edit") undo_action = QAction(QIcon(), "Undo", self) undo_action.setStatusTip("Undo last change") undo_action.triggered.connect(self.editor.undo) edit_menu.addAction(undo_action) undo_action.setShortcut(QKeySequence('Ctrl+Z')) redo_action = QAction(QIcon(), "Redo", self) redo_action.setStatusTip("Redo last change") redo_action.triggered.connect(self.editor.redo) edit_toolbar.addAction(redo_action) edit_menu.addAction(redo_action) redo_action.setShortcut(QKeySequence('Ctrl+Y')) edit_menu.addSeparator() cut_action = QAction(QIcon(), "Cut", self) cut_action.setStatusTip("Cut selected text") cut_action.triggered.connect(self.editor.cut) edit_toolbar.addAction(cut_action) edit_menu.addAction(cut_action) cut_action.setShortcut(QKeySequence('Ctrl+X')) copy_action = QAction(QIcon(), "Copy", self) copy_action.setStatusTip("Copy selected text") copy_action.triggered.connect(self.editor.copy) edit_toolbar.addAction(copy_action) edit_menu.addAction(copy_action) copy_action.setShortcut(QKeySequence('Ctrl+C')) paste_action = QAction(QIcon(), "Paste", self) paste_action.setStatusTip("Paste from clipboard") paste_action.triggered.connect(self.editor.paste) edit_toolbar.addAction(paste_action) edit_menu.addAction(paste_action) paste_action.setShortcut(QKeySequence('Ctrl+V')) select_action = QAction(QIcon(), "Select all", self) select_action.setStatusTip("Select all text") select_action.triggered.connect(self.editor.selectAll) edit_menu.addAction(select_action) select_action.setShortcut(QKeySequence('Ctrl+A')) edit_menu.addSeparator() wrap_action = QAction(QIcon(), "Wrap text to window", self) wrap_action.setStatusTip("Toggle wrap text to window") wrap_action.setCheckable(True) wrap_action.setChecked(True) wrap_action.triggered.connect(self.edit_toggle_wrap) edit_menu.addAction(wrap_action) self.update_title() self.show() def dialog_critical(self, s): dlg = QMessageBox(self) dlg.setText(s) dlg.setIcon(QMessageBox.Critical) dlg.show() def file_open(self): path, _ = QFileDialog.getOpenFileName(self, "Open file", "", "Text documents (*.txt)") if path: try: with open(path, 'rU') as f: text = f.read() except Exception as e: self.dialog_critical(str(e)) else: self.path = path self.editor.setPlainText(text) self.update_title() def file_save(self): if self.path is None: return self.file_saveas() self._save_to_path(self.path) def file_saveas(self): path, _ = QFileDialog.getSaveFileName(self, "Save file", "", "Text documents (*.txt)") if not path: return self._save_to_path(path) def _save_to_path(self, path): text = self.editor.toPlainText() try: with open(path, 'w') as f: f.write(text) except Exception as e: self.dialog_critical(str(e)) else: self.path = path self.update_title() def file_print(self): dlg = QPrintDialog() if dlg.exec_(): self.editor.print_(dlg.printer()) def update_title(self): self.setWindowTitle("%s - Notepad" % (os.path.basename(self.path) if self.path else "Untitled")) def edit_toggle_wrap(self): self.editor.setLineWrapMode(1 if self.editor.lineWrapMode() == 0 else 0)
class GridTransformerDialog(QDialog): def __init__(self, parent=None): super(GridTransformerDialog, self).__init__( parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self.setWindowTitle("Grid Transformer") self.setWindowIcon(qta.icon('fa.th')) # dict to store inputs that will get passed to the grid transformer # this gets validated to ensure all the bits of info are in it before # the run button gets enabled self.grid_transformer_inputs = {} self.layout = QVBoxLayout() self.setLayout(self.layout) self._add_inputs() self._add_output() self._add_process() close_layout = QHBoxLayout() close_layout.addStretch() button_close = QPushButton("Close") button_close.clicked.connect(self.close_dialog) close_layout.addWidget(button_close) self.layout.addLayout(close_layout) def _add_inputs(self): inputs_groupbox = QGroupBox("Inputs") inputs_groupbox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) inputs_layout = QVBoxLayout() inputs_layout.setSpacing(0) inputs_groupbox.setLayout(inputs_layout) for input_band_name in input_band_names: inputband = GridTransformerInputBand(input_band_name) inputs_layout.addWidget(inputband) inputband.band_selected.connect(self._band_selected) inputband.log_message.connect(self._log_message) self.layout.addWidget(inputs_groupbox) def _band_selected(self, band_details): band_name, filename, band_index = band_details self.grid_transformer_inputs[band_name] = (filename, band_index) self.validate() def validate(self): self.run_button.setEnabled(self._is_valid()) def _is_valid(self) -> bool: '''Is this ready to run, as in has the user specified all inputs needed for the grid transformer''' for band_name in input_band_names: if band_name not in self.grid_transformer_inputs: return False if self.grid_transformer_inputs[band_name] is None: return False if 'output' not in self.grid_transformer_inputs: return False return True def _log_message(self, message): self.log_messages.appendPlainText(message) def _add_output(self): output_groupbox = QGroupBox("Output") output_groupbox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) output_layout = QVBoxLayout() output_layout.setSpacing(0) output_groupbox.setLayout(output_layout) output_file_layout = QHBoxLayout() output_file_layout.setSpacing(4) self.output_file_input = QLineEdit() self.output_file_input.textChanged.connect( self._on_output_filename_changed) self.output_file_input.setMinimumWidth(400) self.output_file_input.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) output_file_layout.addWidget(self.output_file_input) output_layout.addLayout(output_file_layout) self.open_output_file_button = QPushButton() output_file_layout.addWidget(self.open_output_file_button) self.open_output_file_button.setIcon(qta.icon('fa.folder-open')) self.open_output_file_button.setToolTip("Select output file location") self.open_output_file_button.clicked.connect(self._click_open_output) self.layout.addWidget(output_groupbox) def _on_output_filename_changed(self, filename): self.grid_transformer_inputs['output'] = filename self.validate() def _set_output_filename(self, filename): self.output_file_input.setText(filename) self.grid_transformer_inputs['output'] = filename self.validate() def _click_open_output(self): filters = ("GeoTIFF (*.tif *.tiff)") filename, _ = QFileDialog.getSaveFileName( self, f"Select output file", GuiSettings.settings().value(output_folder_settings), filters) if filename is None: return last_open_folder = os.path.dirname(filename) if os.path.exists(last_open_folder): GuiSettings.settings().setValue(output_folder_settings, last_open_folder) self._set_output_filename(filename) def _add_process(self): process_groupbox = QGroupBox("Process") process_groupbox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) process_layout = QVBoxLayout() process_layout.setSpacing(0) process_groupbox.setLayout(process_layout) pbar_frame = QFrame() pbar_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) pbar_hbox = QHBoxLayout() pbar_hbox.setContentsMargins(QtCore.QMargins(0, 0, 0, 16)) pbar_hbox.setSpacing(16) # Run and stop buttons hbox = QHBoxLayout() hbox.setSpacing(8) self.run_button = QPushButton() # is only enabled when validation passes self.run_button.setEnabled(False) self.run_button.setText("Run") self.run_button.setFixedWidth(100) run_icon = qta.icon('fa.play', color='green') self.run_button.setIcon(run_icon) self.run_button.clicked.connect(self._click_run) hbox.addWidget(self.run_button) self.stop_button = QPushButton() self.stop_button.setEnabled(False) self.stop_button.setText("Stop") self.stop_button.setFixedWidth(100) stop_icon = qta.icon('fa.stop', color='red') self.stop_button.setIcon(stop_icon) self.stop_button.clicked.connect(self._click_stop) hbox.addWidget(self.stop_button) self.progress_bar = QProgressBar() self.progress_bar.setTextVisible(True) self.progress_bar.setAlignment(QtCore.Qt.AlignCenter) self.progress_bar.setValue(0) self.progress_bar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) pbar_hbox.addLayout(hbox) pbar_hbox.addWidget(self.progress_bar) pbar_frame.setLayout(pbar_hbox) process_layout.addWidget(pbar_frame) self.warning_frame = QFrame() self.warning_frame.setVisible(False) self.warning_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) hbox = QHBoxLayout() warning_icon_widget = qta.IconWidget('fa.warning', color='red') warning_icon_widget.setIconSize(QtCore.QSize(48, 48)) warning_icon_widget.update() hbox.addWidget(warning_icon_widget) warning_label = QLabel( "Grid Transformer did not complete successfully. Please refer to " "log output.") warning_label.setStyleSheet("QLabel { color: red; }") warning_label.setWordWrap(True) warning_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) hbox.addWidget(warning_label) self.warning_frame.setLayout(hbox) process_layout.addWidget(self.warning_frame) self.success_frame = QFrame() self.success_frame.setVisible(False) self.success_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) hbox = QHBoxLayout() success_icon_widget = qta.IconWidget('fa.check', color='green') success_icon_widget.setIconSize(QtCore.QSize(48, 48)) success_icon_widget.update() hbox.addWidget(success_icon_widget) success_label = QLabel("Grid Transformer completed successfully.") success_label.setStyleSheet("QLabel { color: green; }") success_label.setWordWrap(True) success_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) hbox.addWidget(success_label) self.success_frame.setLayout(hbox) process_layout.addWidget(self.success_frame) log_layout = QVBoxLayout() log_layout.setSpacing(4) log_label = QLabel("Log messages") log_label.setStyleSheet("QLabel { color: grey; }") log_layout.addWidget(log_label) self.log_messages = QPlainTextEdit() log_font = QFont("monospace") log_font.setStyleHint(QFont.TypeWriter) self.log_messages.setFont(log_font) self.log_messages.setReadOnly(True) self.log_messages.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # self.log_messages.sizePolicy.setVerticalStretch(1) log_layout.addWidget(self.log_messages) process_layout.addLayout(log_layout) self.layout.addWidget(process_groupbox) def _on_progress(self, progress): self.progress_bar.setValue(int(progress * 100)) def _on_complete(self, successful: bool): self.warning_frame.setVisible(not successful) self.success_frame.setVisible(successful) self.stop_button.setEnabled(False) self.run_button.setEnabled(True) run_time = time.perf_counter() - self.start_time self._log_message( f"Total grid transformation time = {run_time:.2f} sec") self._log_message("\n\n") def _click_run(self): self.warning_frame.setVisible(False) self.success_frame.setVisible(False) self.stop_button.setEnabled(True) self.run_button.setEnabled(False) self.gt_executor = QtGridTransformerThread( self.grid_transformer_inputs) self.gt_executor.progress.connect(self._on_progress) self.gt_executor.message.connect(self._log_message) self.gt_executor.complete.connect(self._on_complete) self.start_time = time.perf_counter() self._log_message("\n\nStarting Grid Transformer process") self.gt_executor.start() def _click_stop(self): if self.gt_executor is not None: self.gt_executor.stop() def close_dialog(self): self.close()
class MainWindow(QMainWindow): font: QFont vertical_layout: QVBoxLayout central_widget: QWidget text_box: QPlainTextEdit recognize_button: QPushButton assistant: AssistantInterface def __init__(self, assistant: AssistantInterface): super().__init__() qInitResources() self.assistant = assistant self.build_layout() self.init_handlers() def build_layout(self): self.setup_window() self.setup_styles() self.setup_font() self.build_central_widget() self.build_vertical_layout() self.build_text_box() self.build_recognize_button() QMetaObject.connectSlotsByName(self) def setup_window(self): window_size = QSize(420, 700) self.setObjectName("window") self.resize(window_size) self.setMinimumSize(window_size) self.setMaximumSize(window_size) self.setWindowTitle(u"Voice Assistant") self.setAutoFillBackground(False) def setup_styles(self): file = QFile(":/styles/style.css") file.open(QFile.ReadOnly) byte_array = file.readAll() file.close() self.setStyleSheet(byte_array.data().decode()) def setup_font(self): self.font = QFont() self.font.setFamily(u"Helvetica") self.font.setPointSize(18) def build_central_widget(self): size_policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) size_policy.setHorizontalStretch(0) size_policy.setVerticalStretch(0) self.central_widget = QWidget(self) self.central_widget.setObjectName(u"central_widget") self.central_widget.setSizePolicy(size_policy) self.setCentralWidget(self.central_widget) def build_vertical_layout(self): self.vertical_layout = QVBoxLayout(self.central_widget) self.vertical_layout.setObjectName(u"vertical_layout") def build_text_box(self): text_box_size = QSize(420, 600) self.text_box = QPlainTextEdit(self.central_widget) self.text_box.setObjectName(u"text_box") self.text_box.setMaximumSize(text_box_size) self.text_box.setFont(self.font) self.text_box.setContextMenuPolicy(Qt.NoContextMenu) self.text_box.setUndoRedoEnabled(False) self.text_box.setReadOnly(True) self.text_box.setPlaceholderText("Waiting for your question...") self.vertical_layout.addWidget(self.text_box) def build_recognize_button(self): button_size = QSize(140, 40) self.recognize_button = QPushButton(self.central_widget) self.recognize_button.setObjectName(u"recognize_button") self.recognize_button.setEnabled(True) self.recognize_button.setMinimumSize(button_size) self.recognize_button.setMaximumSize(button_size) self.recognize_button.setFont(self.font) self.recognize_button.setAutoFillBackground(False) self.recognize_button.setText("Recognize") self.recognize_button.setShortcut("Return") self.vertical_layout.addWidget(self.recognize_button, 0, Qt.AlignHCenter) def init_handlers(self): self.recognize_button.pressed.connect(self.start_recognition) self.assistant.on_wake = self.on_assistant_started self.assistant.on_sleep = self.on_assistant_finished self.assistant.on_assistant_listen = self.on_recognize_started self.assistant.on_user_message = self.on_user_text self.assistant.on_assistant_message = self.on_assistant_text def on_assistant_started(self): self.recognize_button.setEnabled(False) def on_assistant_finished(self): self.recognize_button.setEnabled(True) self.recognize_button.setText("Recognize") def on_recognize_started(self): self.async_play_sound() self.recognize_button.setText("Listening...") def on_user_text(self, user_text: str): self.recognize_button.setText("Processing...") self.append_message(f"[You] {user_text}") def on_assistant_text(self, assistant_answer: str): signed_text = f"[{self.assistant.get_name()}] {assistant_answer}" self.append_message(signed_text) def append_message(self, message: str): self.text_box.appendPlainText(f"{message}\n") def start_recognition(self): coroutine = self.assistant.handle() asyncio.create_task(coroutine) @async_function def async_play_sound(self): file = QFile(":/sounds/click.mp3") file.open(QFile.ReadOnly) byte_array = file.readAll() file.close() with tempfile.NamedTemporaryFile("wb", delete=True) as temp: temp.write(byte_array.data()) playsound(temp.name) def closeEvent(self, event: QCloseEvent): for task in asyncio.all_tasks(): task.cancel() asyncio.get_running_loop().stop() async def start(self): self.show()
class TrackEditor(Observation, QWidget, metaclass=FinalMeta): def __init__(self, subject, powermode=False, allowEditBackend=False, confirmUpdate=True): Observation.__init__(self, subject) QWidget.__init__(self) self._template = None self.deleted = False self.shouldSave = False self.powermode = powermode self.allowEditBackend = allowEditBackend self.confirmUpdate = confirmUpdate self.descriptionMaxLen = 1000 self._code = "" layout = QVBoxLayout() layout.setAlignment(Qt.AlignTop) layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) if self.allowEditBackend: self._initForm() self._initCodeEditor() self.setFixedSize(630, 600) self.setAttribute(Qt.WA_StyledBackground) self.setStyleSheet(Theme.templateEditor.style) self.setContextMenuPolicy(Qt.NoContextMenu) self.setWindowModality(Qt.ApplicationModal) self.setWindowFlags(Qt.Tool | Qt.WindowTitleHint | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint | Qt.WindowMaximizeButtonHint) self.setWindowFlag(Qt.WindowMinimizeButtonHint, False) self.codeView.closeShortcut.setEnabled(False) QShortcut(QKeySequence(self.tr("Ctrl+w")), self, self.close) def _initForm(self): layout = QFormLayout() self.runCommand = QLineEdit(self, maxLength=200) self.runCommand.setStyleSheet(Theme.templateEditor.inputStyle) self.runCommand.setFont(Theme.templateEditor.inputCodeFont) self.runCommand.setFixedHeight(Theme.templateEditor.inputHeight) if self.powermode: self.backendName = QLineEdit(self, maxLength=20) self.backendName.setStyleSheet(Theme.templateEditor.inputStyle) self.backendName.setFont(Theme.templateEditor.inputFont) self.backendName.setFixedHeight(Theme.templateEditor.inputHeight) self.editorMode = QComboBox() [self.editorMode.addItem(mode) for mode in self._availableModes()] self.editorMode.currentIndexChanged.connect( self.onEditorModeChanged) self.inputRegex = QLineEdit(self, maxLength=100) self.inputRegex.setToolTip("regex") self.inputRegex.setStyleSheet(Theme.templateEditor.inputStyle) self.inputRegex.setFont(Theme.templateEditor.inputCodeFont) self.inputRegex.setFixedHeight(Theme.templateEditor.inputHeight) self.inputReplace = QLineEdit(self, maxLength=100) self.inputReplace.setToolTip("substitution string") self.inputReplace.setStyleSheet(Theme.templateEditor.inputStyle) self.inputReplace.setFont(Theme.templateEditor.inputCodeFont) self.inputReplace.setFixedHeight(Theme.templateEditor.inputHeight) self.outputRegex = QLineEdit(self, maxLength=100) self.outputRegex.setToolTip("regex") self.outputRegex.setStyleSheet(Theme.templateEditor.inputStyle) self.outputRegex.setFont(Theme.templateEditor.inputCodeFont) self.outputRegex.setFixedHeight(Theme.templateEditor.inputHeight) self.outputReplace = QLineEdit(self, maxLength=100) self.outputReplace.setToolTip("substitution string") self.outputReplace.setStyleSheet(Theme.templateEditor.inputStyle) self.outputReplace.setFont(Theme.templateEditor.inputCodeFont) self.outputReplace.setFixedHeight(Theme.templateEditor.inputHeight) self.description = QPlainTextEdit(self, minimumHeight=80) self.description.setStyleSheet( Theme.templateEditor.descriptionStyle) self.description.setFont(Theme.templateEditor.descriptionFont) layout.addRow(self.tr("Run Command:"), self.runCommand) if self.powermode: layout.addRow(self.tr("Backend Name:"), self.backendName) layout.addRow(self.tr("Editor Mode:"), self.editorMode) inputMiddlewareLayout = QHBoxLayout() inputMiddlewareLayout.addWidget(self.inputRegex) inputMiddlewareLayout.addWidget(self.inputReplace) layout.addRow(self.tr("Input Middleware:"), inputMiddlewareLayout) outputMiddlewareLayout = QHBoxLayout() outputMiddlewareLayout.addWidget(self.outputRegex) outputMiddlewareLayout.addWidget(self.outputReplace) layout.addRow(self.tr("Output Middleware:"), outputMiddlewareLayout) layout.addRow(self.tr("Description:"), self.description) layout.setSpacing(10) layout.setContentsMargins(10, 10, 10, 10) self.layout().addLayout(layout) def _initCodeEditor(self): self.codeView = CodeView(self.subject, viewTip=False) self.layout().addWidget(QLabel("Setup Code:", margin=10)) self.layout().addWidget(self.codeView) self.codeView.setDelegate(self) def _availableModes(self): return acePropertyNames("mode-", ".js", False) def onEditorModeChanged(self, e): mode = self.editorMode.itemText(e) self.codeView.setMode(mode) if self._template is not None: self._template.editor_mode = mode def onSave(self): self.shouldSave = True if self.allowEditBackend: self._template.run_command = self.runCommand.text().strip() if self.powermode and self.allowEditBackend: self._template.backend_name = self.backendName.text().strip() self._template.editor_mode = self.editorMode.currentText() self._template.backend_middleware.input.regex = \ self.inputRegex.text() self._template.backend_middleware.input.substitution = \ self.inputReplace.text() self._template.backend_middleware.output.regex = \ self.outputRegex.text() self._template.backend_middleware.output.substitution = \ self.outputReplace.text() self._template.description = self.description.toPlainText( )[:self.descriptionMaxLen] self._template.setup_code = self._code def setTemplate(self, delegate): self._template = delegate self.codeView.setDelegate(self) self.deserialize() def setCode(self, code, notify): if self._template is None: return self._code = code if self.shouldSave: self._template.setup_code = code self.onTemplateUpdate() def onTemplateUpdate(self): pass def code(self): if self._template is None: return "" return self._template.setup_code def codeWindowTitle(self): return "Track Template Editor" def deserialize(self): if self._template is None: return if self.allowEditBackend: self.runCommand.setText(self._template.run_command.strip()) if self.powermode and self.allowEditBackend: self.backendName.setText(self._template.backend_name.strip()) self.editorMode.setCurrentText(self._template.editor_mode) self.inputRegex.setText( self._template.backend_middleware.input.regex) self.inputReplace.setText( self._template.backend_middleware.input.substitution) self.outputRegex.setText( self._template.backend_middleware.output.regex) self.outputReplace.setText( self._template.backend_middleware.output.substitution) self.description.document().setPlainText( self._template.description) else: self.codeView.setMode(self._template.editor_mode) self._code = self._template.setup_code self.setWindowTitle("Track Template Editor") def delete(self): self.deleted = True self.codeView.delete() self.unregister() self.setParent(None) self.deleteLater() def template(self): return self._template def showEvent(self, event): self.shouldSave = False self.codeView.show() super().showEvent(event) def closeEvent(self, event): if self._template is not None and not self.deleted: if self.confirmUpdate: question = "Do you want to save changes in " +\ f"{self._template.backend_name} template?" confirmation = ConfirmationDialog("Update Track Template", question) if confirmation.exec_() == ConfirmationDialog.Yes: self.onSave() else: self.onSave() self.codeView.close() super().closeEvent(event)
class RunTab(QtWidgets.QWidget): run_checks = QtCore.Signal() def __init__(self, prj: QAXProject): super(RunTab, self).__init__() self.prj = prj self.check_executor = None self.vbox = QtWidgets.QVBoxLayout() self.setLayout(self.vbox) self._add_check_outputs() self._add_process() # final setup self.set_run_stop_buttons_enabled(False) def _add_check_outputs(self): co_groupbox = QGroupBox("Check outputs") co_groupbox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) co_layout = QVBoxLayout() co_layout.setSpacing(16) co_groupbox.setLayout(co_layout) self.qajson_spatial_checkbox = QCheckBox( "Include summary spatial output in QAJSON. " "Supports QAX visualisation.") self.qajson_spatial_checkbox.setCheckState( QtCore.Qt.CheckState.Checked) co_layout.addWidget(self.qajson_spatial_checkbox) export_layout = QVBoxLayout() export_layout.setSpacing(4) self.export_spatial_checkbox = QCheckBox( "Export detailed spatial outputs to file. " "Supports visualisation in other geospatial applications.") self.export_spatial_checkbox.stateChanged.connect( self._on_export_spatial_changed) export_layout.addWidget(self.export_spatial_checkbox) output_folder_layout = QHBoxLayout() output_folder_layout.setSpacing(4) output_folder_layout.addSpacerItem(QtWidgets.QSpacerItem(37, 20)) self.output_folder_label = QLabel( "Detailed spatial output folder location:") output_folder_layout.addWidget(self.output_folder_label) self.output_folder_input = QLineEdit() self.output_folder_input.setText( GuiSettings.settings().value("spatial_outputs")) self.output_folder_input.setMinimumWidth(300) self.output_folder_input.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) output_folder_layout.addWidget(self.output_folder_input) self.open_output_folder_button = QPushButton() output_folder_layout.addWidget(self.open_output_folder_button) self.open_output_folder_button.setIcon(qta.icon('fa.folder-open')) self.open_output_folder_button.setToolTip( f"Select file containing data") self.open_output_folder_button.clicked.connect( self._click_open_spatial_export_folder) export_layout.addLayout(output_folder_layout) co_layout.addLayout(export_layout) self._on_export_spatial_changed() self.vbox.addWidget(co_groupbox) def _click_open_spatial_export_folder(self): output_folder = QFileDialog.getExistingDirectory( self, f"Select folder for spatial outputs", GuiSettings.settings().value("spatial_outputs"), QFileDialog.ShowDirsOnly) if os.path.exists(output_folder): GuiSettings.settings().setValue("spatial_outputs", output_folder) self.output_folder_input.setText(output_folder) def _on_export_spatial_changed(self): is_export = self.export_spatial_checkbox.isChecked() self.output_folder_label.setEnabled(is_export) self.output_folder_input.setEnabled(is_export) self.open_output_folder_button.setEnabled(is_export) def _add_process(self): process_groupbox = QGroupBox("Process") process_groupbox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) process_layout = QVBoxLayout() process_layout.setSpacing(0) process_groupbox.setLayout(process_layout) pbar_frame = QFrame() pbar_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) pbar_hbox = QHBoxLayout() pbar_hbox.setContentsMargins(QtCore.QMargins(0, 0, 0, 16)) pbar_hbox.setSpacing(16) # Run and stop buttons hbox = QHBoxLayout() hbox.setSpacing(8) self.run_button = QPushButton() # is only enabled when validation passes self.run_button.setEnabled(False) self.run_button.setText("Run") self.run_button.setToolTip("Start check execution") self.run_button.setFixedWidth(100) run_icon = qta.icon('fa.play', color='green') self.run_button.setIcon(run_icon) self.run_button.clicked.connect(self._click_run) hbox.addWidget(self.run_button) self.stop_button = QPushButton() self.stop_button.setEnabled(False) self.stop_button.setText("Stop") self.stop_button.setToolTip("Stop check execution") self.stop_button.setFixedWidth(100) stop_icon = qta.icon('fa.stop', color='red') self.stop_button.setIcon(stop_icon) self.stop_button.clicked.connect(self._click_stop) hbox.addWidget(self.stop_button) self.progress_bar = QProgressBar() self.progress_bar.setTextVisible(True) self.progress_bar.setAlignment(QtCore.Qt.AlignCenter) self.progress_bar.setValue(0) self.progress_bar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) pbar_hbox.addLayout(hbox) pbar_hbox.addWidget(self.progress_bar) pbar_frame.setLayout(pbar_hbox) process_layout.addWidget(pbar_frame) vbox = QVBoxLayout() vbox.setSpacing(8) vbox.setContentsMargins(QtCore.QMargins(0, 0, 0, 16)) process_layout.addLayout(vbox) hbox = QHBoxLayout() vbox.addLayout(hbox) check_name_label = QLabel("Check:") check_name_label.setFixedWidth(80) hbox.addWidget(check_name_label) self.check_name_text_label = QLabel("n/a") hbox.addWidget(self.check_name_text_label) hbox = QHBoxLayout() vbox.addLayout(hbox) status_name_label = QLabel("Status:") status_name_label.setFixedWidth(80) hbox.addWidget(status_name_label) self.status_name_text_label = QLabel("Not started") hbox.addWidget(self.status_name_text_label) self.warning_frame = QFrame() self.warning_frame.setVisible(False) self.warning_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) hbox = QHBoxLayout() warning_icon_widget = qta.IconWidget('fa.warning', color='red') warning_icon_widget.setIconSize(QtCore.QSize(48, 48)) warning_icon_widget.update() hbox.addWidget(warning_icon_widget) warning_label = QLabel( "Grid Transformer did not complete successfully. Please refer to " "log output.") warning_label.setStyleSheet("QLabel { color: red; }") warning_label.setWordWrap(True) warning_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) hbox.addWidget(warning_label) self.warning_frame.setLayout(hbox) process_layout.addWidget(self.warning_frame) self.success_frame = QFrame() self.success_frame.setVisible(False) self.success_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) hbox = QHBoxLayout() success_icon_widget = qta.IconWidget('fa.check', color='green') success_icon_widget.setIconSize(QtCore.QSize(48, 48)) success_icon_widget.update() hbox.addWidget(success_icon_widget) success_label = QLabel("All checks completed successfully.") success_label.setStyleSheet("QLabel { color: green; }") success_label.setWordWrap(True) success_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) hbox.addWidget(success_label) self.success_frame.setLayout(hbox) process_layout.addWidget(self.success_frame) log_layout = QVBoxLayout() log_layout.setSpacing(4) log_label = QLabel("Log messages") log_label.setStyleSheet("QLabel { color: grey; }") log_layout.addWidget(log_label) self.log_messages = QPlainTextEdit() log_font = QFont("monospace") log_font.setStyleHint(QFont.TypeWriter) self.log_messages.setFont(log_font) self.log_messages.setReadOnly(True) self.log_messages.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) log_layout.addWidget(self.log_messages) process_layout.addLayout(log_layout) self.vbox.addWidget(process_groupbox) def set_run_stop_buttons_enabled(self, is_running: bool) -> NoReturn: if is_running: self.run_button.setEnabled(False) self.stop_button.setEnabled(True) else: self.run_button.setEnabled(True) self.stop_button.setEnabled(False) def _log_message(self, message): self.log_messages.appendPlainText(message) def run_executor(self, check_executor: QtCheckExecutorThread): # we pass the check_executor into the run tab as this is where the UI # components are that will display the execution status. self.set_run_stop_buttons_enabled(True) self._log_message("Check execution started") self.start_time = time.perf_counter() self.check_executor = check_executor self.check_executor.options = self.get_options() self.check_executor.check_tool_started.connect( self._on_check_tool_started) self.check_executor.progress.connect(self._on_progress) self.check_executor.qajson_updated.connect(self._on_qajson_update) self.check_executor.checks_complete.connect(self._on_checks_complete) self.check_executor.status_changed.connect(self._on_status_change) self.check_executor.start() def get_options(self) -> Dict: ''' Gets a list of options based on user entered data. eg; the spatial output specifications. ''' return { CheckOption.spatial_output_qajson: self.qajson_spatial_checkbox.isChecked(), CheckOption.spatial_output_export: self.export_spatial_checkbox.isChecked(), CheckOption.spatial_output_export_location: self.output_folder_input.text() } def _click_run(self): self.run_checks.emit() def _click_stop(self): if self.check_executor is None: logger.warn("Check executor does not exist, cannot stop") return self._log_message("Stopping check execution") self.check_executor.stop() @QtCore.Slot(float) def _on_progress(self, progress): self.progress_bar.setValue(int(progress * 100)) @QtCore.Slot() def _on_qajson_update(self): self.prj.qa_json = self.check_executor.qa_json @QtCore.Slot(object) def _on_check_tool_started(self, tpl): check_tool_name, check_number, total_check_count = tpl self.check_name_text_label.setText("{} ({}/{})".format( check_tool_name, check_number, total_check_count)) @QtCore.Slot() def _on_checks_complete(self): run_time = time.perf_counter() - self.start_time self._log_message( f"Execution time for all checks = {run_time:.2f} sec") self._log_message("\n\n") self.set_run_stop_buttons_enabled(False) self.prj.qa_json = self.check_executor.qa_json @QtCore.Slot(str) def _on_status_change(self, status): self.status_name_text_label.setText(status)
class MyWidget(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.thread = SerialMonitorThread() self.thread.dataReady.connect(self.get_data, Qt.QueuedConnection) self.thread.setTerminationEnabled(True) #Menu self.setPalette(get_verifone_color()) collapsible = CollapsibleWidget() self.init_logging(collapsible) self.init_download(collapsible) self.init_analyser(collapsible) collapsible.add_sections() # Scroll Area self.createLoggingDisplayLabel() self.scrollArea = QScrollArea() self.scrollArea.setBackgroundRole(QPalette.Dark) self.scrollArea.setWidget(self.text) self.scrollArea.setWidgetResizable(True) hLayout = QHBoxLayout() #hLayout.addLayout(vLayout) hLayout.addWidget(collapsible) hLayout.addWidget(self.scrollArea) self.setLayout(hLayout) def init_logging(self, collapsible): self.logger = QPushButton("Start Logging", self) self.logger.setFont(QFont("Times", 14, QFont.Bold)) self.logger.clicked.connect(lambda: self.display_log_data()) self.logger.setStyleSheet("background-color: white") #self.filterLayout = QtWidgets.QHBoxLayout() self.logFilterLabel = QLabel('Filter', self) self.logFilterLabel.setFont(QFont("Times", 14, QFont.Bold)) self.logFilterLabel.setPalette(get_white_color_text()) self.logFilterLabel.setFixedWidth(60) self.logFilter = QLineEdit(self) self.logFilter.setPalette(get_white_color()) self.logFilter.setStyleSheet("background-color: white") self.logFilter.setFixedWidth(200) #self.filterLayout.addWidget(self.logFilterLabel, QtCore.Qt.AlignLeft) #self.filterLayout.addWidget(self.logFilter, QtCore.Qt.AlignLeft) self.serialList = QComboBox() ports = get_available_serial_ports() if (len(ports) == 1): self.serialList.addItem(ports[0]) self.thread.set_comport(self.serialList.currentText()) else: self.serialList.addItem("Select") for port in ports: self.serialList.addItem(port) self.serialList.currentIndexChanged.connect( lambda: self.set_serial_port()) self.serialList.setStyleSheet("background-color: white") self.clear = QPushButton("Clear Log File", self) self.clear.setStyleSheet("background-color: white") self.clear.setFont(QFont("Times", 14, QFont.Bold)) self.clear.clicked.connect(lambda: self.clear_data()) widget = QFrame(collapsible.get_tree()) widget.setPalette(get_verifone_color()) title = "Logging" self.loggerGrid = QGridLayout(widget) self.loggerGrid.addWidget(self.logger, 0, 0, 1, 2) self.loggerGrid.addWidget(self.logFilterLabel, 1, 0, 1, 1) self.loggerGrid.addWidget(self.logFilter, 1, 1, 1, 1) self.loggerGrid.addWidget(self.serialList, 2, 0, 1, 2) self.loggerGrid.addWidget(self.clear, 3, 0, 1, 2) collapsible.include_section(title, widget) def init_download(self, collapsible): self.download = QPushButton("Download Package", self) self.download.setFont(QFont("Times", 14, QFont.Bold)) self.download.clicked.connect(lambda: self.send_file()) self.download.setStyleSheet("background-color: white") self.loadDownloadFile = QPushButton("Load File", self) self.loadDownloadFile.setFont(QFont("Times", 14, QFont.Bold)) self.loadDownloadFile.clicked.connect(self.loadFromFile) self.loadDownloadFile.setStyleSheet("background-color: white") self.downloadFileName = QLineEdit("File name", self) self.downloadFileName.setStyleSheet("background-color: white") self.downloadFileName.setFixedWidth(300) self.downloadAddress = QLineEdit("IP Address", self) self.downloadAddress.setStyleSheet("background-color: white") self.downloadAddress.setFixedWidth(300) self.downloadStatus = QLabel("Download Status", self) self.downloadStatus.setStyleSheet( "background-color: rgba(3, 169, 229, 0); color : white") self.downloadStatus.setFixedWidth(300) widget = QFrame(collapsible.get_tree()) title = "Download" self.downloadGrid = QGridLayout(widget) self.downloadGrid.addWidget(self.download, 0, 0, 1, 2) self.downloadGrid.addWidget(self.loadDownloadFile, 1, 0, 1, 2) self.downloadGrid.addWidget(self.downloadFileName, 2, 0, 1, 2) self.downloadGrid.addWidget(self.downloadAddress, 3, 0, 1, 2) self.downloadGrid.addWidget(self.downloadStatus, 4, 0, 1, 2) collapsible.include_section(title, widget) def init_analyser(self, collapsible): self.performanceData = QPushButton("View Performance Data", self) self.performanceData.setFont(QFont("Times", 14, QFont.Bold)) self.performanceData.clicked.connect( lambda: self.display_performance_data()) self.performanceData.setStyleSheet("background-color: white") self.performanceChart = QPushButton("View Performance Chart", self) self.performanceChart.setFont(QFont("Times", 14, QFont.Bold)) self.performanceChart.clicked.connect( lambda: self.display_performance_chart()) self.performanceChart.setStyleSheet("background-color: white") widget = QFrame(collapsible.get_tree()) title = "Analyser" self.analyserGrid = QGridLayout(widget) self.analyserGrid.addWidget(self.performanceData, 0, 0, 1, 2) self.analyserGrid.addWidget(self.performanceChart, 1, 0, 1, 2) collapsible.include_section(title, widget) def loadFromFile(self): fileName, _ = QFileDialog.getOpenFileName( self, "Load Package", '', "Download Files (*.tgz);;All Files (*)") if not fileName: return try: in_file = open(str(fileName), 'rb') except IOError: QMessageBox.information( self, "Unable to open file", "There was an error opening \"%s\"" % fileName) return in_file.close() self.downloadFileName.setText(fileName) def createLoggingDisplayLabel(self): # Display Area self.text = QPlainTextEdit(self) self.text.setReadOnly(True) self.text.setFont(QFont("Times", 12, QFont.Bold)) self.text.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard) def clear_data(self): self.text.clear() os.remove(logfile_path, dir_fd=None) def display_log_data(self): #send_file() if ('COM' in self.serialList.currentText()): self.createLoggingDisplayLabel() self.scrollArea.setWidget(self.text) self.thread.stop() self.thread.start() data = get_data_from_file(logfile_path) if (len(data) > 0 and data != None): self.text.appendPlainText(data) self.logger.setDisabled(True) def get_data(self, data): if (len(data) > 0): logFile = open(logfile_path, "a") logFile.write(data) logFile.close() filterText = self.logFilter.text() if filterText in data.rstrip(): self.text.appendPlainText(data.rstrip()) vbar = self.scrollArea.verticalScrollBar() vbar.setValue(vbar.maximum()) def display_performance_data(self): self.thread.stop() data = get_data_from_file(logfile_path) jsonData = translate_data_to_json(data) self.performanceData = DisplayPerformanceData() self.performanceData.loadCsv( os.path.join(base_log_path, "performance_data.csv")) self.scrollArea.setWidget(self.performanceData) self.logger.setDisabled(False) def display_performance_chart(self): self.thread.stop() self.scrollArea.setWidget(get_performace_chart()) self.logger.setDisabled(False) def set_serial_port(self): self.thread.set_comport(self.serialList.currentText()) def send_file(self): base_path = os.path.join("c:/", "VFI", 'wks', 'global-payment-application', 'GPA', 'output', 'vos2', 'gpa', 'dl.gpa-1.0.0.0-000.tgz') fileName = self.downloadFileName.text() try: in_file = open(str(fileName), 'rb') except IOError: QMessageBox.information( self, "Unable to open file", "There was an error opening \"%s\"" % fileName) return in_file.close() load_vos_package_ip('192.168.0.104', fileName, self.downloadStatus)
class QFileBrowse(QWidget): def __init__(self, subfolder_name='QR Codes', *args, **kwargs): super().__init__(*args, **kwargs) self.layout = QGridLayout() self.button = QPushButton("Change save location") self.subfolder = QCheckBox("Create subfolder") self.subfolder.stateChanged.connect(self.update_display) self.button.clicked.connect(self.browse) self.subfolder_name = subfolder_name self.dialog = QFileDialog() self._base_path = pathlib.Path(".").expanduser().resolve() # output path display self.path_display = QPlainTextEdit() # make bold font = self.path_display.font() font.setWeight(QFont.Bold) self.path_display.setFont(font) # configure height self.path_display.setFixedHeight(QFontMetrics(font).height() * 2) self.path_display.setLineWrapMode(QPlainTextEdit.NoWrap) # styles self.path_display.setReadOnly(True) self.path_display.setFrameStyle(QFrame.Box | QFrame.Sunken) self.path_display.setTextInteractionFlags(Qt.TextSelectableByMouse) self.path_display.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) self.path_display.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.path_display.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # add widgets to layout self.layout.addWidget(self.button, 0, 0) self.layout.addWidget(self.subfolder, 0, 1) self.layout.addWidget(self.path_display, 1, 0, 1, 2) self.layout.setMargin(0) self.setLayout(self.layout) self.update_display() @property def save_path(self): if self.subfolder.isChecked(): return self._base_path / self.subfolder_name return self._base_path def browse(self): path = self.dialog.getExistingDirectory() if path != '': self._base_path = pathlib.Path(path).expanduser().resolve() self.update_display() def update_display(self): scrollbar = self.path_display.horizontalScrollBar() old_scroll = scrollbar.value() scrolled_to_end = old_scroll == scrollbar.maximum() self.path_display.setPlainText(str(self.save_path)) if scrolled_to_end: scrollbar.setValue(scrollbar.maximum()) else: scrollbar.setValue(old_scroll)
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 BibTeXWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self._source = None self._table = None self._id = -1 self._inspire_id = None self._doi = None self._bibtex_string = "" self._fetcher = Fetcher() self._fetcher.BatchReady.connect(self._HandleBatchReady) self._fetcher.FetchingFinished.connect(self._HandleFetchingCompleted) self._text_edit = QPlainTextEdit() font = QFont("Monospace") self._text_edit.setFont(font) self._text_edit.setTextInteractionFlags( Qt.TextInteractionFlag.TextSelectableByKeyboard | Qt.TextInteractionFlag.TextSelectableByMouse) self._text_edit.setEnabled(False) self._dowload_inspire = QToolButton() self._dowload_inspire.setIcon(QIcon(icons.INSPIRE)) self._dowload_inspire.clicked.connect(self._FetchINSPIRE) self._dowload_inspire.setEnabled(False) self._dowload_doi = QToolButton() self._dowload_doi.setIcon(QIcon(icons.DOI)) self._dowload_doi.clicked.connect(self._FetchDOI) self._dowload_doi.setEnabled(False) self._SetupUI() def _SetupUI(self): tool_widget = QWidget() tool_layout = QHBoxLayout() tool_layout.setContentsMargins(0, 0, 0, 0) tool_layout.addWidget(self._dowload_inspire) tool_layout.addWidget(self._dowload_doi) tool_layout.addStretch(1) tool_widget.setLayout(tool_layout) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._text_edit) layout.addWidget(tool_widget) self.setLayout(layout) def SetTable(self, database_table): if self._table == database_table: return if self._table is not None: self._table.Cleared.disconnect(self.Clear) self._table = database_table self._table.Cleared.connect(self.Clear) def Clear(self): self._text_edit.clear() self._text_edit.setEnabled(False) self._dowload_inspire.setEnabled(False) self._dowload_doi.setEnabled(False) def DisplayItem(self, id_): # NOTE: What if the item gets updated? if self._id == id_: return self._id = id_ self._fetcher.Stop() self.Clear() if self._id == -1: return record = self._table.GetRow(self._id, ("inspire_id", "dois")) self._inspire_id = record["inspire_id"] self._doi = None if record["dois"] == [] else record["dois"][0] has_inspire_id = self._inspire_id is not None has_doi = self._doi is not None self._text_edit.setEnabled(has_inspire_id | has_doi) self._dowload_inspire.setEnabled(has_inspire_id) self._dowload_doi.setEnabled(has_doi) def _FetchINSPIRE(self): self._fetcher.Fetch(InspireBibTeXPlugin, "recid:" + str(self._inspire_id), batch_size=2) def _FetchDOI(self): self._fetcher.Fetch(DOIBibTeXPlugin, self._doi) def _HandleBatchReady(self, batch): self._bibtex_string = batch[0] def _HandleFetchingCompleted(self, records_number): self._text_edit.setPlainText(self._bibtex_string)