Exemple #1
0
    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)
Exemple #3
0
    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)
Exemple #4
0
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)
Exemple #6
0
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))
Exemple #7
0
    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)
Exemple #8
0
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)
Exemple #9
0
    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)
Exemple #10
0
    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)
Exemple #13
0
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())
Exemple #15
0
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()
Exemple #16
0
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())
Exemple #17
0
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)
Exemple #18
0
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)
Exemple #19
0
 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)
Exemple #21
0
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()
Exemple #22
0
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()
Exemple #23
0
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())
Exemple #24
0
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
Exemple #26
0
        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
Exemple #27
0
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)
Exemple #29
0
 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
Exemple #30
0
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()