示例#1
0
def run_widget(queue, window_type, name, topic, addr):

    app = QApplication(sys.argv)
    loop = asyncqt.QEventLoop(app)
    asyncio.set_event_loop(loop)
    win = QMainWindow()

    if window_type == 'AreaDetector':
        widget = AreaDetWidget(name, topic, addr, win)

    elif window_type == 'WaveformDetector':
        widget = WaveformWidget(name, topic, addr, win)

    elif window_type == 'HistogramDetector':
        widget = HistogramWidget(name, topic, addr, win)

    elif window_type == 'ScalarDetector':
        widget = ScalarWidget(name, topic, addr, win)

    else:
        raise ValueError('%s not valid window_type' % window_type)

    win.setCentralWidget(widget)
    win.setWindowTitle(name)
    win.show()

    return app.exec_()
示例#2
0
def create_window():
    # Create app and widgets
    app = QApplication(sys.argv)
    win = QMainWindow()
    main_area = QWidget()
    button_area = QWidget()
    scroll_area = QScrollArea()
    button = QPushButton("Start Video")
    btn_grab = QPushButton("Grab Frame")

    # Create layouts
    vbox = QVBoxLayout()
    hbox = QHBoxLayout()

    # Fill Layouts
    vbox.addWidget(scroll_area)
    vbox.addWidget(button_area)
    hbox.addStretch()
    hbox.addWidget(button)
    hbox.addWidget(btn_grab)

    # Assign layouts to widgets
    main_area.setLayout(vbox)
    button_area.setLayout(hbox)
    scroll_area.setLayout(QVBoxLayout())

    # Attach some child widgets directly
    win.setCentralWidget(main_area)

    return app, win, button, btn_grab, scroll_area
示例#3
0
def create_figure_window(title=''):
    """Creates a figure in a Qt window. Returns the tuple (window, mplfigure)"""
    win = QMainWindow()
    mplfig = MPLFigure()
    win.setCentralWidget(mplfig.canvas)
    win.setWindowTitle(title)
    return win, mplfig
示例#4
0
 def test_move_to_cords(self, pos, qtbot):
     window = QMainWindow()
     qtbot.addWidget(window)
     widget = QWidget()
     window.setCentralWidget(widget)
     popup = QtPopup(widget)
     popup.move_to(pos)
示例#5
0
def create_window():
    # Create app and widgets
    app = QApplication(sys.argv)
    win = QMainWindow()
    main_area = QWidget()
    button_area = QWidget()
    scroll_area = QScrollArea()
    button = QPushButton("Start Video")
    btn_grab = QPushButton("Grab Frame")

    # Create layouts
    vbox = QVBoxLayout()
    hbox = QHBoxLayout()

    # Fill Layouts
    vbox.addWidget(scroll_area)
    vbox.addWidget(button_area)
    hbox.addStretch()
    hbox.addWidget(button)
    hbox.addWidget(btn_grab)

    # Assign layouts to widgets
    main_area.setLayout(vbox)
    button_area.setLayout(hbox)
    scroll_area.setLayout(QVBoxLayout())

    # Attach some child widgets directly
    win.setCentralWidget(main_area)

    return app, win, button, btn_grab, scroll_area
示例#6
0
def launch_suite(suite: TyphosSuite,
                 initial_size: Optional[QSize] = None) -> QMainWindow:
    """
    Creates a main window and execs the application.

    Parameters
    ----------
    suite : TyphosSuite
        The suite that we'd like to launch.
    initial_size : QSize, optional
        If provided, the initial size for the full suite window.
        This can be useful when creating launcher scripts when
        the default window size isn't very good for that
        particular suite (e.g. flow layouts)

    Returns
    -------
    window : QMainWindow
        The window that we created. This will not be returned until
        after the application is done running. This is primarily
        useful for unit tests.
    """
    window = QMainWindow()
    window.setCentralWidget(suite)
    window.setWindowTitle(suite.windowTitle())
    window.setUnifiedTitleAndToolBarOnMac(True)
    if initial_size is not None:
        window.resize(initial_size)
    window.show()
    logger.info("Launching application ...")
    get_qapp().exec_()
    logger.info("Execution complete!")
    return window
示例#7
0
def create_figure_window(title=''):
    """Creates a figure in a Qt window. Returns the tuple (window, mplfigure)"""
    win = QMainWindow()
    mplfig = MPLFigure()
    win.setCentralWidget(mplfig.canvas)
    win.setWindowTitle(title)
    return win, mplfig
示例#8
0
def testsample():
    from spyder.utils.qthelpers import qapplication
    app = qapplication(test_time=5)
    win = QMainWindow(None)
    codewidget = MxCodeListWidget(win)
    win.setCentralWidget(codewidget)
    codewidget.setWidgetResizable(True)
    codewidget.setModel(CodeListModel(win, sampletexts))
    win.show()
    sys.exit(app.exec_())
示例#9
0
def start_viewer():
    matplotlib.use('Qt5Agg')
    _create_qApp()
    main_window = QMainWindow()
    viewer = Viewer()
    main_window.setCentralWidget(viewer)
    main_window.show()
    # Avoid letting main_window be garbage collected.
    viewer._main_window = main_window
    return viewer
示例#10
0
    def setupUi(self, mainWindow: QMainWindow):
        layout = QHBoxLayout()
        self.loginWidget = LoginWidget()
        self.chatWidget = ChatWidget()
        layout.addWidget(self.loginWidget)
        layout.addWidget(self.chatWidget)

        widget = QWidget()
        widget.setLayout(layout)
        mainWindow.setCentralWidget(widget)
示例#11
0
    def test_move_to_error_wrong_params(self, qtbot):
        window = QMainWindow()
        qtbot.addWidget(window)
        widget = QWidget()
        window.setCentralWidget(widget)
        popup = QtPopup(widget)
        with pytest.raises(ValueError):
            popup.move_to("dummy_text")

        with pytest.raises(ValueError):
            popup.move_to({})
示例#12
0
def launch_suite(suite):
    """Creates a main window and execs the application."""
    window = QMainWindow()
    window.setCentralWidget(suite)
    window.setWindowTitle(suite.windowTitle())
    window.setUnifiedTitleAndToolBarOnMac(True)
    window.show()
    logger.info("Launching application ...")
    get_qapp().exec_()
    logger.info("Execution complete!")
    return window
示例#13
0
def main():
    # create the application
    app = QApplication(sys.argv)
    # create the main window
    window = QMainWindow()
    # create a label to hold our text
    label = QLabel(text="hello world!", )
    # set the label to align in the center
    label.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
    # add our label to the central widget of the window
    window.setCentralWidget(label)
    # display the main window
    window.show()
    # start the Qt main loop execution
    sys.exit(app.exec_())
示例#14
0
class Application(QApplication):
    def __init__(self, argv):
        # call the parent (QApplication) constructor
        super().__init__(argv)
        # create the main window
        self.window = QMainWindow()
        # create a matplotlib widget and set the window as its parent
        self.mpl = QtMplWidget(self.window)
        # add our label to the central widget of the window
        self.window.setCentralWidget(self.mpl)
        # add a simple line (y = x^2) to the plot
        xdata = list(range(1, 100 + 1))
        ydata = [x**2 for x in xdata]
        self.mpl.axes.plot(xdata, ydata)
        # display the main window
        self.window.show()
示例#15
0
def main():
    # create the application
    app = QApplication(sys.argv)
    # create the main window
    window = QMainWindow()
    # create a matplotlib widget and set the window as its parent
    mpl = QtMplWidget(window)
    # set the plot as the central widget of the window
    window.setCentralWidget(mpl)
    # add a simple line (y = x^2) to the plot
    xdata = list(range(1, 100 + 1))
    ydata = [x ** 2 for x in xdata]
    mpl.axes.plot(xdata, ydata)
    # display the main window
    window.show()
    # start the Qt main loop execution
    sys.exit(app.exec_())
def main():
    # First, some boilerplate to make a super-minimal Qt application that we want
    # to add some bluesky-widgets components into.
    app = QApplication(["Some App"])
    window = QMainWindow()
    central_widget = QWidget(window)
    window.setCentralWidget(central_widget)
    central_widget.setLayout(QVBoxLayout())
    central_widget.layout().addWidget(
        QLabel("This is part of the 'original' app."))
    window.show()

    # *** INTEGRATION WITH BLUESKY-WIDGETS STARTS HERE. ***

    # Ensure that any background workers started by bluesky-widgets stop
    # gracefully when the application closes.
    from bluesky_widgets.qt.threading import wait_for_workers_to_quit

    app.aboutToQuit.connect(wait_for_workers_to_quit)

    # Get the catalog (must be databroker.v2-style).
    CATALOG_NAME = "example"
    import databroker

    catalog = databroker.catalog[CATALOG_NAME]
    # Create an instance of our model.
    search_model = SearchAndOpen(catalog, columns=columns)
    # Define what to do when the signal associated with the "Open" button fires.
    # In this toy example, just print to the terminal.
    search_model.events.open.connect(
        lambda event: print(f"Opening {event.selected_runs}"))
    # Create a Qt "view" of this model...
    search_view = QtSearchListWithButton(search_model)
    # ...and place it in our app.
    central_widget.layout().addWidget(search_view)

    # *** INTEGRATION WITH BLUESKY-WIDGETS ENDS HERE. ***

    # Run the app.
    app.exec_()
示例#17
0
class Application(QApplication):

    def __init__(self, argv):
        # call the parent (QApplication) constructor
        super().__init__(argv)
        # create the main window
        self.window = QMainWindow()
        # create a label to hold our text
        self.label = QLabel(text="Hello world!", )
        # set the label to align in the center
        self.label.setAlignment(
            QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter
        )
        # get the current font and make it a little bigger
        font = self.label.font()
        font.setPointSize(24)
        # reapply the font to the label
        self.label.setFont(font)
        # add our label to the central widget of the window
        self.window.setCentralWidget(self.label)
        # display the main window
        self.window.show()
示例#18
0
def test_widget(widget_class,
                size=None,
                title=None,
                options=True,
                timeout=1000):
    """Test widget"""
    widget_name = widget_class.__name__
    app = QApplication([])
    window = widget = widget_class()
    if options:
        if isinstance(widget, QMainWindow):
            widget = window.centralWidget()
            widget.setParent(None)
        else:
            window = QMainWindow()
        central_widget = TestCentralWidget(widget_name, parent=window)
        central_widget.add_widget(widget)
        window.setCentralWidget(central_widget)
        widget_of_interest = central_widget.widget_of_interest
    else:
        widget_of_interest = window
    widget_of_interest.setObjectName(widget_name)
    if title is None:
        from qwt import __version__

        title = 'Test "%s" - PythonQwt %s' % (widget_name, __version__)
    window.setWindowTitle(title)
    if size is not None:
        width, height = size
        window.resize(width, height)

    window.show()
    if os.environ.get("TEST_UNATTENDED") is not None:
        QTimer.singleShot(timeout, lambda: take_screenshot(widget_of_interest))
    app.exec_()
    return app
示例#19
0
class Window:
    """Application window that contains the menu bar and viewer.

    Parameters
    ----------
    qt_viewer : QtViewer
        Contained viewer widget.

    Attributes
    ----------
    file_menu : qtpy.QtWidgets.QMenu
        File menu.
    help_menu : qtpy.QtWidgets.QMenu
        Help menu.
    main_menu : qtpy.QtWidgets.QMainWindow.menuBar
        Main menubar.
    qt_viewer : QtViewer
        Contained viewer widget.
    view_menu : qtpy.QtWidgets.QMenu
        View menu.
    window_menu : qtpy.QtWidgets.QMenu
        Window menu.
    """

    raw_stylesheet = get_stylesheet()

    def __init__(self, qt_viewer: QtViewer, *, show: bool = True):

        self.qt_viewer = qt_viewer

        self._qt_window = QMainWindow()
        self._qt_window.setAttribute(Qt.WA_DeleteOnClose)
        self._qt_window.setUnifiedTitleAndToolBarOnMac(True)
        self._qt_center = QWidget(self._qt_window)

        self._qt_window.setCentralWidget(self._qt_center)
        self._qt_window.setWindowTitle(self.qt_viewer.viewer.title)
        self._qt_center.setLayout(QHBoxLayout())
        self._status_bar = QStatusBar()
        self._qt_window.setStatusBar(self._status_bar)

        self._add_menubar()

        self._add_file_menu()
        self._add_view_menu()
        self._add_window_menu()
        self._add_plugins_menu()
        self._add_help_menu()

        self._status_bar.showMessage('Ready')
        self._help = QLabel('')
        self._status_bar.addPermanentWidget(self._help)

        self._qt_center.layout().addWidget(self.qt_viewer)
        self._qt_center.layout().setContentsMargins(4, 0, 4, 0)

        self._update_palette()

        self._add_viewer_dock_widget(self.qt_viewer.dockConsole)
        self._add_viewer_dock_widget(self.qt_viewer.dockLayerControls)
        self._add_viewer_dock_widget(self.qt_viewer.dockLayerList)

        self.qt_viewer.viewer.events.status.connect(self._status_changed)
        self.qt_viewer.viewer.events.help.connect(self._help_changed)
        self.qt_viewer.viewer.events.title.connect(self._title_changed)
        self.qt_viewer.viewer.events.palette.connect(self._update_palette)

        if perf.USE_PERFMON:
            # Add DebugMenu if using perfmon. The DebugMenu is intended to
            # contain non-perfmon stuff as well. When it does we will want
            # a separate env variable for it.
            self._debug_menu = DebugMenu(self)

            # The QtPerformance widget only exists if we are using perfmon.
            self._add_viewer_dock_widget(self.qt_viewer.dockPerformance)
        else:
            self._debug_menu = None

        if show:
            self.show()

    def _add_menubar(self):
        """Add menubar to napari app."""
        self.main_menu = self._qt_window.menuBar()
        # Menubar shortcuts are only active when the menubar is visible.
        # Therefore, we set a global shortcut not associated with the menubar
        # to toggle visibility, *but*, in order to not shadow the menubar
        # shortcut, we disable it, and only enable it when the menubar is
        # hidden. See this stackoverflow link for details:
        # https://stackoverflow.com/questions/50537642/how-to-keep-the-shortcuts-of-a-hidden-widget-in-pyqt5
        self._main_menu_shortcut = QShortcut(QKeySequence('Ctrl+M'),
                                             self._qt_window)
        self._main_menu_shortcut.activated.connect(
            self._toggle_menubar_visible)
        self._main_menu_shortcut.setEnabled(False)

    def _toggle_menubar_visible(self):
        """Toggle visibility of app menubar.

        This function also disables or enables a global keyboard shortcut to
        show the menubar, since menubar shortcuts are only available while the
        menubar is visible.
        """
        if self.main_menu.isVisible():
            self.main_menu.setVisible(False)
            self._main_menu_shortcut.setEnabled(True)
        else:
            self.main_menu.setVisible(True)
            self._main_menu_shortcut.setEnabled(False)

    def _add_file_menu(self):
        """Add 'File' menu to app menubar."""
        open_images = QAction('Open File(s)...', self._qt_window)
        open_images.setShortcut('Ctrl+O')
        open_images.setStatusTip('Open file(s)')
        open_images.triggered.connect(self.qt_viewer._open_files_dialog)

        open_stack = QAction('Open Files as Stack...', self._qt_window)
        open_stack.setShortcut('Ctrl+Alt+O')
        open_stack.setStatusTip('Open files')
        open_stack.triggered.connect(
            self.qt_viewer._open_files_dialog_as_stack_dialog)

        open_folder = QAction('Open Folder...', self._qt_window)
        open_folder.setShortcut('Ctrl+Shift+O')
        open_folder.setStatusTip('Open a folder')
        open_folder.triggered.connect(self.qt_viewer._open_folder_dialog)

        save_selected_layers = QAction('Save Selected Layer(s)...',
                                       self._qt_window)
        save_selected_layers.setShortcut('Ctrl+S')
        save_selected_layers.setStatusTip('Save selected layers')
        save_selected_layers.triggered.connect(
            lambda: self.qt_viewer._save_layers_dialog(selected=True))

        save_all_layers = QAction('Save All Layers...', self._qt_window)
        save_all_layers.setShortcut('Ctrl+Shift+S')
        save_all_layers.setStatusTip('Save all layers')
        save_all_layers.triggered.connect(
            lambda: self.qt_viewer._save_layers_dialog(selected=False))

        screenshot = QAction('Save Screenshot...', self._qt_window)
        screenshot.setShortcut('Alt+S')
        screenshot.setStatusTip(
            'Save screenshot of current display, default .png')
        screenshot.triggered.connect(self.qt_viewer._screenshot_dialog)

        screenshot_wv = QAction('Save Screenshot with Viewer...',
                                self._qt_window)
        screenshot_wv.setShortcut('Alt+Shift+S')
        screenshot_wv.setStatusTip(
            'Save screenshot of current display with the viewer, default .png')
        screenshot_wv.triggered.connect(self._screenshot_dialog)

        # OS X will rename this to Quit and put it in the app menu.
        exitAction = QAction('Exit', self._qt_window)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setMenuRole(QAction.QuitRole)

        def handle_exit():
            # if the event loop was started in gui_qt() then the app will be
            # named 'napari'. Since the Qapp was started by us, just close it.
            if QApplication.applicationName() == 'napari':
                QApplication.closeAllWindows()
                QApplication.quit()
            # otherwise, something else created the QApp before us (such as
            # %gui qt IPython magic).  If we quit the app in this case, then
            # *later* attempts to instantiate a napari viewer won't work until
            # the event loop is restarted with app.exec_().  So rather than
            # quit just close all the windows (and clear our app icon).
            else:
                QApplication.setWindowIcon(QIcon())
                self.close()

            if perf.USE_PERFMON:
                # Write trace file before exit, if we were writing one.
                # Is there a better place to make sure this is done on exit?
                perf.timers.stop_trace_file()

        exitAction.triggered.connect(handle_exit)

        self.file_menu = self.main_menu.addMenu('&File')
        self.file_menu.addAction(open_images)
        self.file_menu.addAction(open_stack)
        self.file_menu.addAction(open_folder)
        self.file_menu.addSeparator()
        self.file_menu.addAction(save_selected_layers)
        self.file_menu.addAction(save_all_layers)
        self.file_menu.addAction(screenshot)
        self.file_menu.addAction(screenshot_wv)
        self.file_menu.addSeparator()
        self.file_menu.addAction(exitAction)

    def _add_view_menu(self):
        """Add 'View' menu to app menubar."""
        toggle_visible = QAction('Toggle Menubar Visibility', self._qt_window)
        toggle_visible.setShortcut('Ctrl+M')
        toggle_visible.setStatusTip('Hide Menubar')
        toggle_visible.triggered.connect(self._toggle_menubar_visible)
        toggle_theme = QAction('Toggle Theme', self._qt_window)
        toggle_theme.setShortcut('Ctrl+Shift+T')
        toggle_theme.setStatusTip('Toggle theme')
        toggle_theme.triggered.connect(self.qt_viewer.viewer._toggle_theme)
        self.view_menu = self.main_menu.addMenu('&View')
        self.view_menu.addAction(toggle_visible)
        self.view_menu.addAction(toggle_theme)

    def _add_window_menu(self):
        """Add 'Window' menu to app menubar."""
        exit_action = QAction("Close Window", self._qt_window)
        exit_action.setShortcut("Ctrl+W")
        exit_action.setStatusTip('Close napari window')
        exit_action.triggered.connect(self._qt_window.close)
        self.window_menu = self.main_menu.addMenu('&Window')
        self.window_menu.addAction(exit_action)

    def _add_plugins_menu(self):
        """Add 'Plugins' menu to app menubar."""
        self.plugins_menu = self.main_menu.addMenu('&Plugins')

        list_plugins_action = QAction("List Installed Plugins...",
                                      self._qt_window)
        list_plugins_action.setStatusTip('List installed plugins')
        list_plugins_action.triggered.connect(self._show_plugin_list)
        self.plugins_menu.addAction(list_plugins_action)

        order_plugin_action = QAction("Plugin Call Order...", self._qt_window)
        order_plugin_action.setStatusTip('Change call order for plugins')
        order_plugin_action.triggered.connect(self._show_plugin_sorter)
        self.plugins_menu.addAction(order_plugin_action)

        report_plugin_action = QAction("Plugin Errors...", self._qt_window)
        report_plugin_action.setStatusTip(
            'Review stack traces for plugin exceptions and notify developers')
        report_plugin_action.triggered.connect(self._show_plugin_err_reporter)
        self.plugins_menu.addAction(report_plugin_action)

    def _show_plugin_list(self, plugin_manager=None):
        """Show dialog with a table of installed plugins and metadata."""
        QtPluginTable(self._qt_window).exec_()

    def _show_plugin_sorter(self):
        """Show dialog that allows users to sort the call order of plugins."""
        plugin_sorter = QtPluginSorter(parent=self._qt_window)
        dock_widget = self.add_dock_widget(plugin_sorter,
                                           name='Plugin Sorter',
                                           area="right")
        plugin_sorter.finished.connect(dock_widget.close)
        plugin_sorter.finished.connect(plugin_sorter.deleteLater)
        plugin_sorter.finished.connect(dock_widget.deleteLater)

    def _show_plugin_err_reporter(self):
        """Show dialog that allows users to review and report plugin errors."""
        plugin_sorter = QtPluginErrReporter(parent=self._qt_window)
        plugin_sorter.exec_()

    def _add_help_menu(self):
        """Add 'Help' menu to app menubar."""
        self.help_menu = self.main_menu.addMenu('&Help')

        about_action = QAction("napari Info", self._qt_window)
        about_action.setShortcut("Ctrl+/")
        about_action.setStatusTip('About napari')
        about_action.triggered.connect(
            lambda e: QtAbout.showAbout(self.qt_viewer))
        self.help_menu.addAction(about_action)

        about_key_bindings = QAction("Show Key Bindings", self._qt_window)
        about_key_bindings.setShortcut("Ctrl+Alt+/")
        about_key_bindings.setShortcutContext(Qt.ApplicationShortcut)
        about_key_bindings.setStatusTip('key_bindings')
        about_key_bindings.triggered.connect(
            self.qt_viewer.show_key_bindings_dialog)
        self.help_menu.addAction(about_key_bindings)

    def add_dock_widget(
        self,
        widget: QWidget,
        *,
        name: str = '',
        area: str = 'bottom',
        allowed_areas=None,
        shortcut=None,
    ):
        """Convenience method to add a QDockWidget to the main window

        Parameters
        ----------
        widget : QWidget
            `widget` will be added as QDockWidget's main widget.
        name : str, optional
            Name of dock widget to appear in window menu.
        area : str
            Side of the main window to which the new dock widget will be added.
            Must be in {'left', 'right', 'top', 'bottom'}
        allowed_areas : list[str], optional
            Areas, relative to main window, that the widget is allowed dock.
            Each item in list must be in {'left', 'right', 'top', 'bottom'}
            By default, all areas are allowed.
        shortcut : str, optional
            Keyboard shortcut to appear in dropdown menu.

        Returns
        -------
        dock_widget : QtViewerDockWidget
            `dock_widget` that can pass viewer events.
        """

        dock_widget = QtViewerDockWidget(
            self.qt_viewer,
            widget,
            name=name,
            area=area,
            allowed_areas=allowed_areas,
            shortcut=shortcut,
        )
        self._add_viewer_dock_widget(dock_widget)
        return dock_widget

    def _add_viewer_dock_widget(self, dock_widget: QtViewerDockWidget):
        """Add a QtViewerDockWidget to the main window

        Parameters
        ----------
        dock_widget : QtViewerDockWidget
            `dock_widget` will be added to the main window.
        """
        dock_widget.setParent(self._qt_window)
        self._qt_window.addDockWidget(dock_widget.qt_area, dock_widget)
        action = dock_widget.toggleViewAction()
        action.setStatusTip(dock_widget.name)
        action.setText(dock_widget.name)
        if dock_widget.shortcut is not None:
            action.setShortcut(dock_widget.shortcut)
        self.window_menu.addAction(action)

    def remove_dock_widget(self, widget):
        """Removes specified dock widget.

        Parameters
        ----------
        widget : QWidget | str
            If widget == 'all', all docked widgets will be removed.
        """
        if widget == 'all':
            for dw in self._qt_window.findChildren(QDockWidget):
                self._qt_window.removeDockWidget(dw)
        else:
            self._qt_window.removeDockWidget(widget)

    def resize(self, width, height):
        """Resize the window.

        Parameters
        ----------
        width : int
            Width in logical pixels.
        height : int
            Height in logical pixels.
        """
        self._qt_window.resize(width, height)

    def show(self):
        """Resize, show, and bring forward the window."""
        self._qt_window.resize(self._qt_window.layout().sizeHint())
        self._qt_window.show()
        # Resize axis labels now that window is shown
        self.qt_viewer.dims._resize_axis_labels()

        # We want to call Window._qt_window.raise_() in every case *except*
        # when instantiating a viewer within a gui_qt() context for the
        # _first_ time within the Qt app's lifecycle.
        #
        # `app_name` will be "napari" iff the application was instantiated in
        # gui_qt(). isActiveWindow() will be True if it is the second time a
        # _qt_window has been created. See #732
        app_name = QApplication.instance().applicationName()
        if app_name != 'napari' or self._qt_window.isActiveWindow():
            self._qt_window.raise_()  # for macOS
            self._qt_window.activateWindow()  # for Windows

    def _update_palette(self, event=None):
        """Update widget color palette."""
        # set window styles which don't use the primary stylesheet
        # FIXME: this is a problem with the stylesheet not using properties
        palette = self.qt_viewer.viewer.palette
        self._status_bar.setStyleSheet(
            template(
                'QStatusBar { background: {{ background }}; '
                'color: {{ text }}; }',
                **palette,
            ))
        self._qt_center.setStyleSheet(
            template('QWidget { background: {{ background }}; }', **palette))
        self._qt_window.setStyleSheet(template(self.raw_stylesheet, **palette))

    def _status_changed(self, event):
        """Update status bar.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._status_bar.showMessage(event.text)

    def _title_changed(self, event):
        """Update window title.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._qt_window.setWindowTitle(event.text)

    def _help_changed(self, event):
        """Update help message on status bar.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._help.setText(event.text)

    def _screenshot_dialog(self):
        """Save screenshot of current display with viewer, default .png"""
        filename, _ = QFileDialog.getSaveFileName(
            parent=self.qt_viewer,
            caption='Save screenshot with viewer',
            directory=self.qt_viewer._last_visited_dir,  # home dir by default
            filter=
            "Image files (*.png *.bmp *.gif *.tif *.tiff)",  # first one used by default
            # jpg and jpeg not included as they don't support an alpha channel
        )
        if (filename != '') and (filename is not None):
            # double check that an appropriate extension has been added as the
            # filter option does not always add an extension on linux and windows
            # see https://bugreports.qt.io/browse/QTBUG-27186
            image_extensions = ('.bmp', '.gif', '.png', '.tif', '.tiff')
            if not filename.endswith(image_extensions):
                filename = filename + '.png'
            self.screenshot(path=filename)

    def screenshot(self, path=None):
        """Take currently displayed viewer and convert to an image array.

        Parameters
        ----------
        path : str
            Filename for saving screenshot image.

        Returns
        -------
        image : array
            Numpy array of type ubyte and shape (h, w, 4). Index [0, 0] is the
            upper-left corner of the rendered region.
        """
        img = self._qt_window.grab().toImage()
        if path is not None:
            imsave(path, QImg2array(img))  # scikit-image imsave method
        return QImg2array(img)

    def close(self):
        """Close the viewer window and cleanup sub-widgets."""
        # on some versions of Darwin, exiting while fullscreen seems to tickle
        # some bug deep in NSWindow.  This forces the fullscreen keybinding
        # test to complete its draw cycle, then pop back out of fullscreen.
        if self._qt_window.isFullScreen():
            self._qt_window.showNormal()
            for i in range(8):
                time.sleep(0.1)
                QApplication.processEvents()
        self.qt_viewer.close()
        self._qt_window.close()
        del self._qt_window
示例#20
0
        self.kernel_manager = QtInProcessKernelManager()
        self.kernel_manager.start_kernel()
        kernel = self.kernel_manager.kernel
        kernel.gui = "qt"

        # Push QWidget to the console
        kernel.shell.push({"widget": widget})

        self.kernel_client = self.kernel_manager.client()
        self.kernel_client.start_channels()

        # Setup console widget
        def stop():
            self.kernel_client.stop_channels()
            self.kernel_manager.shutdown_kernel()

        self.exit_requested.connect(stop)


if __name__ == "__main__":
    from qtpy.QtWidgets import QApplication, QMainWindow, QLabel

    app = QApplication([])
    window = QMainWindow()
    window.setCentralWidget(QLabel("test"))
    db = DebuggableMenuBar()
    window.setMenuBar(db)
    window.show()

    app.exec_()
示例#21
0
class TomographyPlugin(GUIPlugin):
    name = "Tomography"

    def __init__(self, *args, **kwargs):
        self.active_filename = ""

        self._main_view = QMainWindow()
        self._main_view.setCentralWidget(QWidget())
        self._main_view.centralWidget().hide()

        self._editors = QStackedWidget()

        self.workflow_process_selector = QComboBox()
        self.workflow_process_selector.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)

        self.workflow_process_selector.addItem("TomoPy Workflow")

        self._workflow = None

        self._tomo_workflow = TomographyWorkflow()  # Create a workflow
        self._workflow = self._tomo_workflow

        self._workflow_editor = WorkflowEditor(workflow=self._workflow)
        self._workflow_editor.sigRunWorkflow.connect(self.run_workflow)

        self._active_workflow_executor = QTreeWidget()

        self._editors.addWidget(self._workflow_editor)

        if hasLTT:
            self._alt_workflow = LTTWorkflow()
            self.workflow_process_selector.addItem("LTT Workflow")
            self._alt_workflow_editor = WorkflowEditor(self._alt_workflow)
            self._alt_workflow_editor.sigRunWorkflow.connect(self.run_workflow)
            self._editors.addWidget(self._alt_workflow_editor)

        self.workflow_process_selector.setCurrentIndex(0)
        self.workflow_process_selector.currentIndexChanged.connect(self.active_workflow_changed)

        self._active_workflow_widget = QWidget()
        self._active_layout = QVBoxLayout()

        self._active_layout.addWidget(self.workflow_process_selector)
        self._active_layout.addWidget(self._editors)
        self._active_layout.addWidget(self._active_workflow_executor)

        self._active_workflow_widget.setLayout(self._active_layout)

        # self.hdf5_viewer = HDFTreeViewer()
        # self.hdf5_viewer.load("/Users/hari/20200521_160002_heartbeat_test.h5")

        self.top_controls = QWidget()
        self.top_controls_layout = QHBoxLayout()
        self.top_controls.setLayout(self.top_controls_layout)
        self.top_controls_add_new_selector = QComboBox()
        self.top_controls_frequency_selector = QSpinBox()
        self.top_controls_frequency_selector.setValue(0)

        self.top_controls_add_new_viewer = QPushButton()
        self.top_controls_add_new_viewer.setText("Add Viewer")
        self.top_controls_add_new_viewer.clicked.connect(self.add_new_viewer_selected)

        self.top_controls_preview = QPushButton()
        self.top_controls_preview.setText("Preview")
        self.top_controls_preview.clicked.connect(self.preview_workflows)

        self.top_controls_execute = QPushButton()
        self.top_controls_execute.setText("Execute All")
        self.top_controls_execute.clicked.connect(self.execute_workflows)

        self.top_controls_add_new_selector.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.top_controls_add_new_viewer.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.top_controls_execute.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.top_controls_frequency_selector.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        self.top_controls_layout.addWidget(self.top_controls_add_new_selector)
        self.top_controls_layout.addWidget(self.top_controls_add_new_viewer)
        self.top_controls_layout.addWidget(self.top_controls_frequency_selector)
        self.top_controls_layout.addWidget(self.top_controls_preview)
        self.top_controls_layout.addWidget(self.top_controls_execute)

        self.top_controls.setLayout(self.top_controls_layout)

        self.status_bar = QLabel("None Loaded...")

        # Create a layout to organize our widgets
        # The first argument (which corresponds to the center widget) is required.
        catalog_viewer_layout = GUILayout(self._main_view,
                                          top=self.top_controls,
                                          right=self._active_workflow_widget,
                                          bottom=self.status_bar)

        # Create a "View" stage that has the catalog viewer layout
        self.stages = {"View": catalog_viewer_layout}

        self.active_workflows = []

        # For classes derived from GUIPlugin, this super __init__ must occur at end
        super(TomographyPlugin, self).__init__(*args, **kwargs)

    def active_workflow_changed(self, index):
        self._editors.setCurrentIndex(index)

        if index == 0:
            self._workflow = self._tomo_workflow
        else:
            self._workflow = self._alt_workflow

    def preview_workflows(self):
        if len(self.active_filename) == 0:
            return

        preview_sinogram = self.top_controls_frequency_selector.value()

        workflows = []
        for aw in range(self._active_workflow_executor.topLevelItemCount()):
            widget_item = self._active_workflow_executor.topLevelItem(aw)
            workflow = widget_item.data(0, Qt.UserRole)
            workflows.append(workflow)

        active_workflows = self.active_workflows

        for workflow in workflows:
            def results_ready(workflow, *results):
                output_image = results[0]["recon"]  # We want the output_image from the last operation
                # print("RECON SHAPE", output_image.shape)
                for active_workflow in active_workflows:
                    if active_workflow[1] == workflow:
                        active_workflow[0].widget().setImage(output_image)  # Update the result view widget
            workflow.execute(callback_slot=partial(results_ready, workflow), path=self.active_filename, sinoindex=preview_sinogram)

    def execute_workflows(self):
        if len(self.active_filename) == 0:
            return

        workflows = []
        for aw in range(self._active_workflow_executor.topLevelItemCount()):
            widget_item = self._active_workflow_executor.topLevelItem(aw)
            workflow = widget_item.data(0, Qt.UserRole)
            workflows.append(workflow)

        active_workflows = self.active_workflows

        for workflow in workflows:
            def results_ready(workflow, *results):
                output_image = results[0]["recon"]  # We want the output_image from the last operation
                # print("RECON SHAPE", output_image.shape)
                for active_workflow in active_workflows:
                    if active_workflow[1] == workflow:
                        active_workflow[0].widget().setImage(output_image)  # Update the result view widget
            workflow.execute(callback_slot=partial(results_ready, workflow), path=self.active_filename)

    def add_new_viewer_selected(self, checked):
        if self.top_controls_add_new_selector.count() == 0:
            return

        selected = self.top_controls_add_new_selector.currentIndex()

        if selected < 0:
            return

        workflow = self.top_controls_add_new_selector.currentData(Qt.UserRole)
        print("WORKFLOW", workflow, workflow.name)

        dock_widget = QDockWidget()
        viewer = CatalogView()
        dock_widget.setWidget(viewer)
        dock_widget.setWindowTitle(workflow.name)

        if len(self.active_workflows) == 0:
            self._main_view.addDockWidget(Qt.RightDockWidgetArea, dock_widget)
        else:
            self._main_view.tabifyDockWidget(self.active_workflows[-1][0], dock_widget)

        self.active_workflows.append((dock_widget, workflow))

    def appendHeader(self, header, **kwargs):
        filename = header._documents["start"][0]["filename"]
        self.active_filename = filename

        self.status_bar.setText("Loading Filename:" + filename)
        
        dataset, bkgs, drks = read_als_hdf5(filename)
        slice_widget = MyCatalogView()
        dw = QDockWidget()
        dw.setWidget(slice_widget)
        dw.setWindowTitle(filename)
        slice_widget.setImage(dataset)

        dock_children = self._main_view.findChildren(QDockWidget)
        print(dock_children)

        #if len(dock_children) == 0:
        #    self._main_view.addDockWidget(Qt.LeftDockWidgetArea, dw)
        #else:
        #    self._main_view.tabifyDockWidget(dock_children[0], dw)

        self._main_view.addDockWidget(Qt.LeftDockWidgetArea, dw)
        self.status_bar.setText("Done Loading Filename:" + filename)

    def appendCatalog(self, catalog, **kwargs):
        """Re-implemented from GUIPlugin - gives us access to a catalog reference
        You MUST implement this method if you want to load catalog data into your GUIPlugin.
        """
        # Set the catalog viewer's catalog, stream, and field (so it knows what to display)
        # This is a quick and simple demonstration; stream and field should NOT be hardcoded
        # stream = "primary"
        # field = "img"
        # self._catalog_viewer.setCatalog(catalog, stream, field)
        print("CATALOG CALLED", catalog)
        pass

    def run_workflow(self):
        """Run the internal workflowi.
        In this example, this will be called whenever the "Run Workflow" in the WorkflowEditor is clicked.
        """
        item = QTreeWidgetItem()

        workflow_name = self._workflow.name + ":" + str(self._active_workflow_executor.model().rowCount())
        workflow = self._workflow.clone()
        workflow.name = workflow_name
        workflow._pretty_print()

        item.setText(0, workflow_name)
        item.setData(0, Qt.UserRole, workflow)
        self._active_workflow_executor.addTopLevelItem(item)
        self.top_controls_add_new_selector.addItem(workflow_name, userData=workflow)
示例#22
0
from instrumental.gui import UDoubleSpinBox


class MyPowerSupply(Instrument):
    voltage = ManualFacet(type=float, units='volts')
    current = ManualFacet(type=float, units='amps')


if __name__ == '__main__':
    ps = MyPowerSupply()
    ps.observe('voltage', print)

    app = QApplication(sys.argv)
    win = QMainWindow()
    w = QWidget(win)
    win.setCentralWidget(w)
    fbox = QFormLayout()

    box1 = ps.facets.voltage.create_widget()
    fbox.addRow('Voltage', box1)
    box2 = ps.facets.current.create_widget()
    fbox.addRow('Current', box2)

    def set_box(event):
        box1.setUValue(event.new)
    ps.observe('voltage', set_box)

    def f():
        ps.voltage = '12 V'
    #box2.uValueChanged.connect(f)
示例#23
0
class Window:
    """Application window that contains the menu bar and viewer.

    Parameters
    ----------
    qt_viewer : QtViewer
        Contained viewer widget.

    Attributes
    ----------
    file_menu : qtpy.QtWidgets.QMenu
        File menu.
    help_menu : qtpy.QtWidgets.QMenu
        Help menu.
    main_menu : qtpy.QtWidgets.QMainWindow.menuBar
        Main menubar.
    qt_viewer : QtViewer
        Contained viewer widget.
    view_menu : qtpy.QtWidgets.QMenu
        View menu.
    window_menu : qtpy.QtWidgets.QMenu
        Window menu.
    """

    raw_stylesheet = combine_stylesheets()

    def __init__(self, qt_viewer, *, show=True):

        self.qt_viewer = qt_viewer

        self._qt_window = QMainWindow()
        self._qt_window.setAttribute(Qt.WA_DeleteOnClose)
        self._qt_window.setUnifiedTitleAndToolBarOnMac(True)
        self._qt_center = QWidget(self._qt_window)

        self._qt_window.setCentralWidget(self._qt_center)
        self._qt_window.setWindowTitle(self.qt_viewer.viewer.title)
        self._qt_center.setLayout(QHBoxLayout())
        self._status_bar = QStatusBar()
        self._qt_window.setStatusBar(self._status_bar)

        self._add_menubar()

        self._add_file_menu()
        self._add_view_menu()
        self._add_window_menu()
        self._add_help_menu()

        self._status_bar.showMessage('Ready')
        self._help = QLabel('')
        self._status_bar.addPermanentWidget(self._help)

        self._qt_center.layout().addWidget(self.qt_viewer)
        self._qt_center.layout().setContentsMargins(4, 0, 4, 0)

        self._update_palette(qt_viewer.viewer.palette)

        if self.qt_viewer.console.shell is not None:
            self._add_viewer_dock_widget(self.qt_viewer.dockConsole)

        self._add_viewer_dock_widget(self.qt_viewer.dockLayerControls)
        self._add_viewer_dock_widget(self.qt_viewer.dockLayerList)

        self.qt_viewer.viewer.events.status.connect(self._status_changed)
        self.qt_viewer.viewer.events.help.connect(self._help_changed)
        self.qt_viewer.viewer.events.title.connect(self._title_changed)
        self.qt_viewer.viewer.events.palette.connect(
            lambda event: self._update_palette(event.palette))

        if show:
            self.show()

    def _add_menubar(self):
        """Add menubar to napari app."""
        self.main_menu = self._qt_window.menuBar()
        # Menubar shortcuts are only active when the menubar is visible.
        # Therefore, we set a global shortcut not associated with the menubar
        # to toggle visibility, *but*, in order to not shadow the menubar
        # shortcut, we disable it, and only enable it when the menubar is
        # hidden. See this stackoverflow link for details:
        # https://stackoverflow.com/questions/50537642/how-to-keep-the-shortcuts-of-a-hidden-widget-in-pyqt5
        self._main_menu_shortcut = QShortcut(QKeySequence('Ctrl+M'),
                                             self._qt_window)
        self._main_menu_shortcut.activated.connect(
            self._toggle_menubar_visible)
        self._main_menu_shortcut.setEnabled(False)

    def _toggle_menubar_visible(self):
        """Toggle visibility of app menubar.

        This function also disables or enables a global keyboard shortcut to
        show the menubar, since menubar shortcuts are only available while the
        menubar is visible.
        """
        if self.main_menu.isVisible():
            self.main_menu.setVisible(False)
            self._main_menu_shortcut.setEnabled(True)
        else:
            self.main_menu.setVisible(True)
            self._main_menu_shortcut.setEnabled(False)

    def _add_file_menu(self):
        """Add 'File' menu to app menubar."""
        open_images = QAction('Open image(s)...', self._qt_window)
        open_images.setShortcut('Ctrl+O')
        open_images.setStatusTip('Open image file(s)')
        open_images.triggered.connect(self.qt_viewer._open_images)

        open_folder = QAction('Open Folder...', self._qt_window)
        open_folder.setShortcut('Ctrl+Shift+O')
        open_folder.setStatusTip(
            'Open a folder of image file(s) or a zarr file')
        open_folder.triggered.connect(self.qt_viewer._open_folder)

        screenshot = QAction('Screenshot', self._qt_window)
        screenshot.setShortcut('Ctrl+Alt+S')
        screenshot.setStatusTip(
            'Save screenshot of current display, default .png')
        screenshot.triggered.connect(self.qt_viewer._save_screenshot)

        self.file_menu = self.main_menu.addMenu('&File')
        self.file_menu.addAction(open_images)
        self.file_menu.addAction(open_folder)
        self.file_menu.addAction(screenshot)

    def _add_view_menu(self):
        """Add 'View' menu to app menubar."""
        toggle_visible = QAction('Toggle menubar visibility', self._qt_window)
        toggle_visible.setShortcut('Ctrl+M')
        toggle_visible.setStatusTip('Hide Menubar')
        toggle_visible.triggered.connect(self._toggle_menubar_visible)
        toggle_theme = QAction('Toggle theme', self._qt_window)
        toggle_theme.setShortcut('Ctrl+Shift+T')
        toggle_theme.setStatusTip('Toggle theme')
        toggle_theme.triggered.connect(self.qt_viewer.viewer._toggle_theme)
        self.view_menu = self.main_menu.addMenu('&View')
        self.view_menu.addAction(toggle_visible)
        self.view_menu.addAction(toggle_theme)

    def _add_window_menu(self):
        """Add 'Window' menu to app menubar."""
        exit_action = QAction("Close window", self._qt_window)
        exit_action.setShortcut("Ctrl+W")
        exit_action.setStatusTip('Close napari window')
        exit_action.triggered.connect(self._qt_window.close)
        self.window_menu = self.main_menu.addMenu('&Window')
        self.window_menu.addAction(exit_action)

    def _add_help_menu(self):
        """Add 'Help' menu to app menubar."""
        self.help_menu = self.main_menu.addMenu('&Help')

        about_action = QAction("napari info", self._qt_window)
        about_action.setShortcut("Ctrl+/")
        about_action.setStatusTip('About napari')
        about_action.triggered.connect(
            lambda e: QtAbout.showAbout(self.qt_viewer))
        self.help_menu.addAction(about_action)

        about_keybindings = QAction("keybindings", self._qt_window)
        about_keybindings.setShortcut("Ctrl+Alt+/")
        about_keybindings.setShortcutContext(Qt.ApplicationShortcut)
        about_keybindings.setStatusTip('keybindings')
        about_keybindings.triggered.connect(
            self.qt_viewer.show_keybindings_dialog)
        self.help_menu.addAction(about_keybindings)

    def add_dock_widget(
        self,
        widget: QWidget,
        *,
        name: str = '',
        area: str = 'bottom',
        allowed_areas=None,
        shortcut=None,
    ):
        """Convenience method to add a QDockWidget to the main window

        Parameters
        ----------
        widget : QWidget
            `widget` will be added as QDockWidget's main widget.
        name : str, optional
            Name of dock widget to appear in window menu.
        area : str
            Side of the main window to which the new dock widget will be added.
            Must be in {'left', 'right', 'top', 'bottom'}
        allowed_areas : list[str], optional
            Areas, relative to main window, that the widget is allowed dock.
            Each item in list must be in {'left', 'right', 'top', 'bottom'}
            By default, all areas are allowed.
        shortcut : str, optional
            Keyboard shortcut to appear in dropdown menu.

        Returns
        -------
        dock_widget : QtViewerDockWidget
            `dock_widget` that can pass viewer events.
        """

        dock_widget = QtViewerDockWidget(
            self.qt_viewer,
            widget,
            name=name,
            area=area,
            allowed_areas=allowed_areas,
            shortcut=shortcut,
        )
        self._add_viewer_dock_widget(dock_widget)
        return dock_widget

    def _add_viewer_dock_widget(self, dock_widget: QtViewerDockWidget):
        """Add a QtViewerDockWidget to the main window

        Parameters
        ----------
        dock_widget : QtViewerDockWidget
            `dock_widget` will be added to the main window.
        """
        dock_widget.setParent(self._qt_window)
        self._qt_window.addDockWidget(dock_widget.qt_area, dock_widget)
        action = dock_widget.toggleViewAction()
        action.setStatusTip(dock_widget.name)
        action.setText(dock_widget.name)
        if dock_widget.shortcut is not None:
            action.setShortcut(dock_widget.shortcut)
        self.window_menu.addAction(action)

    def remove_dock_widget(self, widget):
        """Removes specified dock widget.

        Parameters
        ----------
            widget : QWidget | str
                If widget == 'all', all docked widgets will be removed.
        """
        if widget == 'all':
            for dw in self._qt_window.findChildren(QDockWidget):
                self._qt_window.removeDockWidget(dw)
        else:
            self._qt_window.removeDockWidget(widget)

    def resize(self, width, height):
        """Resize the window.

        Parameters
        ----------
        width : int
            Width in logical pixels.
        height : int
            Height in logical pixels.
        """
        self._qt_window.resize(width, height)

    def show(self):
        """Resize, show, and bring forward the window."""
        self._qt_window.resize(self._qt_window.layout().sizeHint())
        self._qt_window.show()

        # We want to call Window._qt_window.raise_() in every case *except*
        # when instantiating a viewer within a gui_qt() context for the
        # _first_ time within the Qt app's lifecycle.
        #
        # `app_name` will be "napari" iff the application was instantiated in
        # gui_qt(). isActiveWindow() will be True if it is the second time a
        # _qt_window has been created. See #732
        app_name = QApplication.instance().applicationName()
        if app_name != 'napari' or self._qt_window.isActiveWindow():
            self._qt_window.raise_()  # for macOS
            self._qt_window.activateWindow()  # for Windows

    def _update_palette(self, palette):
        """Update widget color palette.

        Parameters
        ----------
        palette : qtpy.QtGui.QPalette
            Color palette for each widget state (Active, Disabled, Inactive).
        """
        # set window styles which don't use the primary stylesheet
        # FIXME: this is a problem with the stylesheet not using properties
        self._status_bar.setStyleSheet(
            template(
                'QStatusBar { background: {{ background }}; '
                'color: {{ text }}; }',
                **palette,
            ))
        self._qt_center.setStyleSheet(
            template('QWidget { background: {{ background }}; }', **palette))
        self._qt_window.setStyleSheet(template(self.raw_stylesheet, **palette))

    def _status_changed(self, event):
        """Update status bar.

        Parameters
        ----------
        event : qtpy.QtCore.QEvent
            Event from the Qt context.
        """
        self._status_bar.showMessage(event.text)

    def _title_changed(self, event):
        """Update window title.

        Parameters
        ----------
        event : qtpy.QtCore.QEvent
            Event from the Qt context.
        """
        self._qt_window.setWindowTitle(event.text)

    def _help_changed(self, event):
        """Update help message on status bar.

        Parameters
        ----------
        event : qtpy.QtCore.QEvent
            Event from the Qt context.
        """
        self._help.setText(event.text)

    def screenshot(self, path=None):
        """Take currently displayed viewer and convert to an image array.

        Parameters
        ----------
        path : str
            Filename for saving screenshot image.

        Returns
        -------
        image : array
            Numpy array of type ubyte and shape (h, w, 4). Index [0, 0] is the
            upper-left corner of the rendered region.
        """
        img = self._qt_window.grab().toImage()
        if path is not None:
            imsave(path, QImg2array(img))  # scikit-image imsave method
        return QImg2array(img)

    def close(self):
        """Close the viewer window and cleanup sub-widgets."""
        # on some versions of Darwin, exiting while fullscreen seems to tickle
        # some bug deep in NSWindow.  This forces the fullscreen keybinding
        # test to complete its draw cycle, then pop back out of fullscreen.
        if self._qt_window.isFullScreen():
            self._qt_window.showNormal()
            for i in range(8):
                time.sleep(0.1)
                QApplication.processEvents()
        self.qt_viewer.close()
        self._qt_window.close()
        del self._qt_window
示例#24
0
class Window:
    """Application window that contains the menu bar and viewer.

    Parameters
    ----------
    qt_widget : QtViewer
        Contained viewer widget.

    Attributes
    ----------
    file_menu : qtpy.QtWidgets.QMenu
        File menu.
    help_menu : qtpy.QtWidgets.QMenu
        Help menu.
    main_menu : qtpy.QtWidgets.QMainWindow.menuBar
        Main menubar.
    qt_widget : QtViewer
        Contained viewer widget.
    view_menu : qtpy.QtWidgets.QMenu
        View menu.
    window_menu : qtpy.QtWidgets.QMenu
        Window menu.
    """

    def __init__(self, qt_widget, *, show):

        self.qt_widget = qt_widget

        self._qt_window = QMainWindow()
        self._qt_window.setAttribute(Qt.WA_DeleteOnClose)
        self._qt_window.setUnifiedTitleAndToolBarOnMac(True)
        self._qt_center = QWidget(self._qt_window)

        self._qt_window.setCentralWidget(self._qt_center)
        if hasattr(self.qt_widget.model, "title"):
            self._qt_window.setWindowTitle(self.qt_widget.model.title)
        self._qt_center.setLayout(QHBoxLayout())
        self._status_bar = QStatusBar()
        self._qt_window.setStatusBar(self._status_bar)

        self._status_bar.showMessage("Ready")
        self._help = QLabel("")
        self._status_bar.addPermanentWidget(self._help)

        self._qt_center.layout().addWidget(self.qt_widget)
        self._qt_center.layout().setContentsMargins(4, 0, 4, 0)

        # self._add_viewer_dock_widget(self.qt_widget.dockConsole)
        # self._add_viewer_dock_widget(self.qt_widget.dockLayerControls)
        # self._add_viewer_dock_widget(self.qt_widget.dockLayerList)

        # self.qt_widget.viewer.events.status.connect(self._status_changed)
        # self.qt_widget.viewer.events.help.connect(self._help_changed)
        # self.qt_widget.viewer.events.title.connect(self._title_changed)
        # self.qt_widget.viewer.events.palette.connect(self._update_palette)

        if show:
            self.show()

    def resize(self, width, height):
        """Resize the window.

        Parameters
        ----------
        width : int
            Width in logical pixels.
        height : int
            Height in logical pixels.
        """
        self._qt_window.resize(width, height)

    def show(self):
        """Resize, show, and bring forward the window."""
        self._qt_window.resize(self._qt_window.layout().sizeHint())
        self._qt_window.show()

        # We want to call Window._qt_window.raise_() in every case *except*
        # when instantiating a viewer within a gui_qt() context for the
        # _first_ time within the Qt app's lifecycle.
        #
        # `app_name` will be ours iff the application was instantiated in
        # gui_qt(). isActiveWindow() will be True if it is the second time a
        # _qt_window has been created. See #732
        app_name = QApplication.instance().applicationName()
        if app_name != get_our_app_name() or self._qt_window.isActiveWindow():
            self._qt_window.raise_()  # for macOS
            self._qt_window.activateWindow()  # for Windows

    def _status_changed(self, event):
        """Update status bar.

        Parameters
        ----------
        event : qtpy.QtCore.QEvent
            Event from the Qt context.
        """
        self._status_bar.showMessage(event.text)

    def _title_changed(self, event):
        """Update window title.

        Parameters
        ----------
        event : qtpy.QtCore.QEvent
            Event from the Qt context.
        """
        self._qt_window.setWindowTitle(event.text)

    def _help_changed(self, event):
        """Update help message on status bar.

        Parameters
        ----------
        event : qtpy.QtCore.QEvent
            Event from the Qt context.
        """
        self._help.setText(event.text)

    def _screenshot_dialog(self):
        """Save screenshot of current display with viewer, default .png"""
        filename, _ = QFileDialog.getSaveFileName(
            parent=self.qt_widget,
            caption="Save screenshot with viewer",
            directory=self.qt_widget._last_visited_dir,  # home dir by default
            filter="Image files (*.png *.bmp *.gif *.tif *.tiff)",  # first one used by default
            # jpg and jpeg not included as they don't support an alpha channel
        )
        if (filename != "") and (filename is not None):
            # double check that an appropriate extension has been added as the
            # filter option does not always add an extension on linux and windows
            # see https://bugreports.qt.io/browse/QTBUG-27186
            image_extensions = (".bmp", ".gif", ".png", ".tif", ".tiff")
            if not filename.endswith(image_extensions):
                filename = filename + ".png"
            self.screenshot(path=filename)

    def screenshot(self, path=None):
        """Take currently displayed viewer and convert to an image array.

        Parameters
        ----------
        path : str
            Filename for saving screenshot image.

        Returns
        -------
        image : array
            Numpy array of type ubyte and shape (h, w, 4). Index [0, 0] is the
            upper-left corner of the rendered region.
        """
        img = self._qt_window.grab().toImage()
        if path is not None:
            from skimage.io import imsave
            from .utils import QImg2array  # noqa: E402

            imsave(path, QImg2array(img))
        return QImg2array(img)

    def close(self):
        """Close the viewer window and cleanup sub-widgets."""
        # on some versions of Darwin, exiting while fullscreen seems to tickle
        # some bug deep in NSWindow.  This forces the fullscreen keybinding
        # test to complete its draw cycle, then pop back out of fullscreen.
        if self._qt_window.isFullScreen():
            self._qt_window.showNormal()
            for i in range(8):
                time.sleep(0.1)
                QApplication.processEvents()
        self.qt_widget.close()
        self._qt_window.close()
        wait_for_workers_to_quit()
        del self._qt_window
示例#25
0
class Window:
    """Application window that contains the menu bar and viewer.

    Parameters
    ----------
    qt_viewer : QtViewer
        Contained viewer widget.

    Attributes
    ----------
    qt_viewer : QtViewer
        Contained viewer widget.
    """

    with open(os.path.join(resources_dir, 'stylesheet.qss'), 'r') as f:
        raw_stylesheet = f.read()

    def __init__(self, qt_viewer, *, show=True):

        self.qt_viewer = qt_viewer

        self._qt_window = QMainWindow()
        self._qt_window.setUnifiedTitleAndToolBarOnMac(True)
        self._qt_center = QWidget(self._qt_window)

        self._qt_window.setCentralWidget(self._qt_center)
        self._qt_window.setWindowTitle(self.qt_viewer.viewer.title)
        self._qt_center.setLayout(QHBoxLayout())
        self._status_bar = QStatusBar()
        self._qt_window.setStatusBar(self._status_bar)
        self._qt_window.closeEvent = self.closeEvent
        self.close = self._qt_window.close

        self._add_menubar()

        self._add_file_menu()
        self._add_view_menu()
        self._add_window_menu()
        self._add_help_menu()

        self._status_bar.showMessage('Ready')
        self._help = QLabel('')
        self._status_bar.addPermanentWidget(self._help)

        self._qt_center.layout().addWidget(self.qt_viewer)
        self._qt_center.layout().setContentsMargins(4, 0, 4, 0)

        self._update_palette(qt_viewer.viewer.palette)

        if self.qt_viewer.console.shell is not None:
            self._add_viewer_dock_widget(self.qt_viewer.dockConsole)

        self._add_viewer_dock_widget(self.qt_viewer.dockLayerControls)
        self._add_viewer_dock_widget(self.qt_viewer.dockLayerList)

        self.qt_viewer.viewer.events.status.connect(self._status_changed)
        self.qt_viewer.viewer.events.help.connect(self._help_changed)
        self.qt_viewer.viewer.events.title.connect(self._title_changed)
        self.qt_viewer.viewer.events.palette.connect(
            lambda event: self._update_palette(event.palette))

        if show:
            self.show()

    def _add_menubar(self):
        self.main_menu = self._qt_window.menuBar()
        # Menubar shortcuts are only active when the menubar is visible.
        # Therefore, we set a global shortcut not associated with the menubar
        # to toggle visibility, *but*, in order to not shadow the menubar
        # shortcut, we disable it, and only enable it when the menubar is
        # hidden. See this stackoverflow link for details:
        # https://stackoverflow.com/questions/50537642/how-to-keep-the-shortcuts-of-a-hidden-widget-in-pyqt5
        self._main_menu_shortcut = QShortcut(QKeySequence('Ctrl+M'),
                                             self._qt_window)
        self._main_menu_shortcut.activated.connect(
            self._toggle_menubar_visible)
        self._main_menu_shortcut.setEnabled(False)

    def _toggle_menubar_visible(self):
        """Toggle visibility of app menubar.

        This function also disables or enables a global keyboard shortcut to
        show the menubar, since menubar shortcuts are only available while the
        menubar is visible.
        """
        if self.main_menu.isVisible():
            self.main_menu.setVisible(False)
            self._main_menu_shortcut.setEnabled(True)
        else:
            self.main_menu.setVisible(True)
            self._main_menu_shortcut.setEnabled(False)

    def _add_file_menu(self):
        open_images = QAction('Open image(s)...', self._qt_window)
        open_images.setShortcut('Ctrl+O')
        open_images.setStatusTip('Open image file(s)')
        open_images.triggered.connect(self.qt_viewer._open_images)

        open_folder = QAction('Open Folder...', self._qt_window)
        open_folder.setShortcut('Ctrl-Shift-O')
        open_folder.setStatusTip(
            'Open a folder of image file(s) or a zarr file')
        open_folder.triggered.connect(self.qt_viewer._open_folder)

        self.file_menu = self.main_menu.addMenu('&File')
        self.file_menu.addAction(open_images)
        self.file_menu.addAction(open_folder)

    def _add_view_menu(self):
        toggle_visible = QAction('Toggle menubar visibility', self._qt_window)
        toggle_visible.setShortcut('Ctrl+M')
        toggle_visible.setStatusTip('Hide Menubar')
        toggle_visible.triggered.connect(self._toggle_menubar_visible)
        self.view_menu = self.main_menu.addMenu('&View')
        self.view_menu.addAction(toggle_visible)

    def _add_window_menu(self):
        exit_action = QAction("Close window", self._qt_window)
        exit_action.setShortcut("Ctrl+W")
        exit_action.setStatusTip('Close napari window')
        exit_action.triggered.connect(self._qt_window.close)
        self.window_menu = self.main_menu.addMenu('&Window')
        self.window_menu.addAction(exit_action)

    def _add_help_menu(self):
        self.help_menu = self.main_menu.addMenu('&Help')

        about_action = QAction("napari info", self._qt_window)
        about_action.setShortcut("Ctrl+/")
        about_action.setStatusTip('About napari')
        about_action.triggered.connect(
            lambda e: QtAbout.showAbout(self.qt_viewer))
        self.help_menu.addAction(about_action)

        about_keybindings = QAction("keybindings", self._qt_window)
        about_keybindings.setShortcut("Ctrl+Alt+/")
        about_keybindings.setShortcutContext(Qt.ApplicationShortcut)
        about_keybindings.setStatusTip('keybindings')
        about_keybindings.triggered.connect(
            self.qt_viewer.aboutKeybindings.toggle_visible)
        self.help_menu.addAction(about_keybindings)

    def add_dock_widget(
        self,
        widget: QWidget,
        *,
        name: str = '',
        area: str = 'bottom',
        allowed_areas=None,
        shortcut=None,
    ):
        """Convenience method to add a QDockWidget to the main window

        Parameters
        ----------
        widget : QWidget
            `widget` will be added as QDockWidget's main widget.
        name : str, optional
            Name of dock widget to appear in window menu.
        area : str
            Side of the main window to which the new dock widget will be added.
            Must be in {'left', 'right', 'top', 'bottom'}
        allowed_areas : list[str], optional
            Areas, relative to main window, that the widget is allowed dock.
            Each item in list must be in {'left', 'right', 'top', 'bottom'}
            By default, all areas are allowed.
        shortcut : str, optional
            Keyboard shortcut to appear in dropdown menu.

        Returns
        -------
        dock_widget : QtViewerDockWidget
            `dock_widget` that can pass viewer events.
        """

        dock_widget = QtViewerDockWidget(
            self.qt_viewer,
            widget,
            name=name,
            area=area,
            allowed_areas=allowed_areas,
            shortcut=shortcut,
        )
        self._add_viewer_dock_widget(dock_widget)
        return dock_widget

    def _add_viewer_dock_widget(self, dock_widget: QtViewerDockWidget):
        """Add a QtViewerDockWidget to the main window

        Parameters
        ----------
        dock_widget : QtViewerDockWidget
            `dock_widget` will be added to the main window.
        """
        dock_widget.setParent(self._qt_window)
        self._qt_window.addDockWidget(dock_widget.qt_area, dock_widget)
        action = dock_widget.toggleViewAction()
        action.setStatusTip(dock_widget.name)
        action.setText(dock_widget.name)
        if dock_widget.shortcut is not None:
            action.setShortcut(dock_widget.shortcut)
        self.window_menu.addAction(action)

    def remove_dock_widget(self, widget):
        """Removes specified dock widget.

        Parameters
        ----------
            widget : QWidget | str
                If widget == 'all', all docked widgets will be removed.
        """
        if widget == 'all':
            for dw in self._qt_window.findChildren(QDockWidget):
                self._qt_window.removeDockWidget(dw)
        else:
            self._qt_window.removeDockWidget(widget)

    def resize(self, width, height):
        """Resize the window.

        Parameters
        ----------
        width : int
            Width in logical pixels.
        height : int
            Height in logical pixels.
        """
        self._qt_window.resize(width, height)

    def show(self):
        """Resize, show, and bring forward the window."""
        self._qt_window.resize(self._qt_window.layout().sizeHint())
        self._qt_window.show()

        # We want to call Window._qt_window.raise_() in every case *except*
        # when instantiating a viewer within a gui_qt() context for the
        # _first_ time within the Qt app's lifecycle.
        #
        # `app_name` will be "napari" iff the application was instantiated in
        # gui_qt(). isActiveWindow() will be True if it is the second time a
        # _qt_window has been created. See #732
        app_name = QApplication.instance().applicationName()
        if app_name != 'napari' or self._qt_window.isActiveWindow():
            self._qt_window.raise_()  # for macOS
            self._qt_window.activateWindow()  # for Windows

    def _update_palette(self, palette):
        # set window styles which don't use the primary stylesheet
        # FIXME: this is a problem with the stylesheet not using properties
        self._status_bar.setStyleSheet(
            template(
                'QStatusBar { background: {{ background }}; '
                'color: {{ text }}; }',
                **palette,
            ))
        self._qt_center.setStyleSheet(
            template('QWidget { background: {{ background }}; }', **palette))
        self._qt_window.setStyleSheet(template(self.raw_stylesheet, **palette))

    def _status_changed(self, event):
        """Update status bar.
        """
        self._status_bar.showMessage(event.text)

    def _title_changed(self, event):
        """Update window title.
        """
        self._qt_window.setWindowTitle(event.text)

    def _help_changed(self, event):
        """Update help message on status bar.
        """
        self._help.setText(event.text)

    def screenshot(self):
        """Take currently displayed viewer and convert to an image array.

        Returns
        -------
        image : array
            Numpy array of type ubyte and shape (h, w, 4). Index [0, 0] is the
            upper-left corner of the rendered region.
        """
        img = self._qt_window.grab().toImage()
        return QImg2array(img)

    def closeEvent(self, event):
        # Forward close event to the console to trigger proper shutdown
        self.qt_viewer.console.shutdown()
        # if the viewer.QtDims object is playing an axis, we need to terminate the
        # AnimationThread before close, otherwise it will cauyse a segFault or Abort trap.
        # (calling stop() when no animation is occuring is also not a problem)
        self.qt_viewer.dims.stop()
        event.accept()
示例#26
0
class FittingPlotView(QtWidgets.QWidget, Ui_plot):
    def __init__(self, parent=None):
        super(FittingPlotView, self).__init__(parent)
        self.setupUi(self)

        self.figure = None
        self.toolbar = None
        self.fitprop_toolbar = None
        self.fit_browser = None
        self.plot_dock = None
        self.dock_window = None

        self.setup_figure()
        self.setup_toolbar()

    def setup_figure(self):
        self.figure = Figure()
        self.figure.canvas = FigureCanvas(self.figure)
        self.figure.canvas.mpl_connect('button_press_event', self.mouse_click)
        self.figure.add_subplot(111, projection="mantid")
        self.figure.tight_layout()
        self.toolbar = FittingPlotToolbar(self.figure.canvas, self, False)
        self.toolbar.setMovable(False)

        self.dock_window = QMainWindow(self.group_plot)
        self.dock_window.setWindowFlags(Qt.Widget)
        self.dock_window.setDockOptions(QMainWindow.AnimatedDocks)
        self.dock_window.setCentralWidget(self.toolbar)
        self.plot_dock = QDockWidget()
        self.plot_dock.setWidget(self.figure.canvas)
        self.plot_dock.setFeatures(QDockWidget.DockWidgetFloatable
                                   | QDockWidget.DockWidgetMovable)
        self.plot_dock.setAllowedAreas(Qt.BottomDockWidgetArea)
        self.plot_dock.setWindowTitle("Fit Plot")
        self.dock_window.addDockWidget(Qt.BottomDockWidgetArea, self.plot_dock)
        self.vLayout_plot.addWidget(self.dock_window)

        self.fit_browser = EngDiffFitPropertyBrowser(
            self.figure.canvas, ToolbarStateManager(self.toolbar))
        # remove SequentialFit from fit menu (implemented a different way)
        qmenu = self.fit_browser.getFitMenu()
        qmenu.removeAction([
            qact for qact in qmenu.actions() if qact.text() == "Sequential Fit"
        ][0])
        # hide unnecessary properties of browser
        hide_props = [
            'StartX', 'EndX', 'Minimizer', 'Cost function', 'Max Iterations',
            'Output', 'Ignore invalid data', 'Peak Radius',
            'Plot Composite Members', 'Convolve Composite Members',
            'Show Parameter Errors', 'Evaluate Function As'
        ]
        self.fit_browser.removePropertiesFromSettingsBrowser(hide_props)
        self.fit_browser.toggleWsListVisible()
        self.fit_browser.closing.connect(self.toolbar.handle_fit_browser_close)
        self.vLayout_fitprop.addWidget(self.fit_browser)
        self.fit_browser.hide()

    def mouse_click(self, event):
        if event.button == event.canvas.buttond.get(Qt.RightButton):
            menu = QMenu()
            self.fit_browser.add_to_menu(menu)
            menu.exec_(QCursor.pos())

    def resizeEvent(self, QResizeEvent):
        self.figure.tight_layout()

    def ensure_fit_dock_closed(self):
        if self.plot_dock.isFloating():
            self.plot_dock.close()

    def setup_toolbar(self):
        self.toolbar.sig_home_clicked.connect(self.display_all)
        self.toolbar.sig_toggle_fit_triggered.connect(self.fit_toggle)

    # =================
    # Component Setters
    # =================

    def fit_toggle(self):
        """Toggle fit browser and tool on/off"""
        if self.fit_browser.isVisible():
            self.fit_browser.hide()
        else:
            self.fit_browser.show()

    def clear_figure(self):
        self.figure.clf()
        self.figure.add_subplot(111, projection="mantid")
        self.figure.tight_layout()
        self.figure.canvas.draw()

    def update_figure(self):
        self.toolbar.update()
        self.figure.tight_layout()
        self.update_legend(self.get_axes()[0])
        self.figure.canvas.draw()
        self.update_fitbrowser()

    def update_fitbrowser(self):
        is_visible = self.fit_browser.isVisible()
        self.toolbar.set_fit_checkstate(False)
        self.fit_browser.hide()
        if self.fit_browser.getWorkspaceNames() and is_visible:
            self.toolbar.set_fit_checkstate(True)
            self.fit_browser.show()

    def remove_ws_from_fitbrowser(self, ws):
        # only one spectra per workspace
        self.fit_browser.removeWorkspaceAndSpectra(ws.name())

    def update_legend(self, ax):
        if ax.get_lines():
            ax.make_legend()
            ax.get_legend().set_title("")
        else:
            if ax.get_legend():
                ax.get_legend().remove()

    def display_all(self):
        for ax in self.get_axes():
            if ax.lines:
                ax.relim()
            ax.autoscale()
        self.update_figure()

    def read_fitprop_from_browser(self):
        return self.fit_browser.read_current_fitprop()

    def update_browser(self, status, func_str, setup_name):
        self.fit_browser.fitResultsChanged.emit(status)
        self.fit_browser.changeWindowTitle.emit(status)
        # update browser with output function and save setup if successful
        if "success" in status.lower():
            self.fit_browser.loadFunction(func_str)
            self.fit_browser.save_current_setup(setup_name)

    # =================
    # Component Getters
    # =================

    def get_axes(self):
        return self.figure.axes

    def get_figure(self):
        return self.figure
示例#27
0
class Window:
    """Application window that contains the menu bar and viewer.

    Parameters
    ----------
    viewer : napari.components.ViewerModel
        Contained viewer widget.

    Attributes
    ----------
    file_menu : qtpy.QtWidgets.QMenu
        File menu.
    help_menu : qtpy.QtWidgets.QMenu
        Help menu.
    main_menu : qtpy.QtWidgets.QMainWindow.menuBar
        Main menubar.
    qt_viewer : QtViewer
        Contained viewer widget.
    view_menu : qtpy.QtWidgets.QMenu
        View menu.
    window_menu : qtpy.QtWidgets.QMenu
        Window menu.
    """

    raw_stylesheet = get_stylesheet()

    def __init__(self, viewer, *, show: bool = True):

        # Check there is a running app
        # instance() returns the singleton instance if it exists, or None
        app = QApplication.instance()
        # if None, raise a RuntimeError with the appropriate message
        if app is None:
            message = (
                "napari requires a Qt event loop to run. To create one, "
                "try one of the following: \n"
                "  - use the `napari.gui_qt()` context manager. See "
                "https://github.com/napari/napari/tree/master/examples for"
                " usage examples.\n"
                "  - In IPython or a local Jupyter instance, use the "
                "`%gui qt` magic command.\n"
                "  - Launch IPython with the option `--gui=qt`.\n"
                "  - (recommended) in your IPython configuration file, add"
                " or uncomment the line `c.TerminalIPythonApp.gui = 'qt'`."
                " Then, restart IPython."
            )
            raise RuntimeError(message)

        if perf_config:
            if perf_config.trace_qt_events:
                from .tracing.qt_event_tracing import convert_app_for_tracing

                # For tracing Qt events we need a special QApplication. If
                # using `gui_qt` we already have the special one, and no
                # conversion is done here. However when running inside
                # IPython or Jupyter this is where we switch out the
                # QApplication.
                app = convert_app_for_tracing(app)

            # Will patch based on config file.
            perf_config.patch_callables()
        _napari_app_id = getattr(
            viewer,
            "_napari_app_id",
            'napari.napari.viewer.' + str(__version__),
        )
        if (
            platform.system() == "Windows"
            and not getattr(sys, 'frozen', False)
            and _napari_app_id
        ):
            import ctypes

            ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
                _napari_app_id
            )

        logopath = os.path.join(
            os.path.dirname(__file__), '..', 'resources', 'logo.png'
        )

        if getattr(viewer, "_napari_global_logo", True):
            app = QApplication.instance()
            app.setWindowIcon(QIcon(logopath))

        # see docstring of `wait_for_workers_to_quit` for caveats on killing
        # workers at shutdown.
        app.aboutToQuit.connect(wait_for_workers_to_quit)

        # Connect the Viewer and create the Main Window
        self.qt_viewer = QtViewer(viewer)

        self._qt_window = QMainWindow()
        self._qt_window.setWindowIcon(QIcon(logopath))
        self._qt_window.setAttribute(Qt.WA_DeleteOnClose)
        self._qt_window.setUnifiedTitleAndToolBarOnMac(True)

        # since we initialize canvas before window, we need to manually connect them again.
        if self._qt_window.windowHandle() is not None:
            self._qt_window.windowHandle().screenChanged.connect(
                self.qt_viewer.canvas._backend.screen_changed
            )
        self._qt_center = QWidget(self._qt_window)

        self._qt_window.setCentralWidget(self._qt_center)
        self._qt_window.setWindowTitle(self.qt_viewer.viewer.title)
        self._qt_center.setLayout(QHBoxLayout())
        self._status_bar = QStatusBar()
        self._qt_window.setStatusBar(self._status_bar)

        self._add_menubar()

        self._add_file_menu()
        self._add_view_menu()
        self._add_window_menu()
        if not os.getenv("DISABLE_ALL_PLUGINS"):
            self._add_plugins_menu()
        self._add_help_menu()

        self._status_bar.showMessage('Ready')
        self._help = QLabel('')
        self._status_bar.addPermanentWidget(self._help)

        self._qt_center.layout().addWidget(self.qt_viewer)
        self._qt_center.layout().setContentsMargins(4, 0, 4, 0)

        self._update_palette()

        self._add_viewer_dock_widget(self.qt_viewer.dockConsole)
        self._add_viewer_dock_widget(self.qt_viewer.dockLayerControls)
        self._add_viewer_dock_widget(self.qt_viewer.dockLayerList)

        self.qt_viewer.viewer.events.status.connect(self._status_changed)
        self.qt_viewer.viewer.events.help.connect(self._help_changed)
        self.qt_viewer.viewer.events.title.connect(self._title_changed)
        self.qt_viewer.viewer.events.palette.connect(self._update_palette)

        if perf.USE_PERFMON:
            # Add DebugMenu and dockPerformance if using perfmon.
            self._debug_menu = DebugMenu(self)
            self._add_viewer_dock_widget(self.qt_viewer.dockPerformance)
        else:
            self._debug_menu = None

        if show:
            self.show()

    def _add_menubar(self):
        """Add menubar to napari app."""
        self.main_menu = self._qt_window.menuBar()
        # Menubar shortcuts are only active when the menubar is visible.
        # Therefore, we set a global shortcut not associated with the menubar
        # to toggle visibility, *but*, in order to not shadow the menubar
        # shortcut, we disable it, and only enable it when the menubar is
        # hidden. See this stackoverflow link for details:
        # https://stackoverflow.com/questions/50537642/how-to-keep-the-shortcuts-of-a-hidden-widget-in-pyqt5
        self._main_menu_shortcut = QShortcut(
            QKeySequence('Ctrl+M'), self._qt_window
        )
        self._main_menu_shortcut.activated.connect(
            self._toggle_menubar_visible
        )
        self._main_menu_shortcut.setEnabled(False)

    def _toggle_menubar_visible(self):
        """Toggle visibility of app menubar.

        This function also disables or enables a global keyboard shortcut to
        show the menubar, since menubar shortcuts are only available while the
        menubar is visible.
        """
        if self.main_menu.isVisible():
            self.main_menu.setVisible(False)
            self._main_menu_shortcut.setEnabled(True)
        else:
            self.main_menu.setVisible(True)
            self._main_menu_shortcut.setEnabled(False)

    def _add_file_menu(self):
        """Add 'File' menu to app menubar."""
        open_images = QAction('Open File(s)...', self._qt_window)
        open_images.setShortcut('Ctrl+O')
        open_images.setStatusTip('Open file(s)')
        open_images.triggered.connect(self.qt_viewer._open_files_dialog)

        open_stack = QAction('Open Files as Stack...', self._qt_window)
        open_stack.setShortcut('Ctrl+Alt+O')
        open_stack.setStatusTip('Open files')
        open_stack.triggered.connect(
            self.qt_viewer._open_files_dialog_as_stack_dialog
        )

        open_folder = QAction('Open Folder...', self._qt_window)
        open_folder.setShortcut('Ctrl+Shift+O')
        open_folder.setStatusTip('Open a folder')
        open_folder.triggered.connect(self.qt_viewer._open_folder_dialog)

        save_selected_layers = QAction(
            'Save Selected Layer(s)...', self._qt_window
        )
        save_selected_layers.setShortcut('Ctrl+S')
        save_selected_layers.setStatusTip('Save selected layers')
        save_selected_layers.triggered.connect(
            lambda: self.qt_viewer._save_layers_dialog(selected=True)
        )

        save_all_layers = QAction('Save All Layers...', self._qt_window)
        save_all_layers.setShortcut('Ctrl+Shift+S')
        save_all_layers.setStatusTip('Save all layers')
        save_all_layers.triggered.connect(
            lambda: self.qt_viewer._save_layers_dialog(selected=False)
        )

        screenshot = QAction('Save Screenshot...', self._qt_window)
        screenshot.setShortcut('Alt+S')
        screenshot.setStatusTip(
            'Save screenshot of current display, default .png'
        )
        screenshot.triggered.connect(self.qt_viewer._screenshot_dialog)

        screenshot_wv = QAction(
            'Save Screenshot with Viewer...', self._qt_window
        )
        screenshot_wv.setShortcut('Alt+Shift+S')
        screenshot_wv.setStatusTip(
            'Save screenshot of current display with the viewer, default .png'
        )
        screenshot_wv.triggered.connect(self._screenshot_dialog)

        # OS X will rename this to Quit and put it in the app menu.
        exitAction = QAction('Exit', self._qt_window)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setMenuRole(QAction.QuitRole)

        def handle_exit():
            # if the event loop was started in gui_qt() then the app will be
            # named 'napari'. Since the Qapp was started by us, just close it.
            if QApplication.applicationName() == 'napari':
                QApplication.closeAllWindows()
                QApplication.quit()
            # otherwise, something else created the QApp before us (such as
            # %gui qt IPython magic).  If we quit the app in this case, then
            # *later* attempts to instantiate a napari viewer won't work until
            # the event loop is restarted with app.exec_().  So rather than
            # quit just close all the windows (and clear our app icon).
            else:
                QApplication.setWindowIcon(QIcon())
                self.close()

            if perf.USE_PERFMON:
                # Write trace file before exit, if we were writing one.
                # Is there a better place to make sure this is done on exit?
                perf.timers.stop_trace_file()

            _stop_monitor()

        exitAction.triggered.connect(handle_exit)

        self.file_menu = self.main_menu.addMenu('&File')
        self.file_menu.addAction(open_images)
        self.file_menu.addAction(open_stack)
        self.file_menu.addAction(open_folder)
        self.file_menu.addSeparator()
        self.file_menu.addAction(save_selected_layers)
        self.file_menu.addAction(save_all_layers)
        self.file_menu.addAction(screenshot)
        self.file_menu.addAction(screenshot_wv)
        self.file_menu.addSeparator()
        self.file_menu.addAction(exitAction)

    def _add_view_menu(self):
        """Add 'View' menu to app menubar."""
        toggle_visible = QAction('Toggle Menubar Visibility', self._qt_window)
        toggle_visible.setShortcut('Ctrl+M')
        toggle_visible.setStatusTip('Hide Menubar')
        toggle_visible.triggered.connect(self._toggle_menubar_visible)
        toggle_theme = QAction('Toggle Theme', self._qt_window)
        toggle_theme.setShortcut('Ctrl+Shift+T')
        toggle_theme.setStatusTip('Toggle theme')
        toggle_theme.triggered.connect(self.qt_viewer.viewer._toggle_theme)
        toggle_fullscreen = QAction('Toggle Full Screen', self._qt_window)
        toggle_fullscreen.setShortcut('Ctrl+F')
        toggle_fullscreen.setStatusTip('Toggle full screen')
        toggle_fullscreen.triggered.connect(self._toggle_fullscreen)
        toggle_play = QAction('Toggle Play', self._qt_window)
        toggle_play.triggered.connect(self._toggle_play)
        toggle_play.setShortcut('Ctrl+Alt+P')
        toggle_play.setStatusTip('Toggle Play')

        self.view_menu = self.main_menu.addMenu('&View')
        self.view_menu.addAction(toggle_fullscreen)
        self.view_menu.addAction(toggle_visible)
        self.view_menu.addAction(toggle_theme)
        self.view_menu.addAction(toggle_play)
        self.view_menu.addSeparator()

        # Add octree actions.
        if config.async_octree:
            toggle_outline = QAction('Toggle Chunk Outlines', self._qt_window)
            toggle_outline.triggered.connect(
                self.qt_viewer._toggle_chunk_outlines
            )
            toggle_outline.setShortcut('Ctrl+Alt+O')
            toggle_outline.setStatusTip('Toggle Chunk Outlines')
            self.view_menu.addAction(toggle_outline)

        # Add axes menu
        axes_menu = QMenu('Axes', parent=self._qt_window)
        axes_visible_action = QAction(
            'Visible',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.visible,
        )
        axes_visible_action.triggered.connect(self._toggle_axes_visible)
        axes_colored_action = QAction(
            'Colored',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.colored,
        )
        axes_colored_action.triggered.connect(self._toggle_axes_colored)
        axes_labels_action = QAction(
            'Labels',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.labels,
        )
        axes_labels_action.triggered.connect(self._toggle_axes_labels)
        axes_dashed_action = QAction(
            'Dashed',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.dashed,
        )
        axes_dashed_action.triggered.connect(self._toggle_axes_dashed)
        axes_arrows_action = QAction(
            'Arrows',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.axes.arrows,
        )
        axes_arrows_action.triggered.connect(self._toggle_axes_arrows)
        axes_menu.addAction(axes_visible_action)
        axes_menu.addAction(axes_colored_action)
        axes_menu.addAction(axes_labels_action)
        axes_menu.addAction(axes_dashed_action)
        axes_menu.addAction(axes_arrows_action)
        self.view_menu.addMenu(axes_menu)

        # Add scale bar menu
        scale_bar_menu = QMenu('Scale Bar', parent=self._qt_window)
        scale_bar_visible_action = QAction(
            'Visible',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.scale_bar.visible,
        )
        scale_bar_visible_action.triggered.connect(
            self._toggle_scale_bar_visible
        )
        scale_bar_colored_action = QAction(
            'Colored',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.scale_bar.colored,
        )
        scale_bar_colored_action.triggered.connect(
            self._toggle_scale_bar_colored
        )
        scale_bar_ticks_action = QAction(
            'Ticks',
            parent=self._qt_window,
            checkable=True,
            checked=self.qt_viewer.viewer.scale_bar.ticks,
        )
        scale_bar_ticks_action.triggered.connect(self._toggle_scale_bar_ticks)
        scale_bar_menu.addAction(scale_bar_visible_action)
        scale_bar_menu.addAction(scale_bar_colored_action)
        scale_bar_menu.addAction(scale_bar_ticks_action)
        self.view_menu.addMenu(scale_bar_menu)

        self.view_menu.addSeparator()

    def _add_window_menu(self):
        """Add 'Window' menu to app menubar."""
        exit_action = QAction("Close Window", self._qt_window)
        exit_action.setShortcut("Ctrl+W")
        exit_action.setStatusTip('Close napari window')
        exit_action.triggered.connect(self._qt_window.close)
        self.window_menu = self.main_menu.addMenu('&Window')
        self.window_menu.addAction(exit_action)

    def _add_plugins_menu(self):
        """Add 'Plugins' menu to app menubar."""
        self.plugins_menu = self.main_menu.addMenu('&Plugins')

        pip_install_action = QAction(
            "Install/Uninstall Package(s)...", self._qt_window
        )
        pip_install_action.triggered.connect(self._show_plugin_install_dialog)
        self.plugins_menu.addAction(pip_install_action)

        order_plugin_action = QAction("Plugin Call Order...", self._qt_window)
        order_plugin_action.setStatusTip('Change call order for plugins')
        order_plugin_action.triggered.connect(self._show_plugin_sorter)
        self.plugins_menu.addAction(order_plugin_action)

        report_plugin_action = QAction("Plugin Errors...", self._qt_window)
        report_plugin_action.setStatusTip(
            'Review stack traces for plugin exceptions and notify developers'
        )
        report_plugin_action.triggered.connect(self._show_plugin_err_reporter)
        self.plugins_menu.addAction(report_plugin_action)

    def _show_plugin_sorter(self):
        """Show dialog that allows users to sort the call order of plugins."""
        plugin_sorter = QtPluginSorter(parent=self._qt_window)
        if hasattr(self, 'plugin_sorter_widget'):
            self.plugin_sorter_widget.show()
        else:
            self.plugin_sorter_widget = self.add_dock_widget(
                plugin_sorter, name='Plugin Sorter', area="right"
            )

    def _show_plugin_install_dialog(self):
        """Show dialog that allows users to sort the call order of plugins."""

        self.plugin_dialog = QtPluginDialog(self._qt_window)
        self.plugin_dialog.exec_()

    def _show_plugin_err_reporter(self):
        """Show dialog that allows users to review and report plugin errors."""
        QtPluginErrReporter(parent=self._qt_window).exec_()

    def _add_help_menu(self):
        """Add 'Help' menu to app menubar."""
        self.help_menu = self.main_menu.addMenu('&Help')

        about_action = QAction("napari Info", self._qt_window)
        about_action.setShortcut("Ctrl+/")
        about_action.setStatusTip('About napari')
        about_action.triggered.connect(
            lambda e: QtAbout.showAbout(self.qt_viewer)
        )
        self.help_menu.addAction(about_action)

        about_key_bindings = QAction("Show Key Bindings", self._qt_window)
        about_key_bindings.setShortcut("Ctrl+Alt+/")
        about_key_bindings.setShortcutContext(Qt.ApplicationShortcut)
        about_key_bindings.setStatusTip('key_bindings')
        about_key_bindings.triggered.connect(
            self.qt_viewer.show_key_bindings_dialog
        )
        self.help_menu.addAction(about_key_bindings)

    def _toggle_scale_bar_visible(self, state):
        self.qt_viewer.viewer.scale_bar.visible = state

    def _toggle_scale_bar_colored(self, state):
        self.qt_viewer.viewer.scale_bar.colored = state

    def _toggle_scale_bar_ticks(self, state):
        self.qt_viewer.viewer.scale_bar.ticks = state

    def _toggle_axes_visible(self, state):
        self.qt_viewer.viewer.axes.visible = state

    def _toggle_axes_colored(self, state):
        self.qt_viewer.viewer.axes.colored = state

    def _toggle_axes_labels(self, state):
        self.qt_viewer.viewer.axes.labels = state

    def _toggle_axes_dashed(self, state):
        self.qt_viewer.viewer.axes.dashed = state

    def _toggle_axes_arrows(self, state):
        self.qt_viewer.viewer.axes.arrows = state

    def _toggle_fullscreen(self, event):
        """Toggle fullscreen mode."""
        if self._qt_window.isFullScreen():
            self._qt_window.showNormal()
        else:
            self._qt_window.showFullScreen()

    def _toggle_play(self, state):
        """Toggle play."""
        if self.qt_viewer.dims.is_playing:
            self.qt_viewer.dims.stop()
        else:
            axis = self.qt_viewer.viewer.dims.last_used or 0
            self.qt_viewer.dims.play(axis)

    def add_dock_widget(
        self,
        widget: QWidget,
        *,
        name: str = '',
        area: str = 'bottom',
        allowed_areas=None,
        shortcut=None,
    ):
        """Convenience method to add a QDockWidget to the main window

        Parameters
        ----------
        widget : QWidget
            `widget` will be added as QDockWidget's main widget.
        name : str, optional
            Name of dock widget to appear in window menu.
        area : str
            Side of the main window to which the new dock widget will be added.
            Must be in {'left', 'right', 'top', 'bottom'}
        allowed_areas : list[str], optional
            Areas, relative to main window, that the widget is allowed dock.
            Each item in list must be in {'left', 'right', 'top', 'bottom'}
            By default, all areas are allowed.
        shortcut : str, optional
            Keyboard shortcut to appear in dropdown menu.

        Returns
        -------
        dock_widget : QtViewerDockWidget
            `dock_widget` that can pass viewer events.
        """

        dock_widget = QtViewerDockWidget(
            self.qt_viewer,
            widget,
            name=name,
            area=area,
            allowed_areas=allowed_areas,
            shortcut=shortcut,
        )
        self._add_viewer_dock_widget(dock_widget)
        return dock_widget

    def _add_viewer_dock_widget(self, dock_widget: QtViewerDockWidget):
        """Add a QtViewerDockWidget to the main window

        Parameters
        ----------
        dock_widget : QtViewerDockWidget
            `dock_widget` will be added to the main window.
        """
        dock_widget.setParent(self._qt_window)
        self._qt_window.addDockWidget(dock_widget.qt_area, dock_widget)
        action = dock_widget.toggleViewAction()
        action.setStatusTip(dock_widget.name)
        action.setText(dock_widget.name)
        if dock_widget.shortcut is not None:
            action.setShortcut(dock_widget.shortcut)
        self.window_menu.addAction(action)

    def remove_dock_widget(self, widget):
        """Removes specified dock widget.

        Parameters
        ----------
        widget : QWidget | str
            If widget == 'all', all docked widgets will be removed.
        """
        if widget == 'all':
            for dw in self._qt_window.findChildren(QDockWidget):
                self._qt_window.removeDockWidget(dw)
        else:
            self._qt_window.removeDockWidget(widget)

    def resize(self, width, height):
        """Resize the window.

        Parameters
        ----------
        width : int
            Width in logical pixels.
        height : int
            Height in logical pixels.
        """
        self._qt_window.resize(width, height)

    def show(self):
        """Resize, show, and bring forward the window."""
        self._qt_window.resize(self._qt_window.layout().sizeHint())
        self._qt_window.show()
        # Resize axis labels now that window is shown
        self.qt_viewer.dims._resize_axis_labels()

        # We want to bring the viewer to the front when
        # A) it is our own (gui_qt) event loop OR we are running in jupyter
        # B) it is not the first time a QMainWindow is being created

        # `app_name` will be "napari" iff the application was instantiated in
        # gui_qt(). isActiveWindow() will be True if it is the second time a
        # _qt_window has been created.
        # See #721, #732, #735, #795, #1594
        app_name = QApplication.instance().applicationName()
        if (
            app_name == 'napari' or in_jupyter()
        ) and self._qt_window.isActiveWindow():
            self.activate()

    def activate(self):
        """Make the viewer the currently active window."""
        self._qt_window.raise_()  # for macOS
        self._qt_window.activateWindow()  # for Windows

    def _update_palette(self, event=None):
        """Update widget color palette."""
        # set window styles which don't use the primary stylesheet
        # FIXME: this is a problem with the stylesheet not using properties
        palette = self.qt_viewer.viewer.palette
        self._status_bar.setStyleSheet(
            template(
                'QStatusBar { background: {{ background }}; '
                'color: {{ text }}; }',
                **palette,
            )
        )
        self._qt_center.setStyleSheet(
            template('QWidget { background: {{ background }}; }', **palette)
        )
        self._qt_window.setStyleSheet(template(self.raw_stylesheet, **palette))

    def _status_changed(self, event):
        """Update status bar.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._status_bar.showMessage(event.text)

    def _title_changed(self, event):
        """Update window title.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._qt_window.setWindowTitle(event.text)

    def _help_changed(self, event):
        """Update help message on status bar.

        Parameters
        ----------
        event : napari.utils.event.Event
            The napari event that triggered this method.
        """
        self._help.setText(event.text)

    def _screenshot_dialog(self):
        """Save screenshot of current display with viewer, default .png"""
        dial = ScreenshotDialog(
            self.screenshot, self.qt_viewer, self.qt_viewer._last_visited_dir
        )
        if dial.exec_():
            self._last_visited_dir = os.path.dirname(dial.selectedFiles()[0])

    def screenshot(self, path=None):
        """Take currently displayed viewer and convert to an image array.

        Parameters
        ----------
        path : str
            Filename for saving screenshot image.

        Returns
        -------
        image : array
            Numpy array of type ubyte and shape (h, w, 4). Index [0, 0] is the
            upper-left corner of the rendered region.
        """
        img = self._qt_window.grab().toImage()
        if path is not None:
            imsave(path, QImg2array(img))  # scikit-image imsave method
        return QImg2array(img)

    def close(self):
        """Close the viewer window and cleanup sub-widgets."""

        # Someone is closing us twice? Only try to delete self._qt_window
        # if we still have one.
        if hasattr(self, '_qt_window'):
            self._delete_qt_window()

    def _delete_qt_window(self):
        """Delete our self._qt_window."""

        # On some versions of Darwin, exiting while fullscreen seems to tickle
        # some bug deep in NSWindow.  This forces the fullscreen keybinding
        # test to complete its draw cycle, then pop back out of fullscreen.
        if self._qt_window.isFullScreen():
            self._qt_window.showNormal()
            for i in range(8):
                time.sleep(0.1)
                QApplication.processEvents()
        self.qt_viewer.close()
        self._qt_window.close()
        del self._qt_window
示例#28
0
#
#    def __init__(self, holdIt, value):
#        QThread.__init__(self)
#        self.holdIt = holdIt
#        self.value = value
#
#    def run(self):
#        t = time.time()
#        time.sleep(2)
#        self.holdIt.update_duration = time.time() - t
#        print "update finished", self.value

if __name__ == '__main__':
    app = QApplication(sys.argv)
    m = QMainWindow()

    w = QWidget()
    m.setCentralWidget(w)
    vlayout = QVBoxLayout(w)
    s_log = LogaritmicSliderSpinBox(m, slider_steps=100)
    s_lin = SliderSpinBox(m, slider_steps=100)
    s_pol = PolynomialSliderSpinBox(m, 2, slider_steps=100, spinbox_steps=1000)
    vlayout.addWidget(s_lin)
    vlayout.addWidget(s_log)
    vlayout.addWidget(s_pol)
    #m.setCentralWidget(s)

    s_log.set_range((0.001, 1000))
    m.show()
    sys.exit(app.exec_())
示例#29
0
class PlotWindow(QMdiSubWindow):
    """
    Displayed plotting subwindow available in the `QMdiArea`.
    """
    def __init__(self, model, *args, **kwargs):
        super(PlotWindow, self).__init__(*args, **kwargs)
        # Hide the icon in the title bar
        self.setWindowIcon(qta.icon('fa.circle', opacity=0))

        # The central widget of the sub window will be a main window so that it
        # can support having tab bars
        self._central_widget = QMainWindow()
        self.setWidget(self._central_widget)

        loadUi(os.path.join(os.path.dirname(__file__), "ui", "plot_window.ui"),
               self._central_widget)

        # The central widget of the main window widget will be the plot
        self._model = model
        self._current_item_index = None

        self._plot_widget = PlotWidget(model=self._model)
        self._plot_widget.plotItem.setMenuEnabled(False)

        self._central_widget.setCentralWidget(self._plot_widget)

        # Setup connections
        self._central_widget.linear_region_action.triggered.connect(
            self.plot_widget._on_add_linear_region)
        self._central_widget.remove_region_action.triggered.connect(
            self.plot_widget._on_remove_linear_region)
        self._central_widget.change_color_action.triggered.connect(
            self._on_change_color)
        self._central_widget.line_labels_action.triggered.connect(
            self._on_line_labels)

        self._central_widget.reset_view_action.triggered.connect(
            self._on_reset_view)

    @property
    def tool_bar(self):
        return self._central_widget.tool_bar

    @property
    def current_item(self):
        if self._current_item_index is not None:
            return self.proxy_model.item_from_index(self._current_item_index)

    @property
    def plot_widget(self):
        return self._plot_widget

    @property
    def proxy_model(self):
        return self.plot_widget.proxy_model

    def _on_current_item_changed(self, current_idx, prev_idx):
        self._current_item_index = current_idx

    def _on_reset_view(self):
        """
        Resets the visible range of the plot taking into consideration only the
        PlotDataItem objects currently attached.
        """
        self.plot_widget.autoRange(items=[
            item for item in self.plot_widget.listDataItems()
            if isinstance(item, PlotDataItem)
        ])

        self.plot_widget.sigRangeChanged.emit(*self.plot_widget.viewRange())

    def _on_change_color(self):
        """
        Listens for color changed events in plot windows, gets the currently
        selected item in the data list view, and changes the stored color
        value.
        """
        # If there is no currently selected rows, raise an error
        if self.current_item is None:
            message_box = QMessageBox()
            message_box.setText("No item selected, cannot change color.")
            message_box.setIcon(QMessageBox.Warning)
            message_box.setInformativeText(
                "There is currently no item selected. Please select an item "
                "before changing its plot color.")

            message_box.exec()
            return

        color = QColorDialog.getColor()

        if color.isValid():
            self.current_item.color = color.name()

    def _on_line_labels(self):
        self._plot_widget._show_linelists_window()
示例#30
0
    workflowItem = QStandardItem("A Workflow Result")
    workflowItem.setCheckable(True)
    hints = []
    for i in range(2):
        if i == 0:
            style = Qt.SolidLine
        else:
            style = Qt.DashLine
        hint = PlotHint(np.arange(10),
                        np.random.random((10, )),
                        name=f"plot{i}",
                        style=style)
        hints.append(hint)
    coplothint = CoPlotHint(*hints, name="COPLOT")
    coPlotItem = QStandardItem(coplothint.name)
    coPlotItem.setCheckable(True)
    coPlotItem.setData(coplothint, Qt.UserRole)
    workflowItem.appendRow(coPlotItem)
    model.appendRow(workflowItem)

    lview = DerivedDataTreeView()
    lview.setModel(model)
    rview = HintTabView()
    rview.setModel(model)

    widget = DerivedDataWidgetTestClass(lview, rview)

    window.setCentralWidget(widget)
    window.show()

    app.exec()
示例#31
0
class Window:
    """Application window that contains the menu bar and viewers.

    Parameters
    ----------
    viewer : Viewer
        Contained viewer.

    Attributes
    ----------
    viewer : Viewer
        Contained viewer.
    """
    def __init__(self, viewer, show=True):
        self._qt_window = QMainWindow()
        self._qt_window.setUnifiedTitleAndToolBarOnMac(True)
        self._qt_center = QWidget()
        self._qt_window.setCentralWidget(self._qt_center)
        self._qt_window.setWindowTitle(viewer.title)
        self._qt_center.setLayout(QHBoxLayout())
        self._status_bar = self._qt_window.statusBar()
        self._status_bar.showMessage('Ready')

        self._help = QLabel('')
        self._status_bar.addPermanentWidget(self._help)

        self._status_bar.setStyleSheet("""QStatusBar { background: %s;
            color: %s}""" % (palette['background'], palette['text']))

        self.viewer = viewer
        self._qt_center.layout().addWidget(self.viewer._qtviewer)
        self._qt_center.layout().setContentsMargins(4, 0, 4, 0)
        self._qt_center.setStyleSheet('QWidget { background: %s;}' %
                                      palette['background'])

        self.viewer.events.status.connect(self._status_changed)
        self.viewer.events.help.connect(self._help_changed)
        self.viewer.events.title.connect(self._title_changed)

        if show:
            self.show()

    def resize(self, width, height):
        """Resize the window.

        Parameters
        ----------
        width : int
            Width in logical pixels.
        height : int
            Height in logical pixels.
        """
        self._qt_window.resize(width, height)

    def show(self):
        """Resize, show, and bring forward the window.
        """
        self._qt_window.resize(self._qt_window.layout().sizeHint())
        self._qt_window.show()
        self._qt_window.raise_()

    def _status_changed(self, event):
        """Update status bar.
        """
        self._status_bar.showMessage(event.text)

    def _title_changed(self, event):
        """Update window title.
        """
        self._qt_window.setWindowTitle(event.text)

    def _help_changed(self, event):
        """Update help message on status bar.
        """
        self._help.setText(event.text)
示例#32
0
class FittingPlotView(QtWidgets.QWidget, Ui_plot):
    def __init__(self, parent=None):
        super(FittingPlotView, self).__init__(parent)
        self.setupUi(self)

        self.figure = None
        self.toolbar = None
        self.fitprop_toolbar = None
        self.fit_browser = None
        self.plot_dock = None
        self.dock_window = None
        self.initial_chart_width = None
        self.initial_chart_height = None
        self.has_first_undock_occurred = 0

        self.setup_figure()
        self.setup_toolbar()

    def setup_figure(self):
        self.figure = Figure()
        self.figure.canvas = FigureCanvas(self.figure)
        self.figure.canvas.mpl_connect('button_press_event', self.mouse_click)
        self.figure.add_subplot(111, projection="mantid")
        self.toolbar = FittingPlotToolbar(self.figure.canvas, self, False)
        self.toolbar.setMovable(False)

        self.dock_window = QMainWindow(self.group_plot)
        self.dock_window.setWindowFlags(Qt.Widget)
        self.dock_window.setDockOptions(QMainWindow.AnimatedDocks)
        self.dock_window.setCentralWidget(self.toolbar)
        self.plot_dock = QDockWidget()
        self.plot_dock.setWidget(self.figure.canvas)
        self.plot_dock.setFeatures(QDockWidget.DockWidgetFloatable
                                   | QDockWidget.DockWidgetMovable)
        self.plot_dock.setAllowedAreas(Qt.BottomDockWidgetArea)
        self.plot_dock.setWindowTitle("Fit Plot")
        self.plot_dock.topLevelChanged.connect(self.make_undocked_plot_larger)
        self.initial_chart_width, self.initial_chart_height = self.plot_dock.width(
        ), self.plot_dock.height()
        self.plot_dock.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding,
                        QSizePolicy.MinimumExpanding))
        self.dock_window.addDockWidget(Qt.BottomDockWidgetArea, self.plot_dock)
        self.vLayout_plot.addWidget(self.dock_window)

        self.fit_browser = EngDiffFitPropertyBrowser(
            self.figure.canvas, ToolbarStateManager(self.toolbar))
        # remove SequentialFit from fit menu (implemented a different way)
        qmenu = self.fit_browser.getFitMenu()
        qmenu.removeAction([
            qact for qact in qmenu.actions() if qact.text() == "Sequential Fit"
        ][0])
        # hide unnecessary properties of browser
        hide_props = [
            'Minimizer', 'Cost function', 'Max Iterations', 'Output',
            'Ignore invalid data', 'Peak Radius', 'Plot Composite Members',
            'Convolve Composite Members', 'Show Parameter Errors',
            'Evaluate Function As'
        ]
        self.fit_browser.removePropertiesFromSettingsBrowser(hide_props)
        self.fit_browser.toggleWsListVisible()
        self.fit_browser.closing.connect(self.toolbar.handle_fit_browser_close)
        self.vLayout_fitprop.addWidget(self.fit_browser)
        self.fit_browser.hide()

    def mouse_click(self, event):
        if event.button == event.canvas.buttond.get(Qt.RightButton):
            menu = QMenu()
            self.fit_browser.add_to_menu(menu)
            menu.exec_(QCursor.pos())

    def resizeEvent(self, QResizeEvent):
        self.update_axes_position()

    def make_undocked_plot_larger(self):
        # only make undocked plot larger the first time it is undocked as the undocked size gets remembered
        if self.plot_dock.isFloating() and self.has_first_undock_occurred == 0:
            factor = 1.0
            aspect_ratio = self.initial_chart_width / self.initial_chart_height
            new_height = self.initial_chart_height * factor
            docked_height = self.dock_window.height()
            if docked_height > new_height:
                new_height = docked_height
            new_width = new_height * aspect_ratio
            self.plot_dock.resize(new_width, new_height)
            self.has_first_undock_occurred = 1

        self.update_axes_position()

    def ensure_fit_dock_closed(self):
        if self.plot_dock.isFloating():
            self.plot_dock.close()

    def setup_toolbar(self):
        self.toolbar.sig_home_clicked.connect(self.display_all)
        self.toolbar.sig_toggle_fit_triggered.connect(self.fit_toggle)

    # =================
    # Component Setters
    # =================

    def fit_toggle(self):
        """Toggle fit browser and tool on/off"""
        if self.fit_browser.isVisible():
            self.fit_browser.hide()
        else:
            self.fit_browser.show()

    def clear_figure(self):
        self.figure.clf()
        self.figure.add_subplot(111, projection="mantid")
        self.figure.canvas.draw()

    def update_figure(self):
        self.toolbar.update()
        self.update_legend(self.get_axes()[0])
        self.update_axes_position()
        self.figure.canvas.draw()
        self.update_fitbrowser()

    def update_axes_position(self):
        """
        Set axes position so that labels are always visible - it deliberately ignores height of ylabel (and less
        importantly the length of xlabel). This is because the plot window typically has a very small height when docked
        in the UI.
        """
        ax = self.get_axes()[0]
        y0_lab = ax.xaxis.get_tightbbox(
            renderer=self.figure.canvas.get_renderer()
        ).transformed(
            self.figure.transFigure.inverted()
        ).y0  # vertical coord of bottom left corner of xlabel in fig ref. frame
        x0_lab = ax.yaxis.get_tightbbox(
            renderer=self.figure.canvas.get_renderer()
        ).transformed(
            self.figure.transFigure.inverted()
        ).x0  # horizontal coord of bottom left corner ylabel in fig ref. frame
        pos = ax.get_position()
        x0_ax = pos.x0 + 0.05 - x0_lab  # move so that ylabel left bottom corner at horizontal coord 0.05
        y0_ax = pos.y0 + 0.05 - y0_lab  # move so that xlabel left bottom corner at vertical coord 0.05
        ax.set_position([x0_ax, y0_ax, 0.95 - x0_ax, 0.95 - y0_ax])

    def update_fitbrowser(self):
        is_visible = self.fit_browser.isVisible()
        self.toolbar.set_fit_checkstate(False)
        self.fit_browser.hide()
        if self.fit_browser.getWorkspaceNames() and is_visible:
            self.toolbar.set_fit_checkstate(True)
            self.fit_browser.show()

    def remove_ws_from_fitbrowser(self, ws):
        # only one spectra per workspace
        try:
            self.fit_browser.removeWorkspaceAndSpectra(ws.name())
        except:
            pass  # name may not be available if ws has just been deleted

    def update_legend(self, ax):
        if ax.get_lines():
            ax.make_legend()
            ax.get_legend().set_title("")
        else:
            if ax.get_legend():
                ax.get_legend().remove()

    def display_all(self):
        for ax in self.get_axes():
            if ax.lines:
                ax.relim()
            ax.autoscale()
        self.update_figure()

    def read_fitprop_from_browser(self):
        return self.fit_browser.read_current_fitprop()

    def update_browser(self, status, func_str, setup_name):
        self.fit_browser.fitResultsChanged.emit(status)
        self.fit_browser.changeWindowTitle.emit(status)
        # update browser with output function and save setup if successful
        if "success" in status.lower():
            self.fit_browser.loadFunction(func_str)
            self.fit_browser.save_current_setup(setup_name)

    # =================
    # Component Getters
    # =================

    def get_axes(self):
        return self.figure.axes

    def get_figure(self):
        return self.figure
示例#33
0
class Window:
    """Application window that contains the menu bar and viewer.

    Parameters
    ----------
    qt_viewer : QtViewer
        Contained viewer widget.

    Attributes
    ----------
    qt_viewer : QtViewer
        Contained viewer widget.
    """
    def __init__(self, qt_viewer, *, show=True):

        self.qt_viewer = qt_viewer

        self._qt_window = QMainWindow()
        self._qt_window.setUnifiedTitleAndToolBarOnMac(True)
        self._qt_center = QWidget()
        self._qt_window.setCentralWidget(self._qt_center)
        self._qt_window.setWindowTitle(self.qt_viewer.viewer.title)
        self._qt_center.setLayout(QHBoxLayout())
        self._status_bar = self._qt_window.statusBar()
        self._qt_window.closeEvent = self.closeEvent
        self.close = self._qt_window.close

        self._add_menubar()

        self._add_file_menu()
        self._add_view_menu()
        self._add_window_menu()

        self._status_bar.showMessage('Ready')
        self._help = QLabel('')
        self._status_bar.addPermanentWidget(self._help)

        self._qt_center.layout().addWidget(self.qt_viewer)
        self._qt_center.layout().setContentsMargins(4, 0, 4, 0)

        self._update_palette(qt_viewer.viewer.palette)

        self.qt_viewer.viewer.events.status.connect(self._status_changed)
        self.qt_viewer.viewer.events.help.connect(self._help_changed)
        self.qt_viewer.viewer.events.title.connect(self._title_changed)
        self.qt_viewer.viewer.events.palette.connect(
            lambda event: self._update_palette(event.palette))

        if show:
            self.show()

    def _add_menubar(self):
        self.main_menu = self._qt_window.menuBar()
        # Menubar shortcuts are only active when the menubar is visible.
        # Therefore, we set a global shortcut not associated with the menubar
        # to toggle visibility, *but*, in order to not shadow the menubar
        # shortcut, we disable it, and only enable it when the menubar is
        # hidden. See this stackoverflow link for details:
        # https://stackoverflow.com/questions/50537642/how-to-keep-the-shortcuts-of-a-hidden-widget-in-pyqt5
        self._main_menu_shortcut = QShortcut(QKeySequence('Ctrl+M'),
                                             self._qt_window)
        self._main_menu_shortcut.activated.connect(
            self._toggle_menubar_visible)
        self._main_menu_shortcut.setEnabled(False)

    def _toggle_menubar_visible(self):
        """Toggle visibility of app menubar.

        This function also disables or enables a global keyboard shortcut to
        show the menubar, since menubar shortcuts are only available while the
        menubar is visible.
        """
        if self.main_menu.isVisible():
            self.main_menu.setVisible(False)
            self._main_menu_shortcut.setEnabled(True)
        else:
            self.main_menu.setVisible(True)
            self._main_menu_shortcut.setEnabled(False)

    def _add_file_menu(self):
        open_images = QAction('Open', self._qt_window)
        open_images.setShortcut('Ctrl+O')
        open_images.setStatusTip('Open image file(s)')
        open_images.triggered.connect(self.qt_viewer._open_images)
        self.file_menu = self.main_menu.addMenu('&File')
        self.file_menu.addAction(open_images)

    def _add_view_menu(self):
        toggle_visible = QAction('Toggle menubar visibility', self._qt_window)
        toggle_visible.setShortcut('Ctrl+M')
        toggle_visible.setStatusTip('Hide Menubar')
        toggle_visible.triggered.connect(self._toggle_menubar_visible)
        self.view_menu = self.main_menu.addMenu('&View')
        self.view_menu.addAction(toggle_visible)

    def _add_window_menu(self):
        exit_action = QAction("Close window", self._qt_window)
        exit_action.setShortcut("Ctrl+W")
        exit_action.setStatusTip('Close napari window')
        exit_action.triggered.connect(self._qt_window.close)
        self.window_menu = self.main_menu.addMenu('&Window')
        self.window_menu.addAction(exit_action)

    def resize(self, width, height):
        """Resize the window.

        Parameters
        ----------
        width : int
            Width in logical pixels.
        height : int
            Height in logical pixels.
        """
        self._qt_window.resize(width, height)

    def show(self):
        """Resize, show, and bring forward the window.
        """
        self._qt_window.resize(self._qt_window.layout().sizeHint())
        self._qt_window.show()
        self._qt_window.raise_()

    def _update_palette(self, palette):
        # set window styles which don't use the primary stylesheet
        # FIXME: this is a problem with the stylesheet not using properties
        self._status_bar.setStyleSheet(
            template(
                'QStatusBar { background: {{ background }}; '
                'color: {{ text }}; }',
                **palette,
            ))
        self._qt_center.setStyleSheet(
            template('QWidget { background: {{ background }}; }', **palette))

    def _status_changed(self, event):
        """Update status bar.
        """
        self._status_bar.showMessage(event.text)

    def _title_changed(self, event):
        """Update window title.
        """
        self._qt_window.setWindowTitle(event.text)

    def _help_changed(self, event):
        """Update help message on status bar.
        """
        self._help.setText(event.text)

    def closeEvent(self, event):
        # Forward close event to the console to trigger proper shutdown
        self.qt_viewer.console.shutdown()
        event.accept()
示例#34
0
class PlotWindow(QMdiSubWindow):
    """
    Displayed plotting subwindow available in the ``QMdiArea``.
    """
    window_removed = Signal()
    color_changed = Signal(PlotDataItem, QColor)
    width_changed = Signal(int)

    def __init__(self, model, *args, **kwargs):
        super(PlotWindow, self).__init__(*args, **kwargs)
        # Hide the icon in the title bar
        self.setWindowIcon(qta.icon('fa.circle', opacity=0))

        # The central widget of the sub window will be a main window so that it
        # can support having tab bars
        self._central_widget = QMainWindow()
        self.setWidget(self._central_widget)

        loadUi(os.path.join(os.path.dirname(__file__), "ui", "plot_window.ui"),
               self._central_widget)

        # The central widget of the main window widget will be the plot
        self._model = model
        self._current_item_index = None

        self._plot_widget = PlotWidget(model=self._model)
        self._plot_widget.plotItem.setMenuEnabled(False)

        self._central_widget.setCentralWidget(self._plot_widget)

        # Setup action group for interaction modes
        mode_group = QActionGroup(self.tool_bar)
        mode_group.addAction(self._central_widget.pan_mode_action)
        self._central_widget.pan_mode_action.setChecked(True)
        mode_group.addAction(self._central_widget.zoom_mode_action)

        def _toggle_mode(state):
            view_state = self.plot_widget.plotItem.getViewBox().state.copy()
            view_state.update({'mouseMode': pg.ViewBox.RectMode
                               if state else pg.ViewBox.PanMode})
            self.plot_widget.plotItem.getViewBox().setState(view_state)

        # Setup plot settings options menu
        self.plot_settings_button = self.tool_bar.widgetForAction(
            self._central_widget.plot_settings_action)
        self.plot_settings_button.setPopupMode(QToolButton.InstantPopup)

        self.plot_settings_menu = QMenu(self.plot_settings_button)
        self.plot_settings_button.setMenu(self.plot_settings_menu)

        self.color_change_action = QAction("Line Color")
        self.plot_settings_menu.addAction(self.color_change_action)

        self.line_width_menu = QMenu("Line Widths")
        self.plot_settings_menu.addMenu(self.line_width_menu)

        # Setup the line width plot setting options
        for i in range(1, 4):
            act = QAction(str(i), self.line_width_menu)
            self.line_width_menu.addAction(act)
            act.triggered.connect(lambda *args, size=i:
                                  self._on_change_width(size))

        # Setup connections
        self._central_widget.pan_mode_action.triggered.connect(
            lambda: _toggle_mode(False))
        self._central_widget.zoom_mode_action.triggered.connect(
            lambda: _toggle_mode(True))
        self._central_widget.linear_region_action.triggered.connect(
            self.plot_widget._on_add_linear_region)
        self._central_widget.remove_region_action.triggered.connect(
            self.plot_widget._on_remove_linear_region)
        self.color_change_action.triggered.connect(
            self._on_change_color)
        self._central_widget.export_plot_action.triggered.connect(
            self._on_export_plot)
        self._central_widget.reset_view_action.triggered.connect(
            lambda: self._on_reset_view())

    @property
    def tool_bar(self):
        """
        Return the tool bar for the embedded plot widget.
        """
        return self._central_widget.tool_bar

    @property
    def current_item(self):
        """
        The currently selected plot data item.
        """
        if self._current_item_index is not None:
            return self.proxy_model.item_from_index(self._current_item_index)

    @property
    def plot_widget(self):
        """
        Return the embedded plot widget
        """
        return self._plot_widget

    @property
    def proxy_model(self):
        """
        The proxy model defined in the internal plot widget.
        """
        return self.plot_widget.proxy_model

    def closeEvent(self, event):
        """
        Called by qt when window closes, upon which
        it emits the window_removed signal.

        Parameters
        ----------
        event :
            ignored in this implementation.
        """
        self.window_removed.emit()

    def _on_current_item_changed(self, current_idx, prev_idx):
        self._current_item_index = current_idx

    def _on_reset_view(self):
        """
        Resets the visible range of the plot taking into consideration only the
        PlotDataItem objects currently attached.
        """
        self.plot_widget.autoRange(
                items=[item for item in self.plot_widget.listDataItems()
                       if isinstance(item, PlotDataItem)])
        self.plot_widget.sigRangeChanged.emit(*self.plot_widget.viewRange())

    def _on_change_color(self):
        """
        Listens for color changed events in plot windows, gets the currently
        selected item in the data list view, and changes the stored color
        value.
        """
        # If there is no currently selected rows, raise an error
        if self.current_item is None:
            message_box = QMessageBox()
            message_box.setText("No item selected, cannot change color.")
            message_box.setIcon(QMessageBox.Warning)
            message_box.setInformativeText(
                "There is currently no item selected. Please select an item "
                "before changing its plot color.")

            message_box.exec()
            return

        color = QColorDialog.getColor(options=QColorDialog.ShowAlphaChannel)

        if color.isValid():
            self.current_item.color = color.toRgb()
            self.color_changed.emit(self.current_item, self.current_item.color)

    def _on_change_width(self, size):
        self.plot_widget.change_width(size)
        self.width_changed.emit(size)

    def _on_export_plot(self):
        file_path, key = compat.getsavefilename(filters=";;".join(
            EXPORT_FILTERS.keys()))

        if key == '':
            return

        exporter = EXPORT_FILTERS[key](self.plot_widget.plotItem)

        # TODO: Current issue in pyqtgraph where the user cannot explicitly
        # define the output size. Fix incoming.

        # plot_size_dialog = PlotSizeDialog(self)
        # plot_size_dialog.height_line_edit.setText(
        #     str(int(exporter.params.param('height').value())))
        # plot_size_dialog.width_line_edit.setText(
        #     str(int(exporter.params.param('width').value())))
        #
        # if key != "*.svg":
        #     if plot_size_dialog.exec_():
        #         exporter.params.param('height').setValue(int(exporter.params.param('height').value()),
        #                                                  blockSignal=exporter.heightChanged)
        #         exporter.params.param('width').setValue(int(exporter.params.param('height').value()),
        #                                                  blockSignal=exporter.widthChanged)
        #     else:
        #         return

        exporter.export(file_path)