def __init__(self, tracker: Tracker) -> None: super().__init__() self.qttracker = QtTracker(tracker) self.bg_thread = QThread(self) self.qttracker.moveToThread(self.bg_thread) self.bg_thread.start() self.qttracker.start_recording() self.setCentralWidget(Controls(self.qttracker)) toolbar = QToolBar() self.addToolBar(toolbar) toolbar.setMovable(False) toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.new_action = QAction("New", self) self.new_action.setToolTip("Create a new file for tracking.") icon = self.style().standardIcon(QStyle.SP_FileIcon) self.new_action.setIcon(icon) self.new_action.triggered.connect(self.new_db) toolbar.addAction(self.new_action) self.load_action = QAction("Load", self) self.load_action.setToolTip("Load a existing file for tracking.") icon = self.style().standardIcon(QStyle.SP_DialogOpenButton) self.load_action.setIcon(icon) self.load_action.triggered.connect(self.load_db) toolbar.addAction(self.load_action)
class WorkflowWidget(QWidget): sigAddFunction = Signal(object) def __init__(self, workflowview: QAbstractItemView): super(WorkflowWidget, self).__init__() self.view = workflowview self.toolbar = QToolBar() self.addfunctionmenu = QToolButton() self.addfunctionmenu.setIcon(QIcon(path("icons/addfunction.png"))) self.addfunctionmenu.setText("Add Function") # Defer menu population to once the plugins have been loaded; otherwise, the menu may not contain anything # if this widget is init'd before all plugins have been loaded. self.functionmenu = QMenu() self.functionmenu.aboutToShow.connect(self.populateFunctionMenu) self.addfunctionmenu.setMenu(self.functionmenu) self.addfunctionmenu.setPopupMode(QToolButton.InstantPopup) self.toolbar.addWidget(self.addfunctionmenu) # self.toolbar.addAction(QIcon(path('icons/up.png')), 'Move Up') # self.toolbar.addAction(QIcon(path('icons/down.png')), 'Move Down') self.toolbar.addAction(QIcon(path("icons/folder.png")), "Load Workflow") self.toolbar.addAction(QIcon(path("icons/trash.png")), "Delete Operation", self.deleteOperation) v = QVBoxLayout() v.addWidget(self.view) v.addWidget(self.toolbar) v.setContentsMargins(0, 0, 0, 0) self.setLayout(v) def populateFunctionMenu(self): self.functionmenu.clear() sortingDict = {} for plugin in pluginmanager.get_plugins_of_type("OperationPlugin"): typeOfOperationPlugin = plugin.getCategory() if not typeOfOperationPlugin in sortingDict.keys(): sortingDict[typeOfOperationPlugin] = [] sortingDict[typeOfOperationPlugin].append(plugin) for key in sortingDict.keys(): self.functionmenu.addSeparator() self.functionmenu.addAction(key) self.functionmenu.addSeparator() for plugin in sortingDict[key]: self.functionmenu.addAction( plugin.name, partial(self.addOperation, plugin, autoconnectall=True)) def addOperation(self, operation: OperationPlugin, autoconnectall=True): self.view.model().workflow.addOperation(operation(), autoconnectall) print("selected new row:", self.view.model().rowCount() - 1) self.view.setCurrentIndex(self.view.model().index( self.view.model().rowCount() - 1, 0)) def deleteOperation(self): for index in self.view.selectedIndexes(): operation = self.view.model().workflow.operations[index.row()] self.view.model().workflow.remove_operation(operation)
def __init__(self, config_file, help_tool): QWidget.__init__(self) layout = QVBoxLayout() toolbar = QToolBar("toolbar") save_action = toolbar.addAction(resourceIcon("ide/disk"), "Save") save_action.triggered.connect(self.save) save_as_action = toolbar.addAction(resourceIcon("ide/save_as"), "Save As") save_as_action.triggered.connect(self.saveAs) # reload_icon = toolbar.style().standardIcon(QStyle.SP_BrowserReload) # reload_action = toolbar.addAction(reload_icon, "Reload") # reload_action.triggered.connect(self.reload) toolbar.addSeparator() toolbar.addAction(help_tool.getAction()) stretchy_separator = QWidget() stretchy_separator.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) toolbar.addWidget(stretchy_separator) search = SearchBox() search.setMaximumWidth(200) search.setContentsMargins(5, 2, 5, 2) toolbar.addWidget(search) layout.addWidget(toolbar) self.ide_panel = IdePanel() layout.addWidget(self.ide_panel, 1) self.config_file = config_file with open(config_file) as f: config_file_text = f.read() self.highlighter = KeywordHighlighter(self.ide_panel.document()) search.filterChanged.connect(self.highlighter.setSearchString) self.parseDefines(config_file_text) self.ide_panel.document().setPlainText(config_file_text) cursor = self.ide_panel.textCursor() cursor.setPosition(0) self.ide_panel.setTextCursor(cursor) self.ide_panel.setFocus() self.setLayout(layout)
class WorkflowWidget(QWidget): sigAddFunction = Signal(object) def __init__(self, workflowview: QAbstractItemView): super(WorkflowWidget, self).__init__() self.view = workflowview self.toolbar = QToolBar() addfunctionmenu = QToolButton() functionmenu = QMenu() sortingDict = {} for plugin in pluginmanager.getPluginsOfCategory("ProcessingPlugin"): typeOfProcessingPlugin = plugin.plugin_object.getCategory() if not typeOfProcessingPlugin in sortingDict.keys(): sortingDict[typeOfProcessingPlugin] = [] sortingDict[typeOfProcessingPlugin].append(plugin) for key in sortingDict.keys(): functionmenu.addSeparator() functionmenu.addAction(key) functionmenu.addSeparator() for plugin in sortingDict[key]: functionmenu.addAction( plugin.name, partial(self.addProcess, plugin.plugin_object, autoconnectall=True)) addfunctionmenu.setMenu(functionmenu) addfunctionmenu.setIcon(QIcon(path("icons/addfunction.png"))) addfunctionmenu.setText("Add Function") addfunctionmenu.setPopupMode(QToolButton.InstantPopup) self.toolbar.addWidget(addfunctionmenu) # self.toolbar.addAction(QIcon(path('icons/up.png')), 'Move Up') # self.toolbar.addAction(QIcon(path('icons/down.png')), 'Move Down') self.toolbar.addAction(QIcon(path("icons/folder.png")), "Load Workflow") self.toolbar.addAction(QIcon(path("icons/trash.png")), "Delete Operation", self.deleteProcess) v = QVBoxLayout() v.addWidget(self.view) v.addWidget(self.toolbar) v.setContentsMargins(0, 0, 0, 0) self.setLayout(v) def addProcess(self, process, autoconnectall=True): self.view.model().workflow.addProcess(process(), autoconnectall) print("selected new row:", self.view.model().rowCount() - 1) self.view.setCurrentIndex(self.view.model().index( self.view.model().rowCount() - 1, 0)) def deleteProcess(self): for index in self.view.selectedIndexes(): process = self.view.model().workflow._processes[index.row()] self.view.model().workflow.removeProcess(process)
def __init__(self, parent=None): super(EventFilterWindow, self).__init__(parent=parent) toolBar = QToolBar(self) toolBar.addAction(QWhatsThis.createAction(toolBar)) self.addToolBar(toolBar) plot = Plot() self.setCentralWidget(plot) plot.setWhatsThis( "An useless plot to demonstrate how to use event filtering.\n\n" "You can click on the color bar, the scales or move the slider.\n" "All points can be moved using the mouse or the keyboard.") CanvasPicker(plot) scalePicker = ScalePicker(plot) scalePicker.clicked.connect(plot.insertCurve)
class PMGWebBrowser(QWidget): def __init__(self, parent=None, toolbar='standard'): """ :param parent: :param toolbar:多种选项:‘no’,‘standard’,'no_url_input','refresh_only' """ super().__init__(parent) self.webview = PMGWebEngineView() self.setLayout(QVBoxLayout()) self.toolbar = QToolBar() self.url_input = QLineEdit() self.toolbar.addWidget(self.url_input) self.toolbar.addAction('go').triggered.connect( lambda b: self.load_url()) back_action = self.toolbar.addAction('back') back_action.triggered.connect(self.webview.back) forward_action = self.toolbar.addAction('forward') forward_action.triggered.connect(self.webview.forward) self.layout().addWidget(self.toolbar) if toolbar == 'no': self.toolbar.hide() elif toolbar == 'no_url_input': self.url_input.hide() elif toolbar == 'refresh_only': self.url_input.hide() back_action.setEnabled(False) forward_action.setEnabled(True) self.layout().addWidget(self.webview) self.setWindowTitle('My Browser') self.showMaximized() # command:> # jupyter notebook --port 5000 --no-browser --ip='*' --NotebookApp.token='' # --NotebookApp.password='' c:\users\12957\ # self.webview.load(QUrl("http://127.0.0.1:5000/notebooks/desktop/Untitled.ipynb")) # 直接请求页面。 # self.webview.load(QUrl("E:\Python\pyminer_bin\PyMiner\bin\pmgwidgets\display\browser\show_formula.html")) # 直接请求页面。 # self.setCentralWidget(self.webview) def load_url(self, url: str = ''): if url == '': url = self.url_input.text().strip() else: self.url_input.setText(url) self.webview.load(QUrl(url))
def __init__(self): QMainWindow.__init__(self) self.setWindowTitle('IV Analyzer') self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdiArea) self.windowMapper = QSignalMapper(self) # self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) # This indirection check whether window actual existed; not sure it's needed self.windowMapper.mapped[QWidget].connect( self.mdiArea.setActiveSubWindow) self.setupActions() self.setupMenuBar() toolbar = QToolBar('Analysis') toolbar.addAction(self.circuitParametersAction) self.addToolBar(toolbar)
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, app_manager, *args, **kwargs): # class init super(MainWindow, self).__init__(*args, **kwargs) self.app = app_manager # Setup Ui self.setupUi(self) self.actions = WindowActions(self.app) self.toolbar = QToolBar('toolbar') self.addToolBar(self.toolbar) # Setup Ui self.setWindowTitle(self.app.app_name) self.setWindowIcon(self.app.ui.window_icon) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) # Setup actions self.actions.exit.triggered.connect(self.app.quit) # Setup menus self.menu_File.addAction(self.actions.exit) # Setup toolbar self.toolbar.addAction(self.actions.exit) # Helpers def bar_log(self, msg): self.statusBar().showMessage(msg)
def fill_layout(self): """Fill grid layout""" import qwt for fname in get_tests(qwt): self.add_test(fname) toolbar = QToolBar(self) all_act = QAction(self.get_std_icon("DialogYesButton"), "", self) all_act.setIconText("Run all tests") all_act.triggered.connect(lambda checked: run_all_tests(wait=False)) folder_act = QAction(self.get_std_icon("DirOpenIcon"), "", self) folder_act.setIconText("Open tests folder") open_test_folder = lambda checked: os.startfile(TEST_PATH) folder_act.triggered.connect(open_test_folder) about_act = QAction(self.get_std_icon("FileDialogInfoView"), "", self) about_act.setIconText("About") about_act.triggered.connect(self.about) for action in (all_act, folder_act, None, about_act): if action is None: toolbar.addSeparator() else: toolbar.addAction(action) toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.addToolBar(toolbar)
def setup(self, toolbar_widget: QtWidgets.QToolBar): toolbar_widget.setOrientation(QtCore.Qt.Vertical) pan_icon = QtGui.QIcon(":/image/pan.png") pan = toolbar_widget.addAction(pan_icon, "Pan") pan.setCheckable(True) zoom_icon = QtGui.QIcon(":/image/zoom.png") zoom = toolbar_widget.addAction(zoom_icon, "Zoom") zoom.setCheckable(True) reset_icon = QtGui.QIcon(":/image/home.png") reset = toolbar_widget.addAction(reset_icon, "Reset") def f(checked, a): if checked: a.setChecked(False) pan.triggered.connect(self._onPan) zoom.triggered.connect(self._onZoom) reset.triggered.connect(self._onReset) self.pan_action = pan self.zoom_action = zoom
def __init__(self, widget: QWidget, buttonText: str, parent=None): """ Constructs a widget that lets the passed ``widget`` keep an internal collapsed state that can be triggered when a button is clicked. Internally, when the button is clicked, a toggled signal is emitted, indicating what the collapse state has been toggled to. Additionally, this signal is connected to the collapse() slot, which will collapse the passed widget if another widget has been added via addWidget(). The widget added via addWidget() is not collapsible. Parameters ---------- widget The widget to make collapsible. buttonText The text of the button that will be used to collapse. parent The parent widget. """ super(CollapsibleWidget, self).__init__(parent) self.widget = widget self.buttonText = buttonText self.collapsed = False toolBar = QToolBar() action = toolBar.addAction(self.buttonText, self.toggle) action.setIconText("&" + action.text()) self.collapseButton = toolBar.widgetForAction(action) self.collapseButton.setCheckable(True) self.collapseButton.setChecked(not self.collapsed) self.splitter = QSplitter(Qt.Horizontal) self.splitter.addWidget(self.widget) self.splitter.setCollapsible(0, self.collapsed) # Keep track of the collapsed widget's size to restore properly when un-collapsed self.widgetSize = self.splitter.sizes()[0] layout = QGridLayout() layout.addWidget(self.splitter, 0, 0) layout.addWidget(toolBar, 1, 0) self.setLayout(layout) self.toggled.connect(self.collapse)
def __init__(self, name, settings): super(Project, self).__init__() self._name = name # --- setup the splitter --- self._splitter = QSplitter() self._splitter.setContentsMargins(0, 0, 0, 0) self._splitter.setOrientation(Qt.Vertical) self._set_closeing_disabled(True) # --- setup the project buttons --- tool_bar = QToolBar() add_root_action = QAction('Add Root', self) tool_bar.addAction(add_root_action) tool_bar.widgetForAction(add_root_action).setObjectName('add_root') add_root_action.triggered.connect(lambda checked: self.add_root()) open_trash_action = QAction('Open Trash', self) tool_bar.addAction(open_trash_action) tool_bar.widgetForAction(open_trash_action).setObjectName('open_trash') open_trash_action.triggered.connect(self.open_trash) save_action = QAction('Save Project', self) tool_bar.addAction(save_action) tool_bar.widgetForAction(save_action).setObjectName('save_project') save_action.triggered.connect(self.save) # --- setup the layout --- main_layout = QVBoxLayout() main_layout.setSpacing(0) main_layout.setContentsMargins(4, 2, 4, 0) main_layout.addWidget(self._splitter) main_layout.addWidget(tool_bar) self.setLayout(main_layout) self._settings = None self.update_settings(settings)
class MainWindow(QMainWindow): FIT_WINDOW, FIT_WIDTH, MANUAL_ZOOM = 0, 1, 2 def __init__(self, label_file=None): super(MainWindow, self).__init__() self.showMaximized() self.setWindowTitle("VTCC.Labelling") self.file_dirs = [] self.file_id = -1 self.labels = [] with open(label_file, 'r') as f: lines = f.read().splitlines() for label in lines: self.labels.append(label) # RIGHT DOCK self.label_dock = QDockWidget("Label List", self) self.label_list_widget = QListWidget(self) self.load_labels(label_file) self.label_dock.setWidget(self.label_list_widget) self.object_dock = QDockWidget("Object List", self) self.object_list_widget = QListWidget(self) self.object_list_widget.currentRowChanged.connect(self.change_object) self.object_dock.setWidget(self.object_list_widget) self.file_dock = QDockWidget("File List", self) self.file_list_widget = QListWidget(self) self.file_list_widget.currentRowChanged.connect(self.change_file) self.file_dock.setWidget(self.file_list_widget) self.addDockWidget(Qt.RightDockWidgetArea, self.label_dock) self.addDockWidget(Qt.RightDockWidgetArea, self.object_dock) self.addDockWidget(Qt.RightDockWidgetArea, self.file_dock) # MAIN CANVAS self.canvas = Canvas(self) self.canvas_area = QScrollArea() self.canvas_area.setWidget(self.canvas) self.canvas_area.setWidgetResizable(True) self.scrollBars = { Qt.Vertical: self.canvas_area.verticalScrollBar(), Qt.Horizontal: self.canvas_area.horizontalScrollBar(), } self.setCentralWidget(self.canvas_area) # LEFT DOCK self.open_action = QAction(QIcon('icons/open.png'), 'Open File', self) self.open_action.triggered.connect(self.open_file) self.open_action.setShortcut(QKeySequence("Ctrl+O")) self.open_dir_action = QAction(QIcon('icons/open.png'), 'Open Dir', self) self.open_dir_action.triggered.connect(self.open_dir) self.next_img_action = QAction(QIcon('icons/next.png'), 'Next Image', self) self.next_img_action.triggered.connect(self.next_img) self.next_img_action.setShortcut(QKeySequence("Right")) self.prev_img_action = QAction(QIcon('icons/prev.png'), 'Prev Image', self) self.prev_img_action.triggered.connect(self.prev_img) self.prev_img_action.setShortcut(QKeySequence("Left")) self.zoom_in_action = QAction(QIcon('icons/zoom-in.png'), 'Zoom In', self) self.zoom_in_action.triggered.connect(self.zoom_in) self.zoom_out_action = QAction(QIcon('icons/zoom-out.png'), 'Zoom Out', self) self.zoom_out_action.triggered.connect(self.zoom_out) self.zoom_org_action = QAction(QIcon('icons/fit-window.png'), 'Fit Window', self) self.zoom_org_action.triggered.connect(self.zoom_org) self.rectangle_action = QAction(QIcon('icons/objects.png'), 'New Rectangle', self) self.rectangle_action.triggered.connect(self.new_rectangle) self.auto_polygon_action = QAction(QIcon('icons/objects.png'), 'New Auto-Polygon', self) self.auto_polygon_action.triggered.connect(self.new_auto_polygon) self.polygon_action = QAction(QIcon('icons/objects.png'), 'New Polygon', self) self.polygon_action.triggered.connect(self.new_polygon) self.next_obj_action = QAction(QIcon('icons/next.png'), 'Next Object', self) self.next_obj_action.triggered.connect(self.canvas.next_obj) self.next_obj_action.setShortcut(QKeySequence("Down")) self.prev_obj_action = QAction(QIcon('icons/prev.png'), 'Prev Object', self) self.prev_obj_action.triggered.connect(self.canvas.prev_obj) self.prev_obj_action.setShortcut(QKeySequence("Up")) self.del_obj_action = QAction(QIcon('icons/delete.png'), 'Delete Object', self) self.del_obj_action.triggered.connect(self.canvas.del_obj) self.del_obj_action.setShortcut(QKeySequence("Del")) self.toolbar = QToolBar(self) self.toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.toolbar.addAction(self.open_action) self.toolbar.addAction(self.open_dir_action) self.toolbar.addAction(self.next_img_action) self.toolbar.addAction(self.prev_img_action) # self.toolbar.addAction(self.zoom_in_action) # self.toolbar.addAction(self.zoom_out_action) # self.toolbar.addAction(self.zoom_org_action) self.toolbar.addAction(self.rectangle_action) self.toolbar.addAction(self.auto_polygon_action) self.toolbar.addAction(self.polygon_action) self.toolbar.addAction(self.next_obj_action) self.toolbar.addAction(self.prev_obj_action) self.toolbar.addAction(self.del_obj_action) self.addToolBar(Qt.LeftToolBarArea, self.toolbar) self.scalers = { self.FIT_WINDOW: self.scaleFitWindow, self.FIT_WIDTH: self.scaleFitWidth, # Set to one to scale to 100% when loading files. self.MANUAL_ZOOM: lambda: 1, } def update_mode(self, mode_id): pass def change_object(self, row): if (row >= 0): self.canvas.cur_object = row self.canvas.repaint() def change_file(self, row): if (row >= 0): self.file_id = row self.canvas.load_file(self.file_dirs[self.file_id]) self.adjustScale(initial=True) def open_file(self): path = '.' if len(self.file_dirs) > 0: path = os.path.dirname(str(self.file_dirs[0])) formats = [ '*.{}'.format(fmt.data().decode()) for fmt in QImageReader.supportedImageFormats() ] filters = "Image files (%s)" % ' '.join(formats) file_dir = QFileDialog.getOpenFileName(self, \ "Choose Image file", path, filters)[0] self.file_dirs = [file_dir] self.import_files() def open_dir(self): targetDirPath = str( QFileDialog.getExistingDirectory( self, 'Open Directory', '.', QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)) self.file_dirs = [] for fmt in QImageReader.supportedImageFormats(): pattern = os.path.join(targetDirPath, "*." + fmt.data().decode()) self.file_dirs += glob.glob(pattern) self.import_files() def next_img(self): if (len(self.file_dirs) > 0): self.file_id = (self.file_id + 1) % len(self.file_dirs) self.canvas.load_file(self.file_dirs[self.file_id]) self.adjustScale(initial=True) self.file_list_widget.setCurrentRow(self.file_id) def prev_img(self): if (len(self.file_dirs) > 0): self.file_id = (self.file_id + len(self.file_dirs) - 1) % len( self.file_dirs) self.canvas.load_file(self.file_dirs[self.file_id]) self.adjustScale(initial=True) self.file_list_widget.setCurrentRow(self.file_id) def import_files(self): self.load_file_list() self.file_id = 0 self.canvas.load_file(self.file_dirs[0]) self.adjustScale(initial=True) self.file_list_widget.setCurrentRow(self.file_id) def load_labels(self, label_file): self.label_list_widget.clear() for label in self.labels: item = QListWidgetItem(label) self.label_list_widget.addItem(item) def load_object_list(self, objects): self.object_list_widget.clear() for obj in objects: item = QListWidgetItem(obj.label) self.object_list_widget.addItem(item) def load_file_list(self): self.file_list_widget.clear() for image_dir in self.file_dirs: if not QFile.exists(image_dir): continue item = QListWidgetItem(image_dir) item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) label_dir = os.path.splitext(image_dir)[0] + '.json' if QFile.exists(label_dir): item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) self.file_list_widget.addItem(item) def scaleFitWindow(self): """Figure out the size of the pixmap to fit the main widget.""" e = 2.0 # So that no scrollbars are generated. w1 = self.centralWidget().width() - e h1 = self.centralWidget().height() - e a1 = w1 / h1 # Calculate a new scale value based on the pixmap's aspect ratio. w2 = self.canvas.pixmap.width() - 0.0 h2 = self.canvas.pixmap.height() - 0.0 a2 = w2 / h2 return w1 / w2 if a2 >= a1 else h1 / h2 def scaleFitWidth(self): # The epsilon does not seem to work too well here. w = self.centralWidget().width() - 2.0 return w / self.canvas.pixmap.width() def adjustScale(self, initial=False): value = self.scalers[self.FIT_WINDOW if initial else self.zoomMode]() self.canvas.rescale(value) def zoom_in(self): value = self.canvas.scale self.canvas.rescale(value * 1.1) def zoom_out(self): value = self.canvas.scale self.canvas.rescale(value * 0.9) def zoom_org(self): print(self.centralWidget().width(), self.centralWidget().height()) print(self.canvas.pixmap.width(), self.canvas.pixmap.height()) print(self.canvas.width(), self.canvas.height()) print(self.canvas_area.width(), self.canvas_area.height()) self.adjustScale(initial=True) def new_rectangle(self): self.canvas.points = [] self.canvas.mode = self.canvas.RECTANGLE def new_auto_polygon(self): self.canvas.points = [] self.canvas.mode = self.canvas.AUTO_POLYGON def new_polygon(self): self.canvas.points = [] self.canvas.mode = self.canvas.POLYGON
class WorkflowWidget(QWidget): sigAddFunction = Signal(object) sigRunWorkflow = Signal() # TODO -- emit Workflow from sigRunWorkflow def __init__(self, workflowview: QAbstractItemView, operation_filter: Callable[[OperationPlugin], bool] = None): super(WorkflowWidget, self).__init__() self.operation_filter = operation_filter self.view = workflowview self.autorun_checkbox = QCheckBox("Run Automatically") self.autorun_checkbox.setCheckState(Qt.Unchecked) self.autorun_checkbox.stateChanged.connect(self._autorun_state_changed) self.run_button = QPushButton("Run Workflow") self.run_button.clicked.connect(self.sigRunWorkflow.emit) self.view.model().workflow.attach(self._autorun) # TODO -- actually hook up the auto run OR dependent class needs to connect (see SAXSGUIPlugin) self.toolbar = QToolBar() self.addfunctionmenu = QToolButton() self.addfunctionmenu.setIcon(QIcon(path("icons/addfunction.png"))) self.addfunctionmenu.setText("Add Function") # Defer menu population to once the plugins have been loaded; otherwise, the menu may not contain anything # if this widget is init'd before all plugins have been loaded. self.functionmenu = QMenu() self.functionmenu.aboutToShow.connect(self.populateFunctionMenu) self.addfunctionmenu.setMenu(self.functionmenu) self.addfunctionmenu.setPopupMode(QToolButton.InstantPopup) self.toolbar.addWidget(self.addfunctionmenu) # self.toolbar.addAction(QIcon(path('icons/up.png')), 'Move Up') # self.toolbar.addAction(QIcon(path('icons/down.png')), 'Move Down') self.toolbar.addAction(QIcon(path("icons/folder.png")), "Load Workflow") self.toolbar.addAction(QIcon(path("icons/trash.png")), "Delete Operation", self.deleteOperation) v = QVBoxLayout() v.addWidget(self.view) h = QHBoxLayout() h.addWidget(self.autorun_checkbox) h.addWidget(self.run_button) v.addLayout(h) v.addWidget(self.toolbar) v.setContentsMargins(0, 0, 0, 0) self.setLayout(v) def _autorun_state_changed(self, state): if state == Qt.Checked: self.run_button.setDisabled(True) else: self.run_button.setDisabled(False) def _autorun(self): if self.autorun_checkbox.isChecked(): self.sigRunWorkflow.emit() def populateFunctionMenu(self): self.functionmenu.clear() sortingDict = MenuDict() operations = pluginmanager.get_plugins_of_type("OperationPlugin") if self.operation_filter is not None: operations = filter(self.operation_filter, operations) for operation in operations: categories = operation.categories if not categories: categories = [("Uncategorized", ) ] # put found operations into a default category for categories_tuple in categories: if isinstance(categories_tuple, str): categories_tuple = (categories_tuple, ) submenu = sortingDict categories_list = list(categories_tuple) while categories_list: category = categories_list.pop(0) submenu = submenu[category] submenu['___'].append(operation) self._mkMenu(sortingDict) def _mkMenu(self, sorting_dict, menu=None): if menu is None: menu = self.functionmenu menu.clear() for key in sorting_dict: if key == '___': menu.addSeparator() for operation in sorting_dict['___']: menu.addAction( operation.name, partial(self.addOperation, operation, autoconnectall=True)) else: submenu = QMenu(title=key, parent=menu) menu.addMenu(submenu) self._mkMenu(sorting_dict[key], submenu) def addOperation(self, operation: OperationPlugin, autoconnectall=True): self.view.model().workflow.add_operation(operation()) if autoconnectall: self.view.model().workflow.auto_connect_all() print("selected new row:", self.view.model().rowCount() - 1) self.view.setCurrentIndex(self.view.model().index( self.view.model().rowCount() - 1, 0)) def deleteOperation(self): index = self.view.currentIndex() operation = self.view.model().workflow.operations[index.row()] self.view.model().workflow.remove_operation(operation) self.view.setCurrentIndex(QModelIndex())
class WorkingDirectory(SpyderPluginWidget): """Working directory changer plugin.""" CONF_SECTION = 'workingdir' CONFIGWIDGET_CLASS = WorkingDirectoryConfigPage LOG_PATH = get_conf_path(CONF_SECTION) set_previous_enabled = Signal(bool) set_next_enabled = Signal(bool) redirect_stdio = Signal(bool) set_explorer_cwd = Signal(str) refresh_findinfiles = Signal() set_current_console_wd = Signal(str) def __init__(self, parent, workdir=None, **kwds): SpyderPluginWidget.__init__(self, parent) self.hide() self.toolbar = QToolBar(self) # Initialize plugin self.initialize_plugin() self.options_button.hide() self.toolbar.setWindowTitle(self.get_plugin_title()) # Used to save Window state self.toolbar.setObjectName(self.get_plugin_title()) # Previous dir action self.history = [] self.histindex = None self.previous_action = create_action(self, "previous", None, ima.icon('previous'), _('Back'), triggered=self.previous_directory) self.toolbar.addAction(self.previous_action) # Next dir action self.next_action = create_action(self, "next", None, ima.icon('next'), _('Next'), triggered=self.next_directory) self.toolbar.addAction(self.next_action) # Enable/disable previous/next actions self.set_previous_enabled.connect(self.previous_action.setEnabled) self.set_next_enabled.connect(self.next_action.setEnabled) # Path combo box adjust = self.get_option('working_dir_adjusttocontents') self.pathedit = PathComboBox(self, adjust_to_contents=adjust) self.pathedit.setToolTip(_("This is the working directory for newly\n" "opened consoles (Python/IPython consoles and\n" "terminals), for the file explorer, for the\n" "find in files plugin and for new files\n" "created in the editor")) self.pathedit.open_dir.connect(self.chdir) self.pathedit.activated[str].connect(self.chdir) self.pathedit.setMaxCount(self.get_option('working_dir_history')) wdhistory = self.load_wdhistory(workdir) if workdir is None: if self.get_option('console/use_project_or_home_directory'): workdir = get_home_dir() else: workdir = self.get_option('console/fixed_directory', default='') if not osp.isdir(workdir): workdir = get_home_dir() self.chdir(workdir) self.pathedit.addItems(wdhistory) self.pathedit.selected_text = self.pathedit.currentText() self.refresh_plugin() self.toolbar.addWidget(self.pathedit) # Browse action browse_action = create_action(self, "browse", None, ima.icon('DirOpenIcon'), _('Browse a working directory'), triggered=self.select_directory) self.toolbar.addAction(browse_action) # Parent dir action parent_action = create_action(self, "parent", None, ima.icon('up'), _('Change to parent directory'), triggered=self.parent_directory) self.toolbar.addAction(parent_action) #------ SpyderPluginWidget API --------------------------------------------- def get_plugin_title(self): """Return widget title""" return _('Current working directory') def get_plugin_icon(self): """Return widget icon""" return ima.icon('DirOpenIcon') def get_plugin_actions(self): """Setup actions""" return [None, None] def register_plugin(self): """Register plugin in Spyder's main window""" self.redirect_stdio.connect(self.main.redirect_internalshell_stdio) self.main.console.shell.refresh.connect(self.refresh_plugin) iconsize = 24 self.toolbar.setIconSize(QSize(iconsize, iconsize)) self.main.addToolBar(self.toolbar) def refresh_plugin(self): """Refresh widget""" curdir = getcwd_or_home() self.pathedit.add_text(curdir) self.save_wdhistory() self.set_previous_enabled.emit( self.histindex is not None and self.histindex > 0) self.set_next_enabled.emit(self.histindex is not None and \ self.histindex < len(self.history)-1) def apply_plugin_settings(self, options): """Apply configuration file's plugin settings""" pass def closing_plugin(self, cancelable=False): """Perform actions before parent main window is closed""" return True #------ Public API --------------------------------------------------------- def load_wdhistory(self, workdir=None): """Load history from a text file in user home directory""" if osp.isfile(self.LOG_PATH): wdhistory, _ = encoding.readlines(self.LOG_PATH) wdhistory = [name for name in wdhistory if os.path.isdir(name)] else: if workdir is None: workdir = get_home_dir() wdhistory = [ workdir ] return wdhistory def save_wdhistory(self): """Save history to a text file in user home directory""" text = [ to_text_string( self.pathedit.itemText(index) ) \ for index in range(self.pathedit.count()) ] try: encoding.writelines(text, self.LOG_PATH) except EnvironmentError: pass @Slot() def select_directory(self): """Select directory""" self.redirect_stdio.emit(False) directory = getexistingdirectory(self.main, _("Select directory"), getcwd_or_home()) if directory: self.chdir(directory) self.redirect_stdio.emit(True) @Slot() def previous_directory(self): """Back to previous directory""" self.histindex -= 1 self.chdir(directory='', browsing_history=True) @Slot() def next_directory(self): """Return to next directory""" self.histindex += 1 self.chdir(directory='', browsing_history=True) @Slot() def parent_directory(self): """Change working directory to parent directory""" self.chdir(os.path.join(getcwd_or_home(), os.path.pardir)) @Slot(str) @Slot(str, bool) @Slot(str, bool, bool) @Slot(str, bool, bool, bool) def chdir(self, directory, browsing_history=False, refresh_explorer=True, refresh_console=True): """Set directory as working directory""" if directory: directory = osp.abspath(to_text_string(directory)) # Working directory history management if browsing_history: directory = self.history[self.histindex] elif directory in self.history: self.histindex = self.history.index(directory) else: if self.histindex is None: self.history = [] else: self.history = self.history[:self.histindex+1] self.history.append(directory) self.histindex = len(self.history)-1 # Changing working directory try: os.chdir(directory) if refresh_explorer: self.set_explorer_cwd.emit(directory) if refresh_console: self.set_current_console_wd.emit(directory) self.refresh_findinfiles.emit() except OSError: self.history.pop(self.histindex) self.refresh_plugin()
class WorkflowWidget(QWidget): sigAddFunction = Signal(object) sigRunWorkflow = Signal() # TODO -- emit Workflow from sigRunWorkflow def __init__(self, workflowview: QAbstractItemView, operation_filter: Callable[[OperationPlugin], bool] = None, workflows: Dict[Workflow, str] = None): super(WorkflowWidget, self).__init__() self.operation_filter = operation_filter self.view = workflowview self.autorun_checkbox = QCheckBox("Run Automatically") self.autorun_checkbox.setCheckState(Qt.Unchecked) self.autorun_checkbox.stateChanged.connect(self._autorun_state_changed) self.run_button = QPushButton("Run Workflow") self.run_button.clicked.connect(self.sigRunWorkflow.emit) self.view.model().workflow.attach(self._autorun) # TODO -- actually hook up the auto run OR dependent class needs to connect (see SAXSGUIPlugin) self.toolbar = QToolBar() self.addfunctionmenu = QToolButton() self.addfunctionmenu.setIcon(QIcon(path("icons/addfunction.png"))) self.addfunctionmenu.setText("Add Function") self.addfunctionmenu.setToolTip("Add Operation") self.addfunctionmenu.setWhatsThis( "This button can be used to add a new operation to the end of a workflow. " "A menu to select operations will be populated based on the installed " "operations' categories.") # Defer menu population to once the plugins have been loaded; otherwise, the menu may not contain anything # if this widget is init'd before all plugins have been loaded. self.functionmenu = QMenu() self.functionmenu.aboutToShow.connect(self.populateFunctionMenu) self.addfunctionmenu.setMenu(self.functionmenu) self.addfunctionmenu.setPopupMode(QToolButton.InstantPopup) self.workflows = WorkflowDict(workflows or {}) self.workflow_menu = QMenu() self.workflow_menu.aboutToShow.connect(self.populateWorkflowMenu) self.workflow_selector = QToolButton() self.workflow_selector.setIcon(QIcon(path("icons/bookshelf.png"))) self.workflow_selector.setText("Select Workflow") self.workflow_selector.setToolTip("Workflow Library") self.workflow_selector.setWhatsThis( "This button allows switching between any stored workflows. " "(Stored workflows are typically defined programmatically " "in a GUI Plugin's modules.)") self.workflow_selector.setMenu(self.workflow_menu) self.workflow_selector.setPopupMode(QToolButton.InstantPopup) self.toolbar.addWidget(self.workflow_selector) self.toolbar.addWidget(self.addfunctionmenu) # self.toolbar.addAction(QIcon(path('icons/up.png')), 'Move Up') # self.toolbar.addAction(QIcon(path('icons/down.png')), 'Move Down') action = self.toolbar.addAction(QIcon(path("icons/save.png")), "Export Workflow") action.setEnabled(False) # FIXME: implement export workflow feature action = self.toolbar.addAction(QIcon(path("icons/folder.png")), "Import Workflow") action.setEnabled(False) # FIXME: implement import workflow feature action = self.toolbar.addAction(QIcon(path("icons/trash.png")), "Delete Operation", self.deleteOperation) action.setWhatsThis("This button removes the currently selected operation from the workflow. "\ "(The currently selected operation is highlighted. "\ "An operation is selected when its text is clicked in the workflow editor.") v = QVBoxLayout() v.addWidget(self.view) h = QHBoxLayout() h.addWidget(self.autorun_checkbox) h.addWidget(self.run_button) v.addLayout(h) v.addWidget(self.toolbar) v.setContentsMargins(0, 0, 0, 0) self.setLayout(v) def _autorun_state_changed(self, state): if state == Qt.Checked: self.run_button.setDisabled(True) else: self.run_button.setDisabled(False) def _autorun(self): if self.autorun_checkbox.isChecked(): self.sigRunWorkflow.emit() def populateFunctionMenu(self): self.functionmenu.clear() sortingDict = MenuDict() operations = pluginmanager.get_plugins_of_type("OperationPlugin") if self.operation_filter is not None: operations = filter(self.operation_filter, operations) for operation in operations: categories = operation.categories if not categories: categories = [("Uncategorized", ) ] # put found operations into a default category for categories_tuple in categories: if isinstance(categories_tuple, str): categories_tuple = (categories_tuple, ) submenu = sortingDict categories_list = list(categories_tuple) while categories_list: category = categories_list.pop(0) submenu = submenu[category] submenu['___'].append(operation) self._mkMenu(sortingDict) def populateWorkflowMenu(self): self.workflow_menu.clear() for workflow, workflow_name in self.workflows.items(): self.workflow_menu.addAction(workflow_name, partial(self.setWorkflow, workflow)) def _mkMenu(self, sorting_dict, menu=None): if menu is None: menu = self.functionmenu menu.clear() for key in sorting_dict: if key == '___': menu.addSeparator() for operation in sorting_dict['___']: menu.addAction( operation.name, partial(self.addOperation, operation, autoconnectall=True)) else: submenu = QMenu(title=key, parent=menu) menu.addMenu(submenu) self._mkMenu(sorting_dict[key], submenu) def setWorkflow(self, workflow: Workflow): self.view.model().workflow = workflow def addWorkflow(self, workflow: Workflow, name: str = None): if name is None: name = workflow.name if name in self.workflows: raise ValueError( f'A workflow already exists in this editor with the name "{name}"' ) self.workflows[name] = workflow def removeWorkflow(self, workflow): for name, match_workflow in self.workflows.items(): if workflow == match_workflow: del self.workflows[name] def addOperation(self, operation: OperationPlugin, autoconnectall=True): self.view.model().workflow.add_operation(operation()) if autoconnectall: self.view.model().workflow.auto_connect_all() print("selected new row:", self.view.model().rowCount() - 1) self.view.setCurrentIndex(self.view.model().index( self.view.model().rowCount() - 1, 0)) def deleteOperation(self): index = self.view.currentIndex() operation = self.view.model().workflow.operations[index.row()] self.view.model().workflow.remove_operation(operation) self.view.setCurrentIndex(QModelIndex())
class TyphosSidebarItem(ptypes.ParameterItem): """ Class to display a Device or Tool in the sidebar """ def __init__(self, param, depth): super().__init__(param, depth) # Configure a QToolbar self.toolbar = QToolBar() self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) self.toolbar.setIconSize(QSize(15, 15)) # Setup the action to open the widget self.open_action = QAction(qta.icon('fa.square', color='green'), 'Open', self.toolbar) self.open_action.triggered.connect(self.open_requested) # Setup the action to embed the widget self.embed_action = QAction(qta.icon('fa.th-large', color='yellow'), 'Embed', self.toolbar) self.embed_action.triggered.connect(self.embed_requested) # Setup the action to hide the widget self.hide_action = QAction(qta.icon('fa.times-circle', color='red'), 'Close', self.toolbar) self.hide_action.triggered.connect(self.hide_requested) self.hide_action.setEnabled(False) # Add actions to toolbars self.toolbar.addAction(self.open_action) self.toolbar.addAction(self.hide_action) if self.param.embeddable: self.toolbar.insertAction(self.hide_action, self.embed_action) def open_requested(self, triggered): """Request to open display for sidebar item""" self.param.sigOpen.emit(self) self._mark_shown() def embed_requested(self, triggered): """Request to open embedded display for sidebar item""" self.param.sigEmbed.emit(self) self._mark_shown() def hide_requested(self, triggered): """Request to hide display for sidebar item""" self.param.sigHide.emit(self) self._mark_hidden() def _mark_shown(self): self.open_action.setEnabled(False) self.embed_action.setEnabled(False) self.hide_action.setEnabled(True) def _mark_hidden(self): self.open_action.setEnabled(True) self.embed_action.setEnabled(True) self.hide_action.setEnabled(False) def treeWidgetChanged(self): """Update the widget when add to a QTreeWidget""" super().treeWidgetChanged() tree = self.treeWidget() if tree is None: return tree.setItemWidget(self, 1, self.toolbar)
class NoteEditor(QMainWindow): def __init__(self, parent, noteType, noteFileName="", b=None, c=None, v=None): super().__init__() self.parent, self.noteType = parent, noteType self.noteFileName = noteFileName if not self.noteType == "file": if v: self.b, self.c, self.v = b, c, v else: self.b, self.c, self.v = config.studyB, config.studyC, config.studyV # default - "Rich" mode for editing self.html = True # default - text is not modified; no need for saving new content self.parent.noteSaved = True config.noteOpened = True config.lastOpenedNote = (noteType, b, c, v) # specify window size self.resizeWindow(2/3, 2/3) # setup interface self.setupMenuBar() self.addToolBarBreak() self.setupToolBar() if config.hideNoteEditorStyleToolbar: self.toolBar.hide() self.addToolBarBreak() self.setupTextUtility() if config.hideNoteEditorTextUtility: self.ttsToolbar.hide() self.translateToolbar.hide() self.setupLayout() # display content when first launched self.displayInitialContent() self.editor.setFocus() # specify window title self.updateWindowTitle() # re-implementing close event, when users close this widget def closeEvent(self, event): if self.parent.noteSaved: config.noteOpened = False event.accept() if config.lastOpenedNote and config.openBibleNoteAfterEditorClosed: #if config.lastOpenedNote[0] == "file": # self.parent.externalFileButtonClicked() if config.lastOpenedNote[0] == "book": self.parent.openStudyBookNote() elif config.lastOpenedNote[0] == "chapter": self.parent.openStudyChapterNote() elif config.lastOpenedNote[0] == "verse": self.parent.openStudyVerseNote() else: if self.parent.warningNotSaved(): self.parent.noteSaved = True config.noteOpened = False event.accept() else: self.parent.bringToForeground(self) event.ignore() # re-implement keyPressEvent, control+S for saving file def keyPressEvent(self, event): keys = { Qt.Key_O: self.openFileDialog, Qt.Key_S: self.saveNote, Qt.Key_B: self.format_bold, Qt.Key_I: self.format_italic, Qt.Key_U: self.format_underline, Qt.Key_M: self.format_custom, Qt.Key_D: self.format_clear, Qt.Key_F: self.focusSearchField, } key = event.key() if event.modifiers() == Qt.ControlModifier and key in keys: keys[key]() # window appearance def resizeWindow(self, widthFactor, heightFactor): availableGeometry = QGuiApplication.instance().desktop().availableGeometry() self.resize(availableGeometry.width() * widthFactor, availableGeometry.height() * heightFactor) def updateWindowTitle(self): if self.noteType == "file": if self.noteFileName: *_, title = os.path.split(self.noteFileName) else: title = "NEW" else: title = self.parent.bcvToVerseReference(self.b, self.c, self.v) if self.noteType == "book": title, *_ = title.split(" ") elif self.noteType == "chapter": title, *_ = title.split(":") mode = {True: "rich", False: "plain"} notModified = {True: "", False: " [modified]"} self.setWindowTitle("Note Editor ({1} mode) - {0}{2}".format(title, mode[self.html], notModified[self.parent.noteSaved])) # switching between "rich" & "plain" mode def switchMode(self): if self.html: note = self.editor.toHtml() note = re.sub("<body style={0}[ ]*?font-family:[ ]*?'[^']*?';[ ]*?font-size:[ ]*?[0-9]+?pt;".format('"'), "<body style={0}font-family:'{1}'; font-size:{2}pt;".format('"', config.font, config.fontSize), note) self.editor.setPlainText(note) self.html = False self.updateWindowTitle() else: note = self.editor.toPlainText() self.editor.setHtml(note) self.html = True self.updateWindowTitle() # without this hide / show command below, QTextEdit does not update the text in some devices self.hide() self.show() def setupMenuBar(self): if config.toolBarIconFullSize: self.setupMenuBarFullIconSize() else: self.setupMenuBarStandardIconSize() def setupMenuBarStandardIconSize(self): self.menuBar = QToolBar() self.menuBar.setWindowTitle(config.thisTranslation["note_title"]) self.menuBar.setContextMenuPolicy(Qt.PreventContextMenu) # In QWidget, self.menuBar is treated as the menubar without the following line # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window self.addToolBar(self.menuBar) newButton = QPushButton() newButton.setToolTip("{0}\n[Ctrl/Cmd + N]".format(config.thisTranslation["menu7_create"])) newButtonFile = os.path.join("htmlResources", "newfile.png") newButton.setIcon(QIcon(newButtonFile)) newButton.clicked.connect(self.newNoteFile) self.menuBar.addWidget(newButton) openButton = QPushButton() openButton.setToolTip("{0}\n[Ctrl/Cmd + O]".format(config.thisTranslation["menu7_open"])) openButtonFile = os.path.join("htmlResources", "open.png") openButton.setIcon(QIcon(openButtonFile)) openButton.clicked.connect(self.openFileDialog) self.menuBar.addWidget(openButton) self.menuBar.addSeparator() saveButton = QPushButton() saveButton.setToolTip("{0}\n[Ctrl/Cmd + S]".format(config.thisTranslation["note_save"])) saveButtonFile = os.path.join("htmlResources", "save.png") saveButton.setIcon(QIcon(saveButtonFile)) saveButton.clicked.connect(self.saveNote) self.menuBar.addWidget(saveButton) saveAsButton = QPushButton() saveAsButton.setToolTip(config.thisTranslation["note_saveAs"]) saveAsButtonFile = os.path.join("htmlResources", "saveas.png") saveAsButton.setIcon(QIcon(saveAsButtonFile)) saveAsButton.clicked.connect(self.openSaveAsDialog) self.menuBar.addWidget(saveAsButton) self.menuBar.addSeparator() toolBarButton = QPushButton() toolBarButton.setToolTip(config.thisTranslation["note_print"]) toolBarButtonFile = os.path.join("htmlResources", "print.png") toolBarButton.setIcon(QIcon(toolBarButtonFile)) toolBarButton.clicked.connect(self.printNote) self.menuBar.addWidget(toolBarButton) self.menuBar.addSeparator() switchButton = QPushButton() switchButton.setToolTip(config.thisTranslation["note_mode"]) switchButtonFile = os.path.join("htmlResources", "switch.png") switchButton.setIcon(QIcon(switchButtonFile)) switchButton.clicked.connect(self.switchMode) self.menuBar.addWidget(switchButton) self.menuBar.addSeparator() # decreaseFontSizeButton = QPushButton() # decreaseFontSizeButton.setToolTip(config.thisTranslation["menu2_smaller"]) # decreaseFontSizeButtonFile = os.path.join("htmlResources", "fontMinus.png") # decreaseFontSizeButton.setIcon(QIcon(decreaseFontSizeButtonFile)) # decreaseFontSizeButton.clicked.connect(self.decreaseNoteEditorFontSize) # self.menuBar.addWidget(decreaseFontSizeButton) # # increaseFontSizeButton = QPushButton() # increaseFontSizeButton.setToolTip(config.thisTranslation["menu2_larger"]) # increaseFontSizeButtonFile = os.path.join("htmlResources", "fontPlus.png") # increaseFontSizeButton.setIcon(QIcon(increaseFontSizeButtonFile)) # increaseFontSizeButton.clicked.connect(self.increaseNoteEditorFontSize) # self.menuBar.addWidget(increaseFontSizeButton) # self.menuBar.addSeparator() self.searchLineEdit = QLineEdit() self.searchLineEdit.setClearButtonEnabled(True) self.searchLineEdit.setToolTip(config.thisTranslation["menu5_search"]) self.searchLineEdit.setMaximumWidth(400) self.searchLineEdit.returnPressed.connect(self.searchLineEntered) self.menuBar.addWidget(self.searchLineEdit) self.menuBar.addSeparator() toolBarButton = QPushButton() toolBarButton.setToolTip(config.thisTranslation["note_toolbar"]) toolBarButtonFile = os.path.join("htmlResources", "toolbar.png") toolBarButton.setIcon(QIcon(toolBarButtonFile)) toolBarButton.clicked.connect(self.toggleToolbar) self.menuBar.addWidget(toolBarButton) toolBarButton = QPushButton() toolBarButton.setToolTip(config.thisTranslation["note_textUtility"]) toolBarButtonFile = os.path.join("htmlResources", "textUtility.png") toolBarButton.setIcon(QIcon(toolBarButtonFile)) toolBarButton.clicked.connect(self.toggleTextUtility) self.menuBar.addWidget(toolBarButton) self.menuBar.addSeparator() def setupMenuBarFullIconSize(self): self.menuBar = QToolBar() self.menuBar.setWindowTitle(config.thisTranslation["note_title"]) self.menuBar.setContextMenuPolicy(Qt.PreventContextMenu) # In QWidget, self.menuBar is treated as the menubar without the following line # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window self.addToolBar(self.menuBar) iconFile = os.path.join("htmlResources", "newfile.png") self.menuBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + N]".format(config.thisTranslation["menu7_create"]), self.newNoteFile) iconFile = os.path.join("htmlResources", "open.png") self.menuBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + O]".format(config.thisTranslation["menu7_open"]), self.openFileDialog) self.menuBar.addSeparator() iconFile = os.path.join("htmlResources", "save.png") self.menuBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + S]".format(config.thisTranslation["note_save"]), self.saveNote) iconFile = os.path.join("htmlResources", "saveas.png") self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["note_saveAs"], self.openSaveAsDialog) self.menuBar.addSeparator() iconFile = os.path.join("htmlResources", "print.png") self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["note_print"], self.printNote) self.menuBar.addSeparator() iconFile = os.path.join("htmlResources", "switch.png") self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["note_mode"], self.switchMode) self.menuBar.addSeparator() # iconFile = os.path.join("htmlResources", "fontMinus.png") # self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["menu2_smaller"], self.decreaseNoteEditorFontSize) # # iconFile = os.path.join("htmlResources", "fontPlus.png") # self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["menu2_larger"], self.increaseNoteEditorFontSize) # self.menuBar.addSeparator() self.searchLineEdit = QLineEdit() self.searchLineEdit.setToolTip("{0}\n[Ctrl/Cmd + F]".format(config.thisTranslation["menu5_search"])) self.searchLineEdit.setMaximumWidth(400) self.searchLineEdit.returnPressed.connect(self.searchLineEntered) self.menuBar.addWidget(self.searchLineEdit) self.menuBar.addSeparator() iconFile = os.path.join("htmlResources", "toolbar.png") self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["note_toolbar"], self.toggleToolbar) iconFile = os.path.join("htmlResources", "textUtility.png") self.menuBar.addAction(QIcon(iconFile), config.thisTranslation["note_textUtility"], self.toggleTextUtility) self.menuBar.addSeparator() def toggleToolbar(self): if config.hideNoteEditorStyleToolbar: self.toolBar.show() config.hideNoteEditorStyleToolbar = False else: self.toolBar.hide() config.hideNoteEditorStyleToolbar = True def toggleTextUtility(self): if config.hideNoteEditorTextUtility: self.ttsToolbar.show() self.translateToolbar.show() config.hideNoteEditorTextUtility = False else: self.ttsToolbar.hide() self.translateToolbar.hide() config.hideNoteEditorTextUtility = True def printNote(self): #document = QTextDocument("Sample Page") document = self.editor.document() printer = QPrinter() myPrintDialog = QPrintDialog(printer, self) if myPrintDialog.exec_() == QDialog.Accepted: return document.print_(printer) def setupToolBar(self): if config.toolBarIconFullSize: self.setupToolBarFullIconSize() else: self.setupToolBarStandardIconSize() def setupToolBarStandardIconSize(self): self.toolBar = QToolBar() self.toolBar.setWindowTitle(config.thisTranslation["noteTool_title"]) self.toolBar.setContextMenuPolicy(Qt.PreventContextMenu) # self.toolBar can be treated as an individual widget and positioned with a specified layout # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window self.addToolBar(self.toolBar) items = ( ("noteTool_textFont", "font.png", self.format_font), ("noteTool_textColor", "textColor.png", self.format_textColor), ("noteTool_textBackgroundColor", "textBgColor.png", self.format_textBackgroundColor), ) for item in items: toolTip, icon, action = item self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar) self.toolBar.addSeparator() items = ( ("noteTool_header1", "header1.png", self.format_header1), ("noteTool_header2", "header2.png", self.format_header2), ("noteTool_header3", "header3.png", self.format_header3), ) for item in items: toolTip, icon, action = item self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar) self.toolBar.addSeparator() items = ( ("{0}\n[Ctrl/Cmd + B]".format(config.thisTranslation["noteTool_bold"]), "bold.png", self.format_bold), ("{0}\n[Ctrl/Cmd + I]".format(config.thisTranslation["noteTool_italic"]), "italic.png", self.format_italic), ("{0}\n[Ctrl/Cmd + U]".format(config.thisTranslation["noteTool_underline"]), "underline.png", self.format_underline), ) for item in items: toolTip, icon, action = item self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar, translation=False) self.toolBar.addSeparator() items = ( ("noteTool_superscript", "superscript.png", self.format_superscript), ("noteTool_subscript", "subscript.png", self.format_subscript), ) for item in items: toolTip, icon, action = item self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar) self.toolBar.addSeparator() self.parent.addStandardIconButton("{0}\n[Ctrl/Cmd + M]\n\n{1}\n* {4}\n* {5}\n* {6}\n\n{2}\n*1 {4}\n*2 {5}\n*3 {6}\n\n{3}\n{10}{4}|{5}|{6}{11}\n{10}{7}|{8}|{9}{11}".format(config.thisTranslation["noteTool_trans0"], config.thisTranslation["noteTool_trans1"], config.thisTranslation["noteTool_trans2"], config.thisTranslation["noteTool_trans3"], config.thisTranslation["noteTool_no1"], config.thisTranslation["noteTool_no2"], config.thisTranslation["noteTool_no3"], config.thisTranslation["noteTool_no4"], config.thisTranslation["noteTool_no5"], config.thisTranslation["noteTool_no6"], "{", "}"), "custom.png", self.format_custom, self.toolBar, translation=False) self.toolBar.addSeparator() items = ( ("noteTool_left", "align_left.png", self.format_left), ("noteTool_centre", "align_center.png", self.format_center), ("noteTool_right", "align_right.png", self.format_right), ("noteTool_justify", "align_justify.png", self.format_justify), ) for item in items: toolTip, icon, action = item self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar) self.toolBar.addSeparator() self.parent.addStandardIconButton("{0}\n[Ctrl/Cmd + D]".format(config.thisTranslation["noteTool_delete"]), "clearFormat.png", self.format_clear, self.toolBar, translation=False) self.toolBar.addSeparator() items = ( ("noteTool_hyperlink", "hyperlink.png", self.openHyperlinkDialog), ("noteTool_externalImage", "gallery.png", self.openImageDialog), ) for item in items: toolTip, icon, action = item self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar) self.toolBar.addSeparator() items = ( ("noteTool_image", "addImage.png", self.addInternalImage), ("noteTool_exportImage", "export.png", self.exportNoteImages), ) for item in items: toolTip, icon, action = item self.parent.addStandardIconButton(toolTip, icon, action, self.toolBar) self.toolBar.addSeparator() def setupToolBarFullIconSize(self): self.toolBar = QToolBar() self.toolBar.setWindowTitle(config.thisTranslation["noteTool_title"]) self.toolBar.setContextMenuPolicy(Qt.PreventContextMenu) # self.toolBar can be treated as an individual widget and positioned with a specified layout # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window self.addToolBar(self.toolBar) iconFile = os.path.join("htmlResources", "font.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_textFont"], self.format_font) iconFile = os.path.join("htmlResources", "textColor.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_textColor"], self.format_textColor) iconFile = os.path.join("htmlResources", "textBgColor.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_textBackgroundColor"], self.format_textBackgroundColor) self.toolBar.addSeparator() iconFile = os.path.join("htmlResources", "header1.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_header1"], self.format_header1) iconFile = os.path.join("htmlResources", "header2.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_header2"], self.format_header2) iconFile = os.path.join("htmlResources", "header3.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_header3"], self.format_header3) self.toolBar.addSeparator() iconFile = os.path.join("htmlResources", "bold.png") self.toolBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + B]".format(config.thisTranslation["noteTool_bold"]), self.format_bold) iconFile = os.path.join("htmlResources", "italic.png") self.toolBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + I]".format(config.thisTranslation["noteTool_italic"]), self.format_italic) iconFile = os.path.join("htmlResources", "underline.png") self.toolBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + U]".format(config.thisTranslation["noteTool_underline"]), self.format_underline) self.toolBar.addSeparator() iconFile = os.path.join("htmlResources", "custom.png") self.toolBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + M]\n\n{1}\n* {4}\n* {5}\n* {6}\n\n{2}\n*1 {4}\n*2 {5}\n*3 {6}\n\n{3}\n{10}{4}|{5}|{6}{11}\n{10}{7}|{8}|{9}{11}".format(config.thisTranslation["noteTool_trans0"], config.thisTranslation["noteTool_trans1"], config.thisTranslation["noteTool_trans2"], config.thisTranslation["noteTool_trans3"], config.thisTranslation["noteTool_no1"], config.thisTranslation["noteTool_no2"], config.thisTranslation["noteTool_no3"], config.thisTranslation["noteTool_no4"], config.thisTranslation["noteTool_no5"], config.thisTranslation["noteTool_no6"], "{", "}"), self.format_custom) self.toolBar.addSeparator() iconFile = os.path.join("htmlResources", "align_left.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_left"], self.format_left) iconFile = os.path.join("htmlResources", "align_center.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_centre"], self.format_center) iconFile = os.path.join("htmlResources", "align_right.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_right"], self.format_right) iconFile = os.path.join("htmlResources", "align_justify.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_justify"], self.format_justify) self.toolBar.addSeparator() iconFile = os.path.join("htmlResources", "clearFormat.png") self.toolBar.addAction(QIcon(iconFile), "{0}\n[Ctrl/Cmd + D]".format(config.thisTranslation["noteTool_delete"]), self.format_clear) self.toolBar.addSeparator() iconFile = os.path.join("htmlResources", "hyperlink.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_hyperlink"], self.openHyperlinkDialog) iconFile = os.path.join("htmlResources", "gallery.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_externalImage"], self.openImageDialog) self.toolBar.addSeparator() iconFile = os.path.join("htmlResources", "addImage.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_image"], self.addInternalImage) iconFile = os.path.join("htmlResources", "export.png") self.toolBar.addAction(QIcon(iconFile), config.thisTranslation["noteTool_exportImage"], self.exportNoteImages) self.toolBar.addSeparator() def setupLayout(self): self.editor = QTextEdit() self.editor.setStyleSheet("font-family:'{0}'; font-size:{1}pt;".format(config.font, config.fontSize)); self.editor.textChanged.connect(self.textChanged) self.setCentralWidget(self.editor) #self.layout = QGridLayout() #self.layout.setMenuBar(self.menuBar) #self.layout.addWidget(self.toolBar, 0, 0) #self.layout.addWidget(self.editor, 1, 0) #self.setLayout(self.layout) # adjustment of note editor font size def increaseNoteEditorFontSize(self): if self.html: self.editor.selectAll() config.noteEditorFontSize += 1 self.editor.setFontPointSize(config.noteEditorFontSize) self.hide() self.show() def decreaseNoteEditorFontSize(self): if self.html and not config.noteEditorFontSize == 0: self.editor.selectAll() config.noteEditorFontSize -= 1 self.editor.setFontPointSize(config.noteEditorFontSize) self.hide() self.show() # search field entered def searchLineEntered(self): searchString = self.searchLineEdit.text() if searchString: cursor = self.editor.document().find(searchString, self.editor.textCursor()) if cursor: self.editor.setTextCursor(cursor) self.hide() self.show() def focusSearchField(self): self.searchLineEdit.setFocus() # track if the text being modified def textChanged(self): if self.parent.noteSaved: self.parent.noteSaved = False self.updateWindowTitle() # display content when first launched def displayInitialContent(self): if self.noteType == "file": if self.noteFileName: self.openNoteFile(self.noteFileName) else: self.newNoteFile() else: self.openBibleNote() self.editor.selectAll() self.editor.setFontPointSize(config.noteEditorFontSize) self.editor.moveCursor(QTextCursor.Start, QTextCursor.MoveAnchor) self.parent.noteSaved = True def getEmptyPage(self): strict = '' if config.includeStrictDocTypeInNote: strict = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">' return """{4}<html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li {0} white-space: pre-wrap; {1} </style></head><body style="font-family:'{2}'; font-size:{3}pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html>"""\ .format("{", "}", config.font, config.fontSize, strict) # load chapter / verse notes from sqlite database def openBibleNote(self): if self.noteType == "book": note = NoteService.getBookNote(self.b) elif self.noteType == "chapter": note = NoteService.getChapterNote(self.b, self.c) elif self.noteType == "verse": note = NoteService.getVerseNote(self.b, self.c, self.v) if note == config.thisTranslation["empty"]: note = self.getEmptyPage() else: note = self.fixNoteFont(note) if self.html: self.editor.setHtml(note) else: self.editor.setPlainText(note) # File I / O def newNoteFile(self): if self.parent.noteSaved: self.newNoteFileAction() elif self.parent.warningNotSaved(): self.newNoteFileAction() def newNoteFileAction(self): self.noteType = "file" self.noteFileName = "" #self.editor.clear() defaultText = self.getEmptyPage() if self.html: self.editor.setHtml(defaultText) else: self.editor.setPlainText(defaultText) self.parent.noteSaved = True self.updateWindowTitle() self.hide() self.show() def openFileDialog(self): if self.parent.noteSaved: self.openFileDialogAction() elif self.parent.warningNotSaved(): self.openFileDialogAction() def openFileDialogAction(self): options = QFileDialog.Options() fileName, filtr = QFileDialog.getOpenFileName(self, config.thisTranslation["menu7_open"], "notes", "UniqueBible.app Note Files (*.uba);;HTML Files (*.html);;HTM Files (*.htm);;All Files (*)", "", options) if fileName: self.openNoteFile(fileName) def openNoteFile(self, fileName): try: f = open(fileName, "r", encoding="utf-8") except: print("Failed to open '{0}'".format(fileName)) note = f.read() f.close() self.noteType = "file" self.noteFileName = fileName note = self.fixNoteFont(note) if self.html: self.editor.setHtml(note) else: self.editor.setPlainText(note) self.parent.noteSaved = True self.updateWindowTitle() self.hide() self.show() def saveNote(self): if self.html: note = self.editor.toHtml() else: note = self.editor.toPlainText() note = self.fixNoteFont(note) if self.noteType == "book": NoteService.saveBookNote(self.b, note) if config.openBibleNoteAfterSave: self.parent.openBookNote(self.b,) self.parent.noteSaved = True self.updateWindowTitle() elif self.noteType == "chapter": NoteService.saveChapterNote(self.b, self.c, note) if config.openBibleNoteAfterSave: self.parent.openChapterNote(self.b, self.c) self.parent.noteSaved = True self.updateWindowTitle() elif self.noteType == "verse": NoteService.saveVerseNote(self.b, self.c, self.v, note) if config.openBibleNoteAfterSave: self.parent.openVerseNote(self.b, self.c, self.v) self.parent.noteSaved = True self.updateWindowTitle() elif self.noteType == "file": if self.noteFileName == "": self.openSaveAsDialog() else: self.saveAsNote(self.noteFileName) def openSaveAsDialog(self): if self.noteFileName: *_, defaultName = os.path.split(self.noteFileName) else: defaultName = "new.uba" options = QFileDialog.Options() fileName, filtr = QFileDialog.getSaveFileName(self, config.thisTranslation["note_saveAs"], os.path.join("notes", defaultName), "UniqueBible.app Note Files (*.uba);;HTML Files (*.html);;HTM Files (*.htm);;All Files (*)", "", options) if fileName: if not "." in os.path.basename(fileName): fileName = fileName + ".uba" self.saveAsNote(fileName) def saveAsNote(self, fileName): if self.html: note = self.editor.toHtml() else: note = self.editor.toPlainText() note = self.fixNoteFont(note) f = open(fileName, "w", encoding="utf-8") f.write(note) f.close() self.noteFileName = fileName self.parent.addExternalFileHistory(fileName) self.parent.setExternalFileButton() self.parent.noteSaved = True self.updateWindowTitle() def fixNoteFont(self, note): note = re.sub("<body style={0}[ ]*?font-family:[ ]*?'[^']*?';[ ]*?font-size:[ ]*?[0-9]+?pt;".format('"'), "<body style={0}font-family:'{1}'; font-size:{2}pt;".format('"', config.font, config.fontSize), note) if not config.includeStrictDocTypeInNote: note = re.sub("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">\n""", "", note) return note # formatting styles def format_clear(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: selectedText = """<span style="font-family:'{0}'; font-size:{1}pt;">{2}</span>""".format(config.font, config.fontSize, selectedText) self.editor.insertHtml(selectedText) else: selectedText = re.sub("<[^\n<>]*?>", "", selectedText) self.editor.insertPlainText(selectedText) else: self.selectTextFirst() def format_header1(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: self.editor.insertHtml("<h1>{0}</h1>".format(selectedText)) else: self.editor.insertPlainText("<h1>{0}</h1>".format(selectedText)) else: self.selectTextFirst() def format_header2(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: self.editor.insertHtml("<h2>{0}</h2>".format(selectedText)) else: self.editor.insertPlainText("<h2>{0}</h2>".format(selectedText)) else: self.selectTextFirst() def format_header3(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: self.editor.insertHtml("<h3>{0}</h3>".format(selectedText)) else: self.editor.insertPlainText("<h3>{0}</h3>".format(selectedText)) else: self.selectTextFirst() def format_font(self): selectedText = self.editor.textCursor().selectedText() if selectedText: ok, font = QFontDialog.getFont(QFont(config.font, config.fontSize), self) if ok: if self.html: self.editor.setCurrentFont(font) else: fontFamily, fontSize, i1, i2, fontWeight, italic, underline, strikeout, *_ = font.key().split(",") spanTag = """<span style="font-family:'{0}'; font-size:{1}pt;""".format(fontFamily, fontSize) # add font weight if fontWeight == "25": spanTag += " font-weight:200;" elif fontWeight == "75": spanTag += " font-weight:600;" # add italic style if italic == "1": spanTag += " font-style:italic;" # add both underline and strikeout style if underline == "1" and strikeout == "1": spanTag += " text-decoration: underline line-through;" # add underline style elif underline == "1": spanTag += " text-decoration: underline;" # add strikeout style elif strikeout == "1": spanTag += " text-decoration: line-through;" # close tag spanTag += '">' self.editor.insertPlainText("{0}{1}</span>".format(spanTag, selectedText)) else: self.selectTextFirst() def format_textColor(self): selectedText = self.editor.textCursor().selectedText() if selectedText: color = QColorDialog.getColor(Qt.darkRed, self) if color.isValid(): if self.html: self.editor.setTextColor(color) else: self.editor.insertPlainText('<span style="color:{0};">{1}</span>'.format(color.name(), self.editor.textCursor().selectedText())) else: self.selectTextFirst() def format_textBackgroundColor(self): selectedText = self.editor.textCursor().selectedText() if selectedText: color = QColorDialog.getColor(Qt.yellow, self) if color.isValid(): if self.html: self.editor.setTextBackgroundColor(color) else: self.editor.insertPlainText('<span style="background-color:{0};">{1}</span>'.format(color.name(), selectedText)) else: self.selectTextFirst() def format_bold(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: # Reference: https://doc.qt.io/qt-5/qfont.html#Weight-enum # Bold = 75 self.editor.setFontWeight(75) else: self.editor.insertPlainText("<b>{0}</b>".format(selectedText)) else: self.selectTextFirst() def format_italic(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: self.editor.setFontItalic(True) else: self.editor.insertPlainText("<i>{0}</i>".format(selectedText)) else: self.selectTextFirst() def format_underline(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: self.editor.setFontUnderline(True) else: self.editor.insertPlainText("<u>{0}</u>".format(selectedText)) else: self.selectTextFirst() def format_superscript(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: self.editor.insertHtml("<sup>{0}</sup>".format(selectedText)) else: self.editor.insertPlainText("<sup>{0}</sup>".format(selectedText)) else: self.selectTextFirst() def format_subscript(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: self.editor.insertHtml("<sub>{0}</sub>".format(selectedText)) else: self.editor.insertPlainText("<sub>{0}</sub>".format(selectedText)) else: self.selectTextFirst() def format_center(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: self.editor.setAlignment(Qt.AlignCenter) else: self.editor.insertPlainText("<div style='text-align:center;'>{0}</div>".format(selectedText)) else: self.selectTextFirst() def format_justify(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: self.editor.setAlignment(Qt.AlignJustify) else: self.editor.insertPlainText("<div style='text-align:justify;'>{0}</div>".format(selectedText)) else: self.selectTextFirst() def format_left(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: self.editor.setAlignment(Qt.AlignLeft) else: self.editor.insertPlainText("<div style='text-align:left;'>{0}</div>".format(selectedText)) else: self.selectTextFirst() def format_right(self): selectedText = self.editor.textCursor().selectedText() if selectedText: if self.html: self.editor.setAlignment(Qt.AlignRight) else: self.editor.insertPlainText("<div style='text-align:right;'>{0}</div>".format(selectedText)) else: self.selectTextFirst() def format_custom(self): selectedText = self.editor.textCursor().selectedText() if selectedText: selectedText = self.customFormat(selectedText) if self.html: self.editor.insertHtml(selectedText) else: self.editor.insertPlainText(selectedText) else: self.selectTextFirst() def customFormat(self, text): # QTextEdit's line break character by pressing ENTER in plain & html mode " " # please note that " " is not an empty string text = text.replace(" ", "\n") text = re.sub("^\*[0-9]+? (.*?)$", r"<ol><li>\1</li></ol>", text, flags=re.M) text = text.replace("</ol>\n<ol>", "\n") text = re.sub("^\* (.*?)$", r"<ul><li>\1</li></ul>", text, flags=re.M) text = text.replace("</ul>\n<ul>", "\n") text = re.sub("^{.*?}$", self.formatHTMLTable, text, flags=re.M) text = text.replace("</table>\n<table>", "\n") # add style to table here # please note that QTextEdit supports HTML 4, rather than HTML 5 # take this old reference: https://www.w3schools.com/tags/tag_table.asp text = text.replace('<table>', '<table border="1" cellpadding="5">') # convert back to QTextEdit linebreak text = text.replace("\n", " ") # wrap with default font and font-size text = """<span style="font-family:'{0}'; font-size:{1}pt;">{2}</span>""".format(config.font, config.fontSize, text) return text def formatHTMLTable(self, match): row = match.group()[1:-1] row = "".join(["<td>{0}</td>".format(cell) for cell in row.split("|")]) return "<table><tr>{0}</tr></table>".format(row) def addInternalImage(self): self.openImageDialog(external=False) def openImageDialog(self, external=True): options = QFileDialog.Options() fileName, filtr = QFileDialog.getOpenFileName(self, config.thisTranslation["html_open"], self.parent.openFileNameLabel.text(), "JPG Files (*.jpg);;JPEG Files (*.jpeg);;PNG Files (*.png);;GIF Files (*.gif);;BMP Files (*.bmp);;All Files (*)", "", options) if fileName: if external: self.linkExternalImage(fileName) else: self.embedImage(fileName) def embedImage(self, fileName): name, extension = os.path.splitext(os.path.basename(fileName)) with open(fileName, "rb") as fileObject: binaryData = fileObject.read() encodedData = base64.b64encode(binaryData) asciiString = encodedData.decode('ascii') imageTag = '<img src="data:image/{2};base64,{0}" alt="{1}">'.format(asciiString, name, extension[1:]) if self.html: self.editor.insertHtml(imageTag) else: self.editor.insertPlainText(imageTag) def exportNoteImages(self): options = QFileDialog.DontResolveSymlinks | QFileDialog.ShowDirsOnly directory = QFileDialog.getExistingDirectory(self, config.thisTranslation["select_a_folder"], self.parent.directoryLabel.text(), options) if directory: if self.html: htmlText = self.editor.toHtml() else: htmlText = self.editor.toPlainText() searchPattern = r'src=(["{0}])data:image/([^<>]+?);[ ]*?base64,[ ]*?([^ <>]+?)\1'.format("'") for counter, value in enumerate(re.findall(searchPattern, htmlText)): *_, ext, asciiString = value binaryString = asciiString.encode("ascii") binaryData = base64.b64decode(binaryString) imageFilePath = os.path.join(directory, "image{0}.{1}".format(counter + 1, ext)) with open(imageFilePath, "wb") as fileObject: fileObject.write(binaryData) def linkExternalImage(self, fileName): imageTag = '<img src="{0}" alt="UniqueBible.app">'.format(fileName) if self.html: self.editor.insertHtml(imageTag) else: self.editor.insertPlainText(imageTag) def openHyperlinkDialog(self): selectedText = self.editor.textCursor().selectedText() if selectedText: text, ok = QInputDialog.getText(self, "UniqueBible.app", config.thisTranslation["noteTool_hyperlink"], QLineEdit.Normal, selectedText) if ok and text != '': hyperlink = '<a href="{0}">{1}</a>'.format(text, selectedText) hyperlink = """<span style="font-family:'{0}'; font-size:{1}pt;">{2}</span>""".format(config.font, config.fontSize, hyperlink) if self.html: self.editor.insertHtml(hyperlink) else: self.editor.insertPlainText(hyperlink) else: self.selectTextFirst() def setupTextUtility(self): self.ttsToolbar = QToolBar() self.ttsToolbar.setWindowTitle(config.thisTranslation["noteTool_title"]) self.ttsToolbar.setContextMenuPolicy(Qt.PreventContextMenu) # self.toolBar can be treated as an individual widget and positioned with a specified layout # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window self.addToolBar(self.ttsToolbar) self.languageCombo = QComboBox() self.ttsToolbar.addWidget(self.languageCombo) if config.espeak: languages = TtsLanguages().isoLang2epeakLang else: languages = TtsLanguages().isoLang2qlocaleLang self.languageCodes = list(languages.keys()) for code in self.languageCodes: self.languageCombo.addItem(languages[code][1]) # Check if selected tts engine has the language user specify. if not (config.ttsDefaultLangauge in self.languageCodes): config.ttsDefaultLangauge = "en" # Set initial item initialIndex = self.languageCodes.index(config.ttsDefaultLangauge) self.languageCombo.setCurrentIndex(initialIndex) button = QPushButton(config.thisTranslation["speak"]) button.setToolTip(config.thisTranslation["speak"]) button.clicked.connect(self.speakText) self.ttsToolbar.addWidget(button) button = QPushButton(config.thisTranslation["stop"]) button.setToolTip(config.thisTranslation["stop"]) button.clicked.connect(self.parent.textCommandParser.stopTtsAudio) self.ttsToolbar.addWidget(button) self.translateToolbar = QToolBar() self.translateToolbar.setWindowTitle(config.thisTranslation["noteTool_title"]) self.translateToolbar.setContextMenuPolicy(Qt.PreventContextMenu) # self.toolBar can be treated as an individual widget and positioned with a specified layout # In QMainWindow, the following line adds the configured QToolBar as part of the toolbar of the main window self.addToolBar(self.translateToolbar) self.fromLanguageCombo = QComboBox() self.translateToolbar.addWidget(self.fromLanguageCombo) self.fromLanguageCombo.addItems(["[Auto]"] +Translator.fromLanguageNames) initialIndex = 0 self.fromLanguageCombo.setCurrentIndex(initialIndex) button = QPushButton(config.thisTranslation["context1_translate"]) button.setToolTip(config.thisTranslation["context1_translate"]) button.clicked.connect(self.translateText) self.translateToolbar.addWidget(button) self.toLanguageCombo = QComboBox() self.translateToolbar.addWidget(self.toLanguageCombo) self.toLanguageCombo.addItems(Translator.toLanguageNames) initialIndex = Translator.toLanguageNames.index(config.userLanguage) self.toLanguageCombo.setCurrentIndex(initialIndex) def speakText(self): text = self.editor.textCursor().selectedText() if text: if config.isTtsInstalled: if ":::" in text: text = text.split(":::")[-1] command = "SPEAK:::{0}:::{1}".format(self.languageCodes[self.languageCombo.currentIndex()], text) self.parent.runTextCommand(command) else: self.displayMessage(config.thisTranslation["message_noSupport"]) else: self.selectTextFirst() def translateText(self): text = self.editor.textCursor().selectedText() if text: translator = Translator() if translator.language_translator is not None: fromLanguage = Translator.fromLanguageCodes[self.fromLanguageCombo.currentIndex() - 1] if self.fromLanguageCombo.currentIndex() != 0 else translator.identify(text) toLanguage = Translator.toLanguageCodes[self.toLanguageCombo.currentIndex()] result = translator.translate(text, fromLanguage, toLanguage) self.editor.insertPlainText(result) else: self.displayMessage(config.thisTranslation["ibmWatsonNotEnalbed"]) webbrowser.open("https://github.com/eliranwong/UniqueBible/wiki/IBM-Watson-Language-Translator") else: self.selectTextFirst() def selectTextFirst(self): self.displayMessage(config.thisTranslation["selectTextFirst"]) def displayMessage(self, message="", title="UniqueBible"): reply = QMessageBox.information(self, title, message)
def create_toolbars(self): for k, v in self.actions.items(): toolbar_item = QToolBar(k) toolbar_item.addAction(v) self.addToolBar(Qt.LeftToolBarArea, toolbar_item)
def __init__(self, settings, path=None): super(RootWidget, self).__init__() # --- setup the file system model --- model = QFileSystemModel() model.setRootPath('This PC') model.setReadOnly(False) model.setIconProvider( JSONFileIconProvider('file_view_icons.json') ) model.setFilter( QDir.AllEntries | QDir.NoDotAndDotDot | QDir.AllDirs | QDir.Hidden ) self._model = FileSystemProxyModel() self._model.setSourceModel(model) # --- setup the tree view --- self._view = QTreeView() self._view.setModel(self._model) self._view.setSelectionMode(QTreeView.ExtendedSelection) self._view.activated.connect(self._handle_activated_index) self._view.setSortingEnabled(True) self._view.setHeaderHidden(True) self._view.setExpandsOnDoubleClick(False) self._view.sortByColumn(0, Qt.AscendingOrder) # Setup drag and drop. self._view.setDragDropMode(QTreeView.DragDrop) self._view.setDefaultDropAction(Qt.MoveAction) # Setup the columns that show. self._view.hideColumn(1) self._view.hideColumn(2) self._view.hideColumn(3) # Note that the double click trigger cannot be in this list, otherwise it flashes in edit # mode before opening the file self._view.setEditTriggers( QTreeView.SelectedClicked | QTreeView.EditKeyPressed) # setup the context menu self._view.setContextMenuPolicy(Qt.CustomContextMenu) self._view.customContextMenuRequested.connect(self._context_menu) # Unselect items on collapse. self._view.collapsed.connect(self._view.clearSelection) # --- setup the current root path label --- self._root_edit = PathEdit() self._root_edit.new_path.connect(self._set_root_path) # --- setup the tool bar --- tool_bar = QToolBar() new_file_action = QAction('New File', self) tool_bar.addAction(new_file_action) tool_bar.widgetForAction(new_file_action).setObjectName('new_file') new_file_action.triggered.connect(self._create_file) new_directory_action = QAction('New Directory', self) tool_bar.addAction(new_directory_action) tool_bar.widgetForAction(new_directory_action).setObjectName('new_directory') new_directory_action.triggered.connect(self._create_directory) close_action = QAction('Close', self) tool_bar.addAction(close_action) self._close_widget = tool_bar.widgetForAction(close_action) self._close_widget.setObjectName('close_root') close_action.triggered.connect(self.close_request.emit) # --- setup the layout --- main_layout = QVBoxLayout() main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) path_layout = QHBoxLayout() path_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) path_layout.addWidget(self._root_edit) path_layout.addWidget(tool_bar) main_layout.addLayout(path_layout) main_layout.addWidget(self._view) self.setLayout(main_layout) if path is not None: self._set_root_path(path) self._root_edit.update(path) self._settings = None self.update_settings(settings)
class ViewerWindow(QMainWindow, Ui_ViewerWindow): def __init__(self, app_manager, images_list, *args, **kwargs): super(ViewerWindow, self).__init__(*args, **kwargs) self.setupUi(self) # Init self self.app = app_manager self.settings = self.app.settings.get_viewer_settings() self.images_list = images_list self.window_moving = False self.win_old_pos = None self.lbl_old_pos = None self.image = next(self.images_list) self.viewer_policy = {"scale": 'fit_auto'} # Init UI self.actions = WindowActions(self.app) self.toolbar = QToolBar('toolbar') self.init_ui() # Init events self.label.mouseDoubleClickEvent = self.label_double_click_event self.label.mousePressEvent = self.label_mouse_press_event self.label.mouseMoveEvent = self.label_mouse_move_event def init_ui(self): # Setup: window self.setWindowTitle(self.app.app_name) self.setWindowIcon(self.app.ui.window_icon) self.centralwidget.layout().setContentsMargins(0, 0, 0, 0) self.statusbar.setVisible(self.settings[V_SHOW_STATUS_BAR]) # Setup: actions self.actions.previous.triggered.connect(self.previous_action) self.actions.next.triggered.connect(self.next_action) self.actions.fit_to_window.triggered.connect(self.fit_to_window) self.actions.fit_to_width.triggered.connect(self.fit_to_width) self.actions.fit_to_height.triggered.connect(self.fit_to_height) self.actions.show_original_size.triggered.connect(self.original_size) self.actions.zoom_in.triggered.connect(self.zoom_in_action) self.actions.zoom_out.triggered.connect(self.zoom_out_action) self.actions.rotate_right.triggered.connect(self.rotate_right_action) self.actions.rotate_left.triggered.connect(self.rotate_left_action) self.actions.flip_vertically.triggered.connect(self.flip_vertically_action) self.actions.flip_horizontally.triggered.connect(self.flip_horizontally_action) self.actions.reload.triggered.connect(self.reload_action) self.actions.show_statusbar.triggered.connect(self.show_statusbar_action) self.actions.slideshow.triggered.connect(self.slideshow_action) self.actions.settings.triggered.connect(self.settings_action) self.actions.minimize.triggered.connect(self.minimize_action) self.actions.maximize.triggered.connect(self.maximize_action) self.actions.about.triggered.connect(self.about_action) self.actions.exit.triggered.connect(self.exit_action) # Setup: context menu self.label.addAction(self.actions.previous) self.label.addAction(self.actions.next) self.label.addAction(self.actions.fit_to_window) self.label.addAction(self.actions.fit_to_width) self.label.addAction(self.actions.fit_to_height) self.label.addAction(self.actions.show_original_size) self.label.addAction(self.actions.zoom_in) self.label.addAction(self.actions.zoom_out) self.label.addAction(self.actions.rotate_right) self.label.addAction(self.actions.rotate_left) self.label.addAction(self.actions.flip_vertically) self.label.addAction(self.actions.flip_horizontally) self.label.addAction(self.actions.reload) self.label.addAction(self.actions.show_statusbar) self.label.addAction(self.actions.slideshow) self.label.addAction(self.actions.settings) self.label.addAction(self.actions.about) self.label.addAction(self.actions.exit) # Setup: toolbar self.toolbar.setMovable(False) self.toolbar.setContextMenuPolicy(Qt.NoContextMenu) self.setContextMenuPolicy(Qt.NoContextMenu) self.addToolBar(self.toolbar) self.toolbar.addAction(self.actions.previous) self.toolbar.addAction(self.actions.next) self.toolbar.addSeparator() self.toolbar.addAction(self.actions.fit_to_window) self.toolbar.addAction(self.actions.fit_to_width) self.toolbar.addAction(self.actions.fit_to_height) self.toolbar.addAction(self.actions.show_original_size) self.toolbar.addAction(self.actions.zoom_in) self.toolbar.addAction(self.actions.zoom_out) self.toolbar.addSeparator() self.toolbar.addAction(self.actions.rotate_right) self.toolbar.addAction(self.actions.rotate_left) self.toolbar.addAction(self.actions.flip_vertically) self.toolbar.addAction(self.actions.flip_horizontally) self.toolbar.addSeparator() self.toolbar.addAction(self.actions.reload) self.toolbar.addWidget(self.actions.separator) self.toolbar.addAction(self.actions.show_statusbar) self.toolbar.addAction(self.actions.slideshow) # Setup Compact theme if self.app.ui.app_theme == 'Compact': self.setWindowFlags(Qt.FramelessWindowHint) self.toolbar.addAction(self.actions.minimize) self.toolbar.addAction(self.actions.maximize) self.toolbar.addAction(self.actions.exit) # Restore last window state self._restore_geometry() def repaint_image(self): width = self.centralWidget().width() - 2 height = self.centralWidget().height() - 2 pixmap = self.image.pixmap(self.viewer_policy, width, height) self.label.setPixmap(pixmap) # Actions def previous_action(self): self.image = self.images_list.__prev__() self.repaint_image() def next_action(self): self.image = self.images_list.__next__() self.repaint_image() def zoom_in_action(self): self.image.zoom_in() self.repaint_image() self._center_label() def zoom_out_action(self): self.image.zoom_out() self.repaint_image() self._center_label() def rotate_right_action(self): self.image.rotate_right() self.repaint_image() def rotate_left_action(self): self.image.rotate_left() self.repaint_image() def flip_vertically_action(self): self.image.flip_vertically() self.repaint_image() def flip_horizontally_action(self): self.image.flip_horizontally() self.repaint_image() def fit_to_window(self): if self.actions.fit_to_window.isChecked(): self.viewer_policy["scale"] = 'fit_to_window' else: self.viewer_policy["scale"] = 'fit_auto' self.actions.fit_to_width.setChecked(False) self.actions.fit_to_height.setChecked(False) self.actions.show_original_size.setChecked(False) self.repaint_image() def fit_to_width(self): if self.actions.fit_to_width.isChecked(): self.viewer_policy["scale"] = 'fit_to_width' else: self.viewer_policy["scale"] = 'fit_auto' self.actions.fit_to_window.setChecked(False) self.actions.fit_to_height.setChecked(False) self.actions.show_original_size.setChecked(False) self.repaint_image() def fit_to_height(self): if self.actions.fit_to_height.isChecked(): self.viewer_policy["scale"] = 'fit_to_height' else: self.viewer_policy["scale"] = 'fit_auto' self.actions.fit_to_window.setChecked(False) self.actions.fit_to_width.setChecked(False) self.actions.show_original_size.setChecked(False) self.repaint_image() def original_size(self): if self.actions.show_original_size.isChecked(): self.viewer_policy["scale"] = 'original_size' else: self.viewer_policy["scale"] = 'fit_auto' self.actions.fit_to_window.setChecked(False) self.actions.fit_to_width.setChecked(False) self.actions.fit_to_height.setChecked(False) self.repaint_image() def reload_action(self): self.image.reload() self.repaint_image() def show_statusbar_action(self): if self.statusbar.isHidden(): self.statusbar.show() else: self.statusbar.hide() self.repaint_image() def slideshow_action(self): if self.isFullScreen(): self._show_normal() else: self._show_fullscreen() def minimize_action(self): self.showMinimized() def maximize_action(self): if self.isMaximized(): self._show_normal() else: self._show_maximized() def settings_action(self): dialog = SettingsDialog(self.app) dialog.exec_() def about_action(self): dialog = AboutDialog(self.app) dialog.exec_() def exit_action(self): if self.isFullScreen(): self._show_normal() else: self._save_geometry() self.close() self.app.quit() # Events def resizeEvent(self, event): self.repaint_image() def label_double_click_event(self, _): self._reset_viewer() def label_mouse_press_event(self, event): if event.button() == Qt.LeftButton: self.lbl_old_pos = event.pos() def label_mouse_move_event(self, event): if event.buttons() == Qt.LeftButton: offset = self.lbl_old_pos - event.pos() self.lbl_old_pos = event.pos() self.scrollArea.verticalScrollBar().setValue(self.scrollArea.verticalScrollBar().value() + offset.y()) self.scrollArea.horizontalScrollBar().setValue(self.scrollArea.horizontalScrollBar().value() + offset.x()) def mouseDoubleClickEvent(self, event): if event.button() == Qt.LeftButton: self.maximize_action() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.win_old_pos = event.globalPos() self.window_moving = True def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: self.setWindowOpacity(1) self.window_moving = False def mouseMoveEvent(self, event): if self.window_moving and not self.isMaximized() and not self.isFullScreen(): delta = QPoint(event.globalPos() - self.win_old_pos) self.move(self.x() + delta.x(), self.y() + delta.y()) self.win_old_pos = event.globalPos() self.setWindowOpacity(0.5) # Helpers def _show_message(self, msg): self.statusBar().showMessage(msg) def _show_normal(self): self.showNormal() self.actions.maximize.setIcon(self.app.ui.maximize_icon) self.actions.minimize.setVisible(True) self.actions.maximize.setVisible(True) def _show_maximized(self): self.showMaximized() self.actions.maximize.setIcon(self.app.ui.restore_icon) def _show_fullscreen(self): self.showFullScreen() self.actions.minimize.setVisible(False) self.actions.maximize.setVisible(False) def _save_geometry(self): geometry = self.saveGeometry() self.app.settings.set(VIEWER_WINDOW_GEOMETRY, geometry) def _restore_geometry(self): if self.settings[V_SAVE_WINDOW_GEOMETRY]: try: self.restoreGeometry(self.settings[VIEWER_WINDOW_GEOMETRY]) if self.isMaximized(): self._show_maximized() else: self._show_normal() except Exception: self._center_window() else: self._center_window() self.win_old_pos = self.pos() def _center_window(self): self.setFixedSize(self.app.ui.best_window_width, self.app.ui.best_window_height) frame_geometry = self.frameGeometry() frame_geometry.moveCenter(self.app.ui.screen_center) self.move(frame_geometry.topLeft()) def _center_label(self): h_max = self.scrollArea.horizontalScrollBar().maximum() v_max = self.scrollArea.verticalScrollBar().maximum() self.scrollArea.horizontalScrollBar().setValue(h_max / 2) self.scrollArea.verticalScrollBar().setValue(v_max / 2) def _reset_viewer(self): self.viewer_policy["scale"] = 'fit_auto' self.image.reload() self.actions.fit_to_window.setChecked(False) self.actions.fit_to_width.setChecked(False) self.actions.fit_to_height.setChecked(False) self.actions.show_original_size.setChecked(False) self.repaint_image()
class SpecimenPositionListWidget(ParameterWidget): class _SpecimenPositionModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self.positions = [] def rowCount(self, *args, **kwargs): return len(self.positions) def columnCount(self, *args, **kwargs): return 5 def data(self, index, role): if not index.isValid() or not (0 <= index.row() < len(self.positions)): return None if role != Qt.DisplayRole: return None position = self.positions[index.row()] column = index.column() if column == 0: return str(position.x) if position.x is not None else '' elif column == 1: return str(position.y) if position.y is not None else '' elif column == 2: return str(position.z) if position.z is not None else '' elif column == 3: return str(position.r) if position.r is not None else '' elif column == 4: return str(position.t) if position.t is not None else '' def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'X' elif section == 1: return 'Y' elif section == 2: return 'Z' elif section == 3: return 'R' elif section == 4: return 'T' elif orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self.positions)): return False position = self.positions[index.row()] column = index.column() if column == 0: position.x = value elif column == 1: position.y = value elif column == 2: position.z = value elif column == 3: position.r = value elif column == 4: position.t = value return True def insertRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginInsertRows(parent, row, row + count - 1) for i in range(count): self.positions.insert(row + i, SpecimenPosition()) self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginRemoveRows(parent, row, row + count - 1) self.positions = self.positions[:row] + self.positions[row + count:] self.endRemoveRows() return True class _SpecimenPositionDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): column = index.column() if column == 0: return NumericalAttributeLineEdit(SpecimenPosition.x, parent) elif column == 1: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) elif column == 2: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) elif column == 3: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) elif column == 4: return NumericalAttributeLineEdit(SpecimenPosition.y, parent) else: return QItemDelegate.createEditor(self, parent, option, index) def setEditorData(self, editor, index): text = index.model().data(index, Qt.DisplayRole) column = index.column() if column == 0: editor.setText(text) elif column == 1: editor.setText(text) elif column == 2: editor.setText(text) elif column == 3: editor.setText(text) elif column == 4: editor.setText(text) else: QItemDelegate.setEditorData(self, editor, index) def setModelData(self, editor, model, index): column = index.column() if column == 0: model.setData(index, editor.text()) elif column == 1: model.setData(index, editor.text()) elif column == 2: model.setData(index, editor.text()) elif column == 3: model.setData(index, editor.text()) elif column == 4: model.setData(index, editor.text()) else: return QItemDelegate.setModelData(self, editor, model, index) def __init__(self, parent=None): ParameterWidget.__init__(self, object, parent) def _init_ui(self): # Widgets self._table = QTableView() self._table.setModel(self._SpecimenPositionModel()) self._table.setItemDelegate(self._SpecimenPositionDelegate(self)) self._table.horizontalHeader().setStretchLastSection(True) self._toolbar = QToolBar() action_add = self._toolbar.addAction(getIcon("list-add"), "Add layer") action_remove = self._toolbar.addAction(getIcon("list-remove"), "Remove layer") # Layouts layout = ParameterWidget._init_ui(self) layout.addRow(self._table) layout.addRow(self._toolbar) # Signals action_add.triggered.connect(self._on_add) action_remove.triggered.connect(self._on_remove) return layout def _on_add(self): index = self._table.selectionModel().currentIndex() model = self._table.model() model.insertRows(index.row() + 1) def _on_remove(self): selection = self._table.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Specimen position", "Select a position") return model = self._table.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.removeRow(row) def parameter(self): positions = [] for position in self._table.model().positions: positions.append(SpecimenPosition(position.x, position.y, position.z, position.r, position.t)) return positions def setParameter(self, positions): model = self._table.model() model.positions = positions model.reset() def positions(self): return self.parameter() def setPositions(self, positions): self.setParameter(positions) def setReadOnly(self, state): ParameterWidget.setReadOnly(self, state) if state: trigger = QTableView.EditTrigger.NoEditTriggers else: trigger = QTableView.EditTrigger.AllEditTriggers self._table.setEditTriggers(trigger) self._toolbar.setEnabled(not state) def isReadOnly(self): return ParameterWidget.isReadOnly(self) and \ self._table.editTriggers() == QTableView.EditTrigger.NoEditTriggers and \ not self._toolbar.isEnabled()
class WorkingDirectory(SpyderPluginWidget): """Working directory changer plugin.""" CONF_SECTION = 'workingdir' CONFIGWIDGET_CLASS = WorkingDirectoryConfigPage LOG_PATH = get_conf_path(CONF_SECTION) sig_option_changed = Signal(str, object) set_previous_enabled = Signal(bool) set_next_enabled = Signal(bool) redirect_stdio = Signal(bool) set_explorer_cwd = Signal(str) refresh_findinfiles = Signal() set_current_console_wd = Signal(str) def __init__(self, parent, workdir=None, **kwds): SpyderPluginWidget.__init__(self, parent) self.toolbar = QToolBar(self) # Initialize plugin self.initialize_plugin() self.toolbar.setWindowTitle(self.get_plugin_title()) # Used to save Window state self.toolbar.setObjectName(self.get_plugin_title()) # Previous dir action self.history = [] self.histindex = None self.previous_action = create_action(self, "previous", None, ima.icon('previous'), _('Back'), triggered=self.previous_directory) self.toolbar.addAction(self.previous_action) # Next dir action self.history = [] self.histindex = None self.next_action = create_action(self, "next", None, ima.icon('next'), _('Next'), triggered=self.next_directory) self.toolbar.addAction(self.next_action) # Enable/disable previous/next actions self.set_previous_enabled.connect(self.previous_action.setEnabled) self.set_next_enabled.connect(self.next_action.setEnabled) # Path combo box adjust = self.get_option('working_dir_adjusttocontents') self.pathedit = PathComboBox(self, adjust_to_contents=adjust) self.pathedit.setToolTip( _("This is the working directory for newly\n" "opened consoles (Python/IPython consoles and\n" "terminals), for the file explorer, for the\n" "find in files plugin and for new files\n" "created in the editor")) self.pathedit.open_dir.connect(self.chdir) self.pathedit.activated[str].connect(self.chdir) self.pathedit.setMaxCount(self.get_option('working_dir_history')) wdhistory = self.load_wdhistory(workdir) if workdir is None: if self.get_option('startup/use_last_directory'): if wdhistory: workdir = wdhistory[0] else: workdir = "." else: workdir = self.get_option('startup/fixed_directory', ".") if not osp.isdir(workdir): workdir = "." self.chdir(workdir) self.pathedit.addItems(wdhistory) self.pathedit.selected_text = self.pathedit.currentText() self.refresh_plugin() self.toolbar.addWidget(self.pathedit) # Browse action browse_action = create_action(self, "browse", None, ima.icon('DirOpenIcon'), _('Browse a working directory'), triggered=self.select_directory) self.toolbar.addAction(browse_action) # Parent dir action parent_action = create_action(self, "parent", None, ima.icon('up'), _('Change to parent directory'), triggered=self.parent_directory) self.toolbar.addAction(parent_action) #------ SpyderPluginWidget API --------------------------------------------- def get_plugin_title(self): """Return widget title""" return _('Global working directory') def get_plugin_icon(self): """Return widget icon""" return ima.icon('DirOpenIcon') def get_plugin_actions(self): """Setup actions""" return (None, None) def register_plugin(self): """Register plugin in Spyder's main window""" self.redirect_stdio.connect(self.main.redirect_internalshell_stdio) self.main.console.shell.refresh.connect(self.refresh_plugin) iconsize = 24 self.toolbar.setIconSize(QSize(iconsize, iconsize)) self.main.addToolBar(self.toolbar) def refresh_plugin(self): """Refresh widget""" curdir = getcwd() self.pathedit.add_text(curdir) self.save_wdhistory() self.set_previous_enabled.emit(self.histindex is not None and self.histindex > 0) self.set_next_enabled.emit(self.histindex is not None and \ self.histindex < len(self.history)-1) def apply_plugin_settings(self, options): """Apply configuration file's plugin settings""" pass def closing_plugin(self, cancelable=False): """Perform actions before parent main window is closed""" return True #------ Public API --------------------------------------------------------- def load_wdhistory(self, workdir=None): """Load history from a text file in user home directory""" if osp.isfile(self.LOG_PATH): wdhistory, _ = encoding.readlines(self.LOG_PATH) wdhistory = [name for name in wdhistory if os.path.isdir(name)] else: if workdir is None: workdir = get_home_dir() wdhistory = [workdir] return wdhistory def save_wdhistory(self): """Save history to a text file in user home directory""" text = [ to_text_string( self.pathedit.itemText(index) ) \ for index in range(self.pathedit.count()) ] encoding.writelines(text, self.LOG_PATH) @Slot() def select_directory(self): """Select directory""" self.redirect_stdio.emit(False) directory = getexistingdirectory(self.main, _("Select directory"), getcwd()) if directory: self.chdir(directory) self.redirect_stdio.emit(True) @Slot() def previous_directory(self): """Back to previous directory""" self.histindex -= 1 self.chdir(directory='', browsing_history=True) @Slot() def next_directory(self): """Return to next directory""" self.histindex += 1 self.chdir(directory='', browsing_history=True) @Slot() def parent_directory(self): """Change working directory to parent directory""" self.chdir(os.path.join(getcwd(), os.path.pardir)) @Slot(str) @Slot(str, bool) @Slot(str, bool, bool) def chdir(self, directory, browsing_history=False, refresh_explorer=True): """Set directory as working directory""" if directory: directory = osp.abspath(to_text_string(directory)) # Working directory history management if browsing_history: directory = self.history[self.histindex] elif directory in self.history: self.histindex = self.history.index(directory) else: if self.histindex is None: self.history = [] else: self.history = self.history[:self.histindex + 1] self.history.append(directory) self.histindex = len(self.history) - 1 # Changing working directory os.chdir(to_text_string(directory)) self.refresh_plugin() if refresh_explorer: self.set_explorer_cwd.emit(directory) self.set_as_current_console_wd() self.refresh_findinfiles.emit() def set_as_current_console_wd(self): """Set as current console working directory""" self.set_current_console_wd.emit(getcwd())
class CompositionElementalWidget(_CompositionWidget): class _CompositionModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self.composition = OrderedDict() def rowCount(self, *args, **kwargs): return len(self.composition) def columnCount(self, *args, **kwargs): return 2 def data(self, index, role): if not index.isValid() or \ not (0 <= index.row() < len(self.composition)): return None if role == Qt.TextAlignmentRole: return Qt.AlignCenter if role != Qt.DisplayRole: return None z, fraction = list(self.composition.items())[index.row()] column = index.column() if column == 0: if z is None: return 'none' else: return str(get_symbol(z)) elif column == 1: return str(fraction) def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'Element' elif section == 1: return 'Fraction' elif orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self.composition)): return False z = list(self.composition.keys())[index.row()] column = index.column() if column == 0: if value in self.composition: return False fraction = self.composition.pop(z) self.composition[value] = fraction elif column == 1: self.composition[z] = float(value) self.dataChanged.emit(index, index) return True def insertRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginInsertRows(parent, row, row + count - 1) if None in self.composition: return False self.composition[None] = 0.0 self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginRemoveRows(parent, row, count + row - 1) keys = list(self.composition.keys()) for key in keys[:row] + keys[row + count:]: self.composition.pop(key) self.endRemoveRows() return True class _CompositionDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): column = index.column() if column == 0: editor = PeriodicTableDialog(parent) editor.setMultipleSelection(False) editor.setRequiresSelection(True) return editor elif column == 1: editor = QLineEdit(parent) editor.setValidator(QDoubleValidator()) return editor else: return QItemDelegate.createEditor(self, parent, option, index) def setEditorData(self, editor, index): text = index.model().data(index, Qt.DisplayRole) column = index.column() if column == 0: if text != 'none': editor.setSelection(text) elif column == 1: editor.setText(text) else: QItemDelegate.setEditorData(self, editor, index) def setModelData(self, editor, model, index): column = index.column() if column == 0: model.setData(index, editor.selection()) elif column == 1: model.setData(index, editor.text()) else: return QItemDelegate.setModelData(self, editor, model, index) def __init__(self, parent=None): _CompositionWidget.__init__(self, CompositionElemental, parent) def _init_ui(self): # Widgets model = self._CompositionModel() self._table = QTableView() self._table.setModel(model) self._table.setItemDelegate(self._CompositionDelegate(self)) self._table.horizontalHeader().setStretchLastSection(True) self._toolbar = QToolBar() action_add = self._toolbar.addAction(getIcon("list-add"), "Add layer") action_remove = self._toolbar.addAction(getIcon("list-remove"), "Remove layer") # Layouts layout = _CompositionWidget._init_ui(self) layout.addRow(self._table) layout.addRow(self._toolbar) # Signals action_add.triggered.connect(self._on_add) action_remove.triggered.connect(self._on_remove) model.dataChanged.connect(self.edited) model.rowsInserted.connect(self.edited) model.rowsRemoved.connect(self.edited) return layout def _on_add(self): index = self._table.selectionModel().currentIndex() model = self._table.model() model.insertRows(index.row() + 1) def _on_remove(self): selection = self._table.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Window layer", "Select a layer") return model = self._table.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.removeRow(row) def _create_parameter(self): return self.CLASS('wt%') def parameter(self, parameter=None): parameter = _CompositionWidget.parameter(self, parameter) parameter.update(self._table.model().composition) return parameter def setParameter(self, condition): _CompositionWidget.setParameter(self, condition) self._table.model().composition.update(condition) self._table.model().reset() def setReadOnly(self, state): _CompositionWidget.setReadOnly(self, state) if state: trigger = QTableView.EditTrigger.NoEditTriggers else: trigger = QTableView.EditTrigger.AllEditTriggers self._table.setEditTriggers(trigger) self._toolbar.setEnabled(not state) def isReadOnly(self): return _CompositionWidget.isReadOnly(self) and \ self._table.editTriggers() == QTableView.EditTrigger.NoEditTriggers and \ not self._toolbar.isEnabled()
class DataBrowser(QWidget): sigOpen = Signal(NonDBHeader) sigPreview = Signal(NonDBHeader) def __init__(self, browserview): super(DataBrowser, self).__init__() hbox = QHBoxLayout() vbox = QVBoxLayout() vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) hbox.setContentsMargins(0, 0, 0, 0) hbox.setSpacing(0) self.setContentsMargins(0, 0, 0, 0) self.browserview = browserview self.browserview.sigOpen.connect(self.sigOpen) self.browserview.sigPreview.connect(self.sigPreview) self.browserview.sigOpenExternally.connect(self.openExternally) self.browserview.sigURIChanged.connect(self.uri_to_text) self.toolbar = QToolBar() self.toolbar.addAction(QIcon(QPixmap(str(path("icons/up.png")))), "Move up directory", self.moveUp) # self.toolbar.addAction(QIcon(QPixmap(str(path('icons/filter.png')))), 'Filter') self.toolbar.addAction(QIcon(QPixmap(str(path("icons/refresh.png")))), "Refresh", self.hardRefreshURI) self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) self.URILineEdit = SearchLineEdit("", clearable=False) self.uri_to_text() hbox.addWidget(self.toolbar) hbox.addWidget(self.URILineEdit) vbox.addLayout(hbox) vbox.addWidget(self.browserview) self.setLayout(vbox) self.URILineEdit.textChanged.connect(self.softRefreshURI) self.URILineEdit.returnPressed.connect(self.softRefreshURI) # hard refresh self.URILineEdit.focusOutEvent = self.softRefreshURI # hard refresh self.hardRefreshURI() def text_to_uri(self): uri = parse.urlparse(self.URILineEdit.text()) self.browserview.model().uri = uri print("uri:", uri) return uri def uri_to_text(self): uri = self.browserview.model().uri text = parse.urlunparse(uri) self.URILineEdit.setText(text) return text def hardRefreshURI(self, *_, **__): self.text_to_uri() self.browserview.refresh() def moveUp(self): self.browserview.model().uri = parse.urlparse(str(Path(self.URILineEdit.text()).parent)) self.browserview.refresh() self.uri_to_text() def openExternally(self, uri): webbrowser.open(uri) softRefreshURI = hardRefreshURI
def __init__(self, U, plot, length=1, title=None): super().__init__() layout = QVBoxLayout() if title: title = QLabel('<b>' + title + '</b>') title.setAlignment(Qt.AlignHCenter) layout.addWidget(title) layout.addWidget(plot) plot.set(U, 0) if length > 1: hlayout = QHBoxLayout() self.slider = QSlider(Qt.Horizontal) self.slider.setMinimum(0) self.slider.setMaximum(length - 1) self.slider.setTickPosition(QSlider.TicksBelow) hlayout.addWidget(self.slider) lcd = QLCDNumber(m.ceil(m.log10(length))) lcd.setDecMode() lcd.setSegmentStyle(QLCDNumber.Flat) hlayout.addWidget(lcd) layout.addLayout(hlayout) hlayout = QHBoxLayout() toolbar = QToolBar() self.a_play = QAction( self.style().standardIcon(QStyle.SP_MediaPlay), 'Play', self) self.a_play.setCheckable(True) self.a_rewind = QAction( self.style().standardIcon(QStyle.SP_MediaSeekBackward), 'Rewind', self) self.a_toend = QAction( self.style().standardIcon(QStyle.SP_MediaSeekForward), 'End', self) self.a_step_backward = QAction( self.style().standardIcon(QStyle.SP_MediaSkipBackward), 'Step Back', self) self.a_step_forward = QAction( self.style().standardIcon(QStyle.SP_MediaSkipForward), 'Step', self) self.a_loop = QAction( self.style().standardIcon(QStyle.SP_BrowserReload), 'Loop', self) self.a_loop.setCheckable(True) toolbar.addAction(self.a_play) toolbar.addAction(self.a_rewind) toolbar.addAction(self.a_toend) toolbar.addAction(self.a_step_backward) toolbar.addAction(self.a_step_forward) toolbar.addAction(self.a_loop) if hasattr(self, 'save'): self.a_save = QAction( self.style().standardIcon(QStyle.SP_DialogSaveButton), 'Save', self) toolbar.addAction(self.a_save) self.a_save.triggered.connect(self.save) hlayout.addWidget(toolbar) self.speed = QSlider(Qt.Horizontal) self.speed.setMinimum(0) self.speed.setMaximum(100) hlayout.addWidget(QLabel('Speed:')) hlayout.addWidget(self.speed) layout.addLayout(hlayout) self.timer = QTimer() self.timer.timeout.connect(self.update_solution) self.slider.valueChanged.connect(self.slider_changed) self.slider.valueChanged.connect(lcd.display) self.speed.valueChanged.connect(self.speed_changed) self.a_play.toggled.connect(self.toggle_play) self.a_rewind.triggered.connect(self.rewind) self.a_toend.triggered.connect(self.to_end) self.a_step_forward.triggered.connect(self.step_forward) self.a_step_backward.triggered.connect(self.step_backward) self.speed.setValue(50) elif hasattr(self, 'save'): hlayout = QHBoxLayout() toolbar = QToolBar() self.a_save = QAction( self.style().standardIcon(QStyle.SP_DialogSaveButton), 'Save', self) toolbar.addAction(self.a_save) hlayout.addWidget(toolbar) layout.addLayout(hlayout) self.a_save.triggered.connect(self.save) self.setLayout(layout) self.plot = plot self.U = U self.length = length
class UiLinelistsWindow(object): # this code was taken as-is from the Designer. # Cleaning it up sounds like a lower priority # task for now. def setupUi(self, MainWindow, title): MainWindow.setWindowTitle(title) MainWindow.setObjectName("MainWindow") MainWindow.resize(600, 850) MainWindow.setMinimumSize(QSize(300, 350)) self.centralWidget = QWidget(MainWindow) self.centralWidget.setObjectName("centralWidget") self.gridLayout = QGridLayout(self.centralWidget) self.gridLayout.setContentsMargins(11, 11, 11, 11) self.gridLayout.setSpacing(6) self.gridLayout.setObjectName("gridLayout") self.horizontalLayout_5 = QHBoxLayout() self.horizontalLayout_5.setContentsMargins(11, 11, 11, 11) self.horizontalLayout_5.setSpacing(6) self.horizontalLayout_5.setObjectName("horizontalLayout_5") self.lines_selected_label = QLabel(self.centralWidget) self.lines_selected_label.setObjectName("lines_selected_label") self.horizontalLayout_5.addWidget(self.lines_selected_label) self.label = QLabel(self.centralWidget) self.label.setObjectName("label") self.horizontalLayout_5.addWidget(self.label) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_5.addItem(spacerItem) self.draw_button = QPushButton(self.centralWidget) self.draw_button.setObjectName("draw_button") self.horizontalLayout_5.addWidget(self.draw_button) self.erase_button = QPushButton(self.centralWidget) self.erase_button.setObjectName("erase_button") self.horizontalLayout_5.addWidget(self.erase_button) self.dismiss_button = QPushButton(self.centralWidget) self.dismiss_button.setObjectName("dismiss_button") self.horizontalLayout_5.addWidget(self.dismiss_button) self.gridLayout.addLayout(self.horizontalLayout_5, 4, 0, 1, 1) self.verticalLayout_11 = QVBoxLayout() self.verticalLayout_11.setContentsMargins(11, 11, 11, 11) self.verticalLayout_11.setSpacing(6) self.verticalLayout_11.setObjectName("verticalLayout_11") self.tabWidget = QTabWidget(self.centralWidget) self.tabWidget.setObjectName("tabWidget") self.tabWidget.setTabsClosable(True) self.verticalLayout_11.addWidget(self.tabWidget) self.gridLayout.addLayout(self.verticalLayout_11, 0, 0, 1, 1) self.horizontalLayout_7 = QHBoxLayout() self.horizontalLayout_7.setContentsMargins(11, 11, 11, 11) self.horizontalLayout_7.setSpacing(6) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.horizontalLayout_7.addItem(spacerItem) self.horizontalLayout_7.setObjectName("horizontalLayout_7") self.gridLayout.addLayout(self.horizontalLayout_7, 2, 0, 2, 1) MainWindow.setCentralWidget(self.centralWidget) # self.menuBar = QMenuBar(MainWindow) # self.menuBar.setGeometry(QRect(0, 0, 767, 22)) # self.menuBar.setObjectName("menuBar") # # self.menuFile = QMenu(self.menuBar) # self.menuFile.setObjectName("menuFile") # # MainWindow.setMenuBar(self.menuBar) self.mainToolBar = QToolBar(MainWindow) self.mainToolBar.setMovable(False) self.mainToolBar.setFloatable(False) self.mainToolBar.setObjectName("mainToolBar") MainWindow.addToolBar(Qt.TopToolBarArea, self.mainToolBar) # self.statusBar = QStatusBar(MainWindow) # self.statusBar.setObjectName("statusBar") # MainWindow.setStatusBar(self.statusBar) self.actionOpen = QAction(MainWindow) icon = QIcon(os.path.join(ICON_PATH, "Open Folder-48.png")) self.actionOpen.setIcon(icon) self.actionOpen.setObjectName("actionOpen") self.actionExport = QAction(MainWindow) icon = QIcon(os.path.join(ICON_PATH, "Export-48.png")) self.actionExport.setIcon(icon) self.actionExport.setObjectName("actionExport") self.line_list_selector = QComboBox() self.line_list_selector.setToolTip( "Select line list from internal library") self.actionExit = QAction(MainWindow) self.actionExit.setObjectName("actionExit") self.actionRemove = QAction(MainWindow) self.actionRemove.setObjectName("actionRemove") self.actionChange_Color = QAction(MainWindow) self.actionChange_Color.setObjectName("actionChange_Color") # self.menuFile.addAction(self.actionOpen) # self.menuFile.addSeparator() # self.menuFile.addAction(self.actionExit) # self.menuBar.addAction(self.menuFile.menuAction()) self.mainToolBar.addAction(self.actionOpen) self.mainToolBar.addAction(self.actionExport) self.mainToolBar.addSeparator() self.mainToolBar.addWidget(self.line_list_selector) self.retranslateUi(MainWindow) QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QCoreApplication.translate self.lines_selected_label.setText(_translate("MainWindow", "0")) self.lines_selected_label.setToolTip( "Total number of lines selected in all sets.") self.label.setText(_translate("MainWindow", "lines selected")) self.label.setToolTip("Total number of lines selected in all sets.") self.draw_button.setText(_translate("MainWindow", "Draw")) self.draw_button.setToolTip( "Plot markers for all selected lines in all sets.") self.erase_button.setText(_translate("MainWindow", "Erase")) self.erase_button.setToolTip("Erase all markers") self.dismiss_button.setText(_translate("MainWindow", "Dismiss")) self.dismiss_button.setToolTip("Dismiss this window") # self.menuFile.setTitle(_translate("MainWindow", "File")) self.actionOpen.setText(_translate("MainWindow", "Open")) self.actionExport.setText( _translate("MainWindow", "Export plotted lines")) self.actionExit.setText(_translate("MainWindow", "Exit")) self.actionRemove.setText(_translate("MainWindow", "Remove")) self.actionRemove.setToolTip( _translate("MainWindow", "Removes the selected layer")) self.actionChange_Color.setText( _translate("MainWindow", "Change Color")) self.actionChange_Color.setToolTip( _translate("MainWindow", "Change the line color selected layer"))
class WorkflowWidget(QWidget): sigAddFunction = Signal(object) sigRunWorkflow = Signal(object) # TODO -- emit Workflow from sigRunWorkflow def __init__(self, workflowview: QAbstractItemView): super(WorkflowWidget, self).__init__() self.view = workflowview self.autorun_checkbox = QCheckBox("Run Automatically") self.autorun_checkbox.setCheckState(Qt.Unchecked) self.autorun_checkbox.stateChanged.connect(self._autorun_state_changed) self.run_button = QPushButton("Run Workflow") self.run_button.clicked.connect(self.sigRunWorkflow.emit) # TODO -- actually hook up the auto run OR dependent class needs to connect (see SAXSGUIPlugin) self.toolbar = QToolBar() self.addfunctionmenu = QToolButton() self.addfunctionmenu.setIcon(QIcon(path("icons/addfunction.png"))) self.addfunctionmenu.setText("Add Function") # Defer menu population to once the plugins have been loaded; otherwise, the menu may not contain anything # if this widget is init'd before all plugins have been loaded. self.functionmenu = QMenu() self.functionmenu.aboutToShow.connect(self.populateFunctionMenu) self.addfunctionmenu.setMenu(self.functionmenu) self.addfunctionmenu.setPopupMode(QToolButton.InstantPopup) self.toolbar.addWidget(self.addfunctionmenu) # self.toolbar.addAction(QIcon(path('icons/up.png')), 'Move Up') # self.toolbar.addAction(QIcon(path('icons/down.png')), 'Move Down') self.toolbar.addAction(QIcon(path("icons/folder.png")), "Load Workflow") self.toolbar.addAction(QIcon(path("icons/trash.png")), "Delete Operation", self.deleteOperation) v = QVBoxLayout() v.addWidget(self.view) h = QHBoxLayout() h.addWidget(self.autorun_checkbox) h.addWidget(self.run_button) v.addLayout(h) v.addWidget(self.toolbar) v.setContentsMargins(0, 0, 0, 0) self.setLayout(v) def _autorun_state_changed(self, state): if state == Qt.Checked: self.run_button.setDisabled(True) else: self.run_button.setDisabled(False) def _run_workflow(self, _): self._workflow def populateFunctionMenu(self): self.functionmenu.clear() sortingDict = {} for plugin in pluginmanager.get_plugins_of_type("OperationPlugin"): typeOfOperationPlugin = plugin.categories # TODO : should OperationPlugin be responsible for initializing categories # to some placeholder value (instead of [])? if typeOfOperationPlugin == []: typeOfOperationPlugin = "uncategorized" # put found operations into a default category if not typeOfOperationPlugin in sortingDict.keys(): sortingDict[typeOfOperationPlugin] = [] sortingDict[typeOfOperationPlugin].append(plugin) for key in sortingDict.keys(): self.functionmenu.addSeparator() self.functionmenu.addAction(key) self.functionmenu.addSeparator() for plugin in sortingDict[key]: self.functionmenu.addAction( plugin.name, partial(self.addOperation, plugin, autoconnectall=True)) def addOperation(self, operation: OperationPlugin, autoconnectall=True): self.view.model().workflow.add_operation(operation()) if autoconnectall: self.view.model().workflow.auto_connect_all() print("selected new row:", self.view.model().rowCount() - 1) self.view.setCurrentIndex(self.view.model().index( self.view.model().rowCount() - 1, 0)) def deleteOperation(self): for index in self.view.selectedIndexes(): operation = self.view.model().workflow.operations[index.row()] self.view.model().workflow.remove_operation(operation)
def _add_action(self, tool_bar: QToolBar, key: str, method: Any) -> QAction: action = QAction(key, self.app_window) action.triggered.connect(method) tool_bar.addAction(action) return action
class WindowWidget(ParameterWidget): class _WindowModel(QAbstractTableModel): def __init__(self): QAbstractTableModel.__init__(self) self.layers = [] def rowCount(self, *args, **kwargs): return len(self.layers) def columnCount(self, *args, **kwargs): return 2 def data(self, index, role): if not index.isValid() or not (0 <= index.row() < len(self.layers)): return None if role != Qt.DisplayRole: return None layer = self.layers[index.row()] column = index.column() if column == 0: return layer.material elif column == 1: return '%s' % layer.thickness def headerData(self, section , orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return 'Material' elif section == 1: return 'Thickness' elif orientation == Qt.Vertical: return str(section + 1) def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) def setData(self, index, value, role=Qt.EditRole): if not index.isValid() or \ not (0 <= index.row() < len(self.layers)): return False layer = self.layers[index.row()] column = index.column() if column == 0: layer.material = value elif column == 1: layer.thickness = value self.dataChanged.emit(index, index) return True def insertRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginInsertRows(parent, row, row + count - 1) for i in range(count): self.layers.insert(row + i, WindowLayer("unknown", 0.0)) self.endInsertRows() return True def removeRows(self, row, count=1, parent=None): if count == 0: return False if parent is None: parent = QModelIndex() self.beginRemoveRows(parent, row, row + count - 1) self.layers = self.layers[:row] + self.layers[row + count:] self.endRemoveRows() return True class _WindowDelegate(QItemDelegate): def __init__(self, parent=None): QItemDelegate.__init__(self, parent) def createEditor(self, parent, option, index): column = index.column() if column == 0: return TextAttributeLineEdit(WindowLayer.material, parent) elif column == 1: return NumericalAttributeLineEdit(WindowLayer.thickness, parent) else: return QItemDelegate.createEditor(self, parent, option, index) def setEditorData(self, editor, index): text = index.model().data(index, Qt.DisplayRole) column = index.column() if column == 0: editor.setText(text) elif column == 1: editor.setText(text) else: QItemDelegate.setEditorData(self, editor, index) def setModelData(self, editor, model, index): column = index.column() if column == 0: model.setData(index, editor.text()) elif column == 1: model.setData(index, editor.text()) else: return QItemDelegate.setModelData(self, editor, model, index) def __init__(self, parent=None): ParameterWidget.__init__(self, Window, parent) def _init_ui(self): # Widgets model = self._WindowModel() self._table = QTableView() self._table.setModel(model) self._table.setItemDelegate(self._WindowDelegate(self)) self._table.horizontalHeader().setStretchLastSection(True) self._toolbar = QToolBar() action_add = self._toolbar.addAction(getIcon("list-add"), "Add layer") action_remove = self._toolbar.addAction(getIcon("list-remove"), "Remove layer") # Layouts layout = ParameterWidget._init_ui(self) layout.addRow(self._table) layout.addRow(self._toolbar) # Signals action_add.triggered.connect(self._on_add) action_remove.triggered.connect(self._on_remove) model.dataChanged.connect(self.edited) model.rowsInserted.connect(self.edited) model.rowsRemoved.connect(self.edited) return layout def _on_add(self): index = self._table.selectionModel().currentIndex() model = self._table.model() model.insertRows(index.row() + 1) def _on_remove(self): selection = self._table.selectionModel().selection().indexes() if len(selection) == 0: QMessageBox.warning(self, "Window layer", "Select a layer") return model = self._table.model() for row in sorted(map(methodcaller('row'), selection), reverse=True): model.removeRow(row) def parameter(self, parameter=None): parameter = ParameterWidget.parameter(self, parameter) parameter.layers.clear() for layer in self._table.model().layers: parameter.append_layer(layer.material, layer.thickness) # copy return parameter def setParameter(self, window): model = self._table.model() model.layers = window.layers model.reset() def window(self): return self.parameter() def setWindow(self, window): self.setParameter(window) def setReadOnly(self, state): ParameterWidget.setReadOnly(self, state) if state: trigger = QTableView.EditTrigger.NoEditTriggers else: trigger = QTableView.EditTrigger.AllEditTriggers self._table.setEditTriggers(trigger) self._toolbar.setEnabled(not state) def isReadOnly(self): return ParameterWidget.isReadOnly(self) and \ self._table.editTriggers() == QTableView.EditTrigger.NoEditTriggers and \ not self._toolbar.isEnabled()