Exemplo n.º 1
0
class FormComboWidget(QWidget):
    update_buttons = Signal()
    
    def __init__(self, datalist, comment="", parent=None):
        QWidget.__init__(self, parent)
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.combobox = QComboBox()
        layout.addWidget(self.combobox)
        
        self.stackwidget = QStackedWidget(self)
        layout.addWidget(self.stackwidget)
        self.combobox.currentIndexChanged.connect(
                                              self.stackwidget.setCurrentIndex)
        
        self.widgetlist = []
        for data, title, comment in datalist:
            self.combobox.addItem(title)
            widget = FormWidget(data, comment=comment, parent=self)
            self.stackwidget.addWidget(widget)
            self.widgetlist.append(widget)
            
    def setup(self):
        for widget in self.widgetlist:
            widget.setup()

    def get(self):
        return [ widget.get() for widget in self.widgetlist]
Exemplo n.º 2
0
class PreferencesDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.contentsWidget = QListWidget()
        self.pagesWidget = QStackedWidget()

        self.plotPref = PlotPreferences(parent)
        self.dataPref = DataPreferences(parent)
        self.pagesWidget.addWidget(self.plotPref)
        self.pagesWidget.addWidget(self.dataPref)

        self.contentsWidget.addItem("Plot")
        self.contentsWidget.addItem("Data")
        self.contentsWidget.currentItemChanged.connect(self.changePage)
        self.contentsWidget.setCurrentRow(0)

        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok
                                          | QDialogButtonBox.Apply
                                          | QDialogButtonBox.Close)

        okButton = self.buttonBox.button(QDialogButtonBox.Ok)
        okButton.clicked.connect(self.ok)
        applyButton = self.buttonBox.button(QDialogButtonBox.Apply)
        applyButton.clicked.connect(self.apply)
        closeButton = self.buttonBox.button(QDialogButtonBox.Close)
        closeButton.clicked.connect(self.close)

        horizontalLayout = QHBoxLayout()
        horizontalLayout.addWidget(self.contentsWidget)
        horizontalLayout.addWidget(self.pagesWidget)

        self.contentsWidget.setSizeAdjustPolicy(
            QAbstractScrollArea.AdjustToContents)

        mainLayout = QVBoxLayout()
        mainLayout.addLayout(horizontalLayout)
        mainLayout.addWidget(self.buttonBox)

        self.setLayout(mainLayout)

        self.setWindowTitle("Preferences")

    def changePage(self, current, previous):
        if not current:
            current = previous
        self.pagesWidget.setCurrentIndex(self.contentsWidget.row(current))

    def show(self):
        for n in range(self.pagesWidget.count()):
            self.pagesWidget.widget(n).setCurrentValues()
        super().show()

    def apply(self):
        self.pagesWidget.currentWidget().apply()

    def ok(self):
        for idx in range(self.pagesWidget.count()):
            self.pagesWidget.widget(idx).apply()
        self.accept()
Exemplo n.º 3
0
class FormComboWidget(QWidget):
    update_buttons = Signal()

    def __init__(self, datalist, comment="", parent=None):
        QWidget.__init__(self, parent)
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.combobox = QComboBox()
        layout.addWidget(self.combobox)

        self.stackwidget = QStackedWidget(self)
        layout.addWidget(self.stackwidget)
        self.combobox.currentIndexChanged.connect(
            self.stackwidget.setCurrentIndex)

        self.widgetlist = []
        for data, title, comment in datalist:
            self.combobox.addItem(title)
            widget = FormWidget(data, comment=comment, parent=self)
            self.stackwidget.addWidget(widget)
            self.widgetlist.append(widget)

    def setup(self):
        for widget in self.widgetlist:
            widget.setup()

    def get(self):
        return [widget.get() for widget in self.widgetlist]
Exemplo n.º 4
0
class RunConfigDialog(BaseRunConfigDialog):
    """Run configuration dialog box: multiple file version"""
    def __init__(self, parent=None):
        BaseRunConfigDialog.__init__(self, parent)
        self.file_to_run = None
        self.combo = None
        self.stack = None

    def run_btn_clicked(self):
        """Run button was just clicked"""
        self.file_to_run = to_text_string(self.combo.currentText())

    def setup(self, fname):
        """Setup Run Configuration dialog with filename *fname*"""
        combo_label = QLabel(_("Select a run configuration:"))
        self.combo = QComboBox()
        self.combo.setMaxVisibleItems(20)
        self.combo.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
        self.combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        self.stack = QStackedWidget()

        configurations = _get_run_configurations()
        for index, (filename, options) in enumerate(configurations):
            if fname == filename:
                break
        else:
            # There is no run configuration for script *fname*:
            # creating a temporary configuration that will be kept only if
            # dialog changes are accepted by the user
            configurations.insert(0, (fname, RunConfiguration(fname).get()))
            index = 0
        for filename, options in configurations:
            widget = RunConfigOptions(self)
            widget.set(options)
            self.combo.addItem(filename)
            self.stack.addWidget(widget)
        self.combo.currentIndexChanged.connect(self.stack.setCurrentIndex)
        self.combo.setCurrentIndex(index)

        self.add_widgets(combo_label, self.combo, 10, self.stack)
        self.add_button_box(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)

        self.setWindowTitle(_("Run configuration per file"))

    def accept(self):
        """Reimplement Qt method"""
        configurations = []
        for index in range(self.stack.count()):
            filename = to_text_string(self.combo.itemText(index))
            runconfigoptions = self.stack.widget(index)
            if index == self.stack.currentIndex() and\
               not runconfigoptions.is_valid():
                return
            options = runconfigoptions.get()
            configurations.append( (filename, options) )
        _set_run_configurations(configurations)
        QDialog.accept(self)
Exemplo n.º 5
0
class RunConfigDialog(BaseRunConfigDialog):
    """Run configuration dialog box: multiple file version"""
    def __init__(self, parent=None):
        BaseRunConfigDialog.__init__(self, parent)
        self.file_to_run = None
        self.combo = None
        self.stack = None
        
    def run_btn_clicked(self):
        """Run button was just clicked"""
        self.file_to_run = to_text_string(self.combo.currentText())
        
    def setup(self, fname):
        """Setup Run Configuration dialog with filename *fname*"""
        combo_label = QLabel(_("Select a run configuration:"))
        self.combo = QComboBox()
        self.combo.setMaxVisibleItems(20)
        self.combo.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
        self.combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        
        self.stack = QStackedWidget()

        configurations = _get_run_configurations()
        for index, (filename, options) in enumerate(configurations):
            if fname == filename:
                break
        else:
            # There is no run configuration for script *fname*:
            # creating a temporary configuration that will be kept only if
            # dialog changes are accepted by the user
            configurations.insert(0, (fname, RunConfiguration(fname).get()))
            index = 0
        for filename, options in configurations:
            widget = RunConfigOptions(self)
            widget.set(options)
            self.combo.addItem(filename)
            self.stack.addWidget(widget)
        self.combo.currentIndexChanged.connect(self.stack.setCurrentIndex)
        self.combo.setCurrentIndex(index)

        self.add_widgets(combo_label, self.combo, 10, self.stack)
        self.add_button_box(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)

        self.setWindowTitle(_("Run configuration per file"))
        
    def accept(self):
        """Reimplement Qt method"""
        configurations = []
        for index in range(self.stack.count()):
            filename = to_text_string(self.combo.itemText(index))
            runconfigoptions = self.stack.widget(index)
            if index == self.stack.currentIndex() and\
               not runconfigoptions.is_valid():
                return
            options = runconfigoptions.get()
            configurations.append( (filename, options) )
        _set_run_configurations(configurations)
        QDialog.accept(self)
Exemplo n.º 6
0
class Editor(QDialog):
    """Basic scene editor."""
    def __init__(self, parent: MainWindow, renderers: List[Renderer]) -> None:
        """Initialize the Editor."""
        super().__init__(parent=parent)
        self.renderers = renderers

        self.tree_widget = QTreeWidget()
        self.tree_widget.setHeaderHidden(True)
        self.stacked_widget = QStackedWidget()
        self.layout = QHBoxLayout()
        self.layout.addWidget(self.tree_widget)
        self.layout.addWidget(self.stacked_widget)

        def _selection_callback() -> None:
            for item in self.tree_widget.selectedItems():
                widget_idx = item.data(0, Qt.ItemDataRole.UserRole)
                self.stacked_widget.setCurrentIndex(widget_idx)

        self.tree_widget.itemSelectionChanged.connect(_selection_callback)

        self.setLayout(self.layout)
        self.setWindowTitle("Editor")
        self.setModal(True)

        self.update()

    def update(self) -> None:
        """Update the internal widget list."""
        self.tree_widget.clear()
        for idx, renderer in enumerate(self.renderers):
            actors = renderer._actors  # pylint: disable=protected-access
            widget_idx = self.stacked_widget.addWidget(
                _get_renderer_widget(renderer))
            top_item = QTreeWidgetItem(self.tree_widget,
                                       ["Renderer {}".format(idx)])
            top_item.setData(0, Qt.ItemDataRole.UserRole, widget_idx)
            self.tree_widget.addTopLevelItem(top_item)
            for name, actor in actors.items():
                if actor is not None:
                    widget_idx = self.stacked_widget.addWidget(
                        _get_actor_widget(actor))
                    child_item = QTreeWidgetItem(top_item, [name])
                    child_item.setData(0, Qt.ItemDataRole.UserRole, widget_idx)
                    top_item.addChild(child_item)
        self.tree_widget.expandAll()

    def toggle(self) -> None:
        """Toggle the editor visibility."""
        self.update()
        if self.isVisible():
            self.hide()
        else:
            self.show()
    def __init__(
        self,
        save_register: typing.Dict[str, type(SaveBase)],
        system_widget=True,
        base_values: typing.Optional[dict] = None,
        parent=None,
        history: typing.Optional[typing.List[str]] = None,
        file_mode=QFileDialog.AnyFile,
    ):
        super().__init__(parent)
        self.setFileMode(file_mode)
        self.save_register = {
            x.get_name_with_suffix(): x
            for x in save_register.values()
        }
        self.setOption(QFileDialog.DontUseNativeDialog, not system_widget)
        self.setAcceptMode(QFileDialog.AcceptSave)
        self.filterSelected.connect(self.change_filter)
        self.setNameFilters(self.save_register.keys())
        self.accepted_native = False
        self.values = {}
        self.names = []
        if history is not None:
            history = self.history() + history
            self.setHistory(history)
        self.base_values = base_values if base_values is not None else {}
        if not system_widget:
            widget = QStackedWidget()
            for name, val in self.save_register.items():
                wi = FormWidget(val.get_fields())
                if name in self.base_values:
                    wi.set_values(self.base_values[name])
                widget.addWidget(wi)
                self.names.append(name)

            self.filterSelected.connect(self.change_parameters)

            layout = self.layout()
            if isinstance(layout, QGridLayout):
                # print(layout.columnCount(), layout.rowCount())
                # noinspection PyArgumentList
                layout.addWidget(widget, 0, layout.columnCount(),
                                 layout.rowCount(), 1)
                self.stack_widget = widget
            else:
                layout.addWidget(widget)
                self.stack_widget = widget
            self.selectNameFilter(self.names[0])
Exemplo n.º 8
0
class ImageAdjustmentDialog(QDialog):
    def __init__(self,
                 image: Image,
                 transform_dict: Dict[str, TransformBase] = None):
        super().__init__()
        if transform_dict is None:
            transform_dict = image_transform_dict
        self.choose = QComboBox()
        self.stacked = QStackedWidget()
        for key, val in transform_dict.items():
            self.choose.addItem(key)
            initial_values = val.calculate_initial(image)
            form_widget = FormWidget(
                val.get_fields_per_dimension(image.get_dimension_letters()),
                initial_values)
            self.stacked.addWidget(form_widget)

        self.choose.currentIndexChanged.connect(self.stacked.setCurrentIndex)

        self.cancel_btn = QPushButton("Cancel")
        self.cancel_btn.clicked.connect(self.reject)

        self.process_btn = QPushButton("Process")
        self.process_btn.clicked.connect(self.process)
        self.transform_dict = transform_dict
        self.result_val: ImageAdjustTuple = None

        layout = QGridLayout()
        layout.addWidget(self.choose, 0, 0, 1, 3)
        layout.addWidget(self.stacked, 1, 0, 1, 3)
        layout.addWidget(self.cancel_btn, 2, 0)
        layout.addWidget(self.process_btn, 2, 2)
        self.setLayout(layout)

    def process(self):
        values = self.stacked.currentWidget().get_values()
        algorithm = self.transform_dict[self.choose.currentText()]
        self.result_val = ImageAdjustTuple(values, algorithm)

        self.accept()
Exemplo n.º 9
0
    def _load_core_widget(self):
        setting = self.params["setting"]
        hlayout = QHBoxLayout()
        list_widget = QListWidget()
        stacked_widget = QStackedWidget()
        for key in setting.keys():
            list_widget.addItem(key)
            widget = QWidget()
            widget_layout = QVBoxLayout()
            for value in setting[key]:
                self._create_form(widget_layout, [value])
            widget_layout.addStretch()
            widget.setLayout(widget_layout)
            stacked_widget.addWidget(widget)
        hlayout.addWidget(list_widget)
        hlayout.addWidget(stacked_widget)
        hlayout.setStretch(0, 1)
        hlayout.setStretch(1, 5)

        list_widget.currentRowChanged.connect(stacked_widget.setCurrentIndex)
        list_widget.setCurrentRow(0)
        return hlayout
Exemplo n.º 10
0
class PreferencesDialog(QDialog):
    """Preferences Dialog for Napari user settings."""

    ui_schema = {
        "call_order": {
            "ui:widget": "plugins"
        },
    }

    resized = Signal(QSize)
    closed = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)

        self._list = QListWidget(self)
        self._stack = QStackedWidget(self)

        self._list.setObjectName("Preferences")

        # Set up buttons
        self._button_cancel = QPushButton(trans._("Cancel"))
        self._button_ok = QPushButton(trans._("OK"))
        self._default_restore = QPushButton(trans._("Restore defaults"))

        # Setup
        self.setWindowTitle(trans._("Preferences"))

        # Layout
        left_layout = QVBoxLayout()
        left_layout.addWidget(self._list)
        left_layout.addStretch()
        left_layout.addWidget(self._default_restore)
        left_layout.addWidget(self._button_cancel)
        left_layout.addWidget(self._button_ok)

        main_layout = QHBoxLayout()
        main_layout.addLayout(left_layout, 1)
        main_layout.addWidget(self._stack, 3)

        self.setLayout(main_layout)

        # Signals

        self._list.currentRowChanged.connect(
            lambda index: self._stack.setCurrentIndex(index))
        self._button_cancel.clicked.connect(self.on_click_cancel)
        self._button_ok.clicked.connect(self.on_click_ok)
        self._default_restore.clicked.connect(self.restore_defaults)

        # Make widget

        self.make_dialog()
        self._list.setCurrentRow(0)

    def closeEvent(self, event):
        """Override to emit signal."""
        self.closed.emit()
        super().closeEvent(event)

    def reject(self):
        """Override to handle Escape."""
        super().reject()
        self.close()

    def resizeEvent(self, event):
        """Override to emit signal."""
        self.resized.emit(event.size())
        super().resizeEvent(event)

    def make_dialog(self):
        """Removes settings not to be exposed to user and creates dialog pages."""

        # Because there are multiple pages, need to keep a dictionary of values dicts.
        # One set of keywords are for each page, then in each entry for a page, there are dicts
        # of setting and its value.

        self._values_orig_dict = {}
        self._values_dict = {}
        self._setting_changed_dict = {}

        for page, setting in SETTINGS.schemas().items():
            schema, values, properties = self.get_page_dict(setting)

            self._setting_changed_dict[page] = {}
            self._values_orig_dict[page] = values
            self._values_dict[page] = values

            # Only add pages if there are any properties to add.
            if properties:
                self.add_page(schema, values)

    def get_page_dict(self, setting):
        """Provides the schema, set of values for each setting, and the properties
        for each setting.

        Parameters
        ----------
        setting : dict
            Dictionary of settings for a page within the settings manager.

        Returns
        -------
        schema : dict
            Json schema of the setting page.
        values : dict
            Dictionary of values currently set for each parameter in the settings.
        properties : dict
            Dictionary of properties within the json schema.

        """

        schema = json.loads(setting['json_schema'])
        # Need to remove certain properties that will not be displayed on the GUI
        properties = schema.pop('properties')
        model = setting['model']
        values = model.dict()
        napari_config = getattr(model, "NapariConfig", None)
        if napari_config is not None:
            for val in napari_config.preferences_exclude:
                properties.pop(val)
                values.pop(val)

        schema['properties'] = properties

        return schema, values, properties

    def restore_defaults(self):
        """Launches dialog to confirm restore settings choice."""

        widget = ConfirmDialog(
            parent=self,
            text=trans._("Are you sure you want to restore default settings?"),
        )
        widget.valueChanged.connect(self._reset_widgets)
        widget.exec_()

    def _reset_widgets(self):
        """Deletes the widgets and rebuilds with defaults."""
        self.close()
        self._list.clear()

        for n in range(self._stack.count()):
            widget = self._stack.removeWidget(self._stack.currentWidget())
            del widget

        self.make_dialog()
        self._list.setCurrentRow(0)
        self.show()

    def on_click_ok(self):
        """Keeps the selected preferences saved to SETTINGS."""
        self.close()

    def on_click_cancel(self):
        """Restores the settings in place when dialog was launched."""
        # Need to check differences for each page.
        for n in range(self._stack.count()):
            # Must set the current row so that the proper list is updated
            # in check differences.
            self._list.setCurrentRow(n)
            page = self._list.currentItem().text().split(" ")[0].lower()
            # get new values for settings.  If they were changed from values at beginning
            # of preference dialog session, change them back.
            # Using the settings value seems to be the best way to get the checkboxes right
            # on the plugin call order widget.
            setting = SETTINGS.schemas()[page]
            schema, new_values, properties = self.get_page_dict(setting)
            self.check_differences(self._values_orig_dict[page], new_values)

        self._list.setCurrentRow(0)
        self.close()

    def add_page(self, schema, values):
        """Creates a new page for each section in dialog.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """
        widget = self.build_page_dialog(schema, values)
        self._list.addItem(schema["title"])
        self._stack.addWidget(widget)

    def build_page_dialog(self, schema, values):
        """Builds the preferences widget using the json schema builder.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """

        builder = WidgetBuilder()
        form = builder.create_form(schema, self.ui_schema)
        # set state values for widget
        form.widget.state = values
        form.widget.on_changed.connect(lambda d: self.check_differences(
            d,
            self._values_dict[schema["title"].lower()],
        ))

        return form

    def _values_changed(self, page, new_dict, old_dict):
        """Loops through each setting in a page to determine if it changed.

        Parameters
        ----------
        new_dict : dict
            Dict that has the most recent changes by user. Each key is a setting value
            and each item is the value.
        old_dict : dict
            Dict wtih values set at the begining of preferences dialog session.

        """
        for setting_name, value in new_dict.items():
            if value != old_dict[setting_name]:
                self._setting_changed_dict[page][setting_name] = value
            elif (value == old_dict[setting_name]
                  and setting_name in self._setting_changed_dict[page]):
                self._setting_changed_dict[page].pop(setting_name)

    def check_differences(self, new_dict, old_dict):
        """Changes settings in settings manager with changes from dialog.

        Parameters
        ----------
        new_dict : dict
            Dict that has the most recent changes by user. Each key is a setting parameter
            and each item is the value.
        old_dict : dict
            Dict wtih values set at the beginning of the preferences dialog session.
        """
        page = self._list.currentItem().text().split(" ")[0].lower()
        self._values_changed(page, new_dict, old_dict)
        different_values = self._setting_changed_dict[page]

        if len(different_values) > 0:
            # change the values in SETTINGS
            for setting_name, value in different_values.items():
                try:
                    setattr(SETTINGS._settings[page], setting_name, value)
                    self._values_dict[page] = new_dict
                except:  # noqa: E722
                    continue
Exemplo n.º 11
0
class Plots(SpyderPluginWidget):
    """Plots plugin."""

    CONF_SECTION = 'plots'
    CONF_FILE = False
    DISABLE_ACTIONS_WHEN_HIDDEN = False

    def __init__(self, parent):
        SpyderPluginWidget.__init__(self, parent)

        # Widgets
        self.stack = QStackedWidget(self)
        self.stack.setStyleSheet("QStackedWidget{padding: 0px; border: 0px}")
        self.shellwidgets = {}

        # Layout
        layout = QGridLayout(self)
        layout.addWidget(self.stack)

    def get_settings(self):
        """Retrieve all Plots configuration settings."""
        return {
            name: self.get_option(name)
            for name in
            ['mute_inline_plotting', 'show_plot_outline', 'auto_fit_plotting']
        }

    # ---- Stack accesors
    def set_current_widget(self, fig_browser):
        """
        Set the currently visible fig_browser in the stack widget, refresh the
        actions of the cog menu button and move it to the layout of the new
        fig_browser.
        """
        self.stack.setCurrentWidget(fig_browser)
        # We update the actions of the options button (cog menu) and
        # we move it to the layout of the current widget.
        self._refresh_actions()
        fig_browser.setup_options_button()

    def current_widget(self):
        return self.stack.currentWidget()

    def count(self):
        return self.stack.count()

    def remove_widget(self, fig_browser):
        self.stack.removeWidget(fig_browser)

    def add_widget(self, fig_browser):
        self.stack.addWidget(fig_browser)

    # ---- Public API
    def add_shellwidget(self, shellwidget):
        """
        Register shell with figure explorer.

        This function opens a new FigureBrowser for browsing the figures
        in the shell.
        """
        shellwidget_id = id(shellwidget)
        if shellwidget_id not in self.shellwidgets:
            self.options_button.setVisible(True)
            fig_browser = FigureBrowser(self,
                                        options_button=self.options_button,
                                        background_color=MAIN_BG_COLOR)
            fig_browser.set_shellwidget(shellwidget)
            fig_browser.setup(**self.get_settings())
            fig_browser.sig_option_changed.connect(
                self.sig_option_changed.emit)
            fig_browser.thumbnails_sb.redirect_stdio.connect(
                self.main.redirect_internalshell_stdio)
            self.register_widget_shortcuts(fig_browser)
            self.add_widget(fig_browser)
            self.shellwidgets[shellwidget_id] = fig_browser
            self.set_shellwidget_from_id(shellwidget_id)
            return fig_browser

    def remove_shellwidget(self, shellwidget_id):
        # If shellwidget_id is not in self.shellwidgets, it simply means
        # that shell was not a Python-based console (it was a terminal)
        if shellwidget_id in self.shellwidgets:
            fig_browser = self.shellwidgets.pop(shellwidget_id)
            self.remove_widget(fig_browser)
            fig_browser.close()

    def set_shellwidget_from_id(self, shellwidget_id):
        if shellwidget_id in self.shellwidgets:
            fig_browser = self.shellwidgets[shellwidget_id]
            self.set_current_widget(fig_browser)

    # ---- SpyderPluginWidget API
    def get_plugin_title(self):
        """Return widget title"""
        return _('Plots')

    def get_plugin_icon(self):
        """Return plugin icon"""
        return ima.icon('hist')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.current_widget()

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return self.current_widget().actions if self.current_widget() else []

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        for fig_browser in list(self.shellwidgets.values()):
            fig_browser.setup(**self.get_settings())

    def on_first_registration(self):
        """Action to be performed on first plugin registration"""
        self.tabify(self.main.variableexplorer)
Exemplo n.º 12
0
class StackedCanvasView(CanvasView):
    """
    View that can display intents in their corresponding canvases.

    Currently, uses a stacked widget with several pages:
    one for tabview and others for different split views.

    Can be adapted as long as its internal widgets are CanvasDisplayWidgets.
    """
    def __init__(self, parent=None, model=None):
        super(StackedCanvasView, self).__init__(parent)
        if model is not None:
            self.setModel(model)

        self.canvas_display_widgets = [
            CanvasDisplayTabWidget(),
            SplitHorizontal(),
            SplitVertical(),
            SplitThreeView(),
            SplitGridView()
        ]

        ### Create stacked widget and fill pages with different canvas display widgets
        self.stackedwidget = QStackedWidget(self)
        # Create a visual layout section for the buttons that are used to switch the widgets
        self.buttonpanel = QHBoxLayout()
        self.buttonpanel.addStretch(10)
        # Create a logical button grouping that will:
        #   - show the currently selected view (button will be checked/pressed)
        #   - allow for switching the buttons/views in a mutually exclusive manner (only one can be pressed at a time)
        self.buttongroup = QButtonGroup()

        def add_canvas_display_widgets():
            for i in range(len(self.canvas_display_widgets)):
                # Add the view to the stacked widget
                self.stackedwidget.addWidget(self.canvas_display_widgets[i])
                # Create a button, using the view's recommended display icon
                button = QPushButton(self)
                button.setCheckable(True)
                button.setIcon(self.canvas_display_widgets[i].icon)
                # Add the button to the logical button group
                self.buttongroup.addButton(button, i)
                # Add the button to the visual layout section
                self.buttonpanel.addWidget(button)

        add_canvas_display_widgets()

        def set_default_canvas_display_widget():
            # The first button added to the buttongroup will be the currently selected button (and therefore view)
            self.buttongroup.button(0).setChecked(True)

        set_default_canvas_display_widget()

        # Whenever a button is switched, capture its id (corresponds to integer index in our case);
        # this will handle switching the view and displaying canvases.
        self.buttongroup.idToggled.connect(self.switch_view)

        # define outer layout & add stacked widget and button panel
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.stackedwidget)
        self.layout.addLayout(self.buttonpanel)
        self.setLayout(self.layout)

    # INTERFACING WITH TOOLBAR (see XPCSToolbar)
    def view(self):
        # from xicam.gui.canvases import ImageIntentCanvas
        view = self.stackedwidget.currentWidget()
        return view.getView()
        # return None

    # DONE INTERFACING

    def switch_view(self, id, toggled):
        # when toggled==True, the the button is the new button that was switched to.
        # when False, the button is the previous button
        view = self.canvas_display_widgets[id]
        if not toggled:
            ...
            # TODO: is there anything we need to do here (re: cleanup)?

        else:
            self.stackedwidget.setCurrentIndex(id)
            self.show_canvases()

    def show_canvases(self):
        self.stackedwidget.currentWidget().clear_canvases()
        self.stackedwidget.currentWidget().show_canvases(
            self._canvas_manager.canvases(self.model()))
Exemplo n.º 13
0
class MixerPanel(QFrame):
    '''A color mixer to hook up to an image.
    You pass the image you the panel to operate on
    and it operates on that image in place. You also
    pass a callback to be called to trigger a refresh.
    This callback is called every time the mixer modifies
    your image.'''
    def __init__(self, img):
        QFrame.__init__(self)
        # self.setFrameStyle(QFrame.Box | QFrame.Sunken)

        self.img = img
        self.mixer = ColorMixer(self.img)
        self.callback = None

        #---------------------------------------------------------------
        # ComboBox
        #---------------------------------------------------------------

        self.combo_box_entries = ['RGB Color', 'HSV Color',
                                  'Brightness/Contrast',
                                  'Gamma',
                                  'Gamma (Sigmoidal)']
        self.combo_box = QComboBox()
        for entry in self.combo_box_entries:
            self.combo_box.addItem(entry)
        self.combo_box.currentIndexChanged.connect(self.combo_box_changed)

        #---------------------------------------------------------------
        # RGB color sliders
        #---------------------------------------------------------------

        # radio buttons
        self.rgb_add = QRadioButton('Additive')
        self.rgb_mul = QRadioButton('Multiplicative')
        self.rgb_mul.toggled.connect(self.rgb_radio_changed)
        self.rgb_add.toggled.connect(self.rgb_radio_changed)

        # sliders
        rs = IntelligentSlider('R', 0.51, -255, self.rgb_changed)
        gs = IntelligentSlider('G', 0.51, -255, self.rgb_changed)
        bs = IntelligentSlider('B', 0.51, -255, self.rgb_changed)
        self.rs = rs
        self.gs = gs
        self.bs = bs

        self.rgb_widget = QWidget()
        self.rgb_widget.layout = QGridLayout(self.rgb_widget)
        self.rgb_widget.layout.addWidget(self.rgb_add, 0, 0, 1, 3)
        self.rgb_widget.layout.addWidget(self.rgb_mul, 1, 0, 1, 3)
        self.rgb_widget.layout.addWidget(self.rs, 2, 0)
        self.rgb_widget.layout.addWidget(self.gs, 2, 1)
        self.rgb_widget.layout.addWidget(self.bs, 2, 2)

        #---------------------------------------------------------------
        # HSV sliders
        #---------------------------------------------------------------
        # radio buttons
        self.hsv_add = QRadioButton('Additive')
        self.hsv_mul = QRadioButton('Multiplicative')
        self.hsv_mul.toggled.connect(self.hsv_radio_changed)
        self.hsv_mul.toggled.connect(self.hsv_radio_changed)

        # sliders
        hs = IntelligentSlider('H', 0.36, -180, self.hsv_changed)
        ss = IntelligentSlider('S', 0.002, 0, self.hsv_changed)
        vs = IntelligentSlider('V', 0.002, 0, self.hsv_changed)
        self.hs = hs
        self.ss = ss
        self.vs = vs

        self.hsv_widget = QWidget()
        self.hsv_widget.layout = QGridLayout(self.hsv_widget)
        self.hsv_widget.layout.addWidget(self.hsv_add, 0, 0, 1, 3)
        self.hsv_widget.layout.addWidget(self.hsv_mul, 1, 0, 1, 3)
        self.hsv_widget.layout.addWidget(self.hs, 2, 0)
        self.hsv_widget.layout.addWidget(self.ss, 2, 1)
        self.hsv_widget.layout.addWidget(self.vs, 2, 2)

        #---------------------------------------------------------------
        # Brightness/Contrast sliders
        #---------------------------------------------------------------
        # sliders
        cont = IntelligentSlider('x', 0.002, 0, self.bright_changed)
        bright = IntelligentSlider('+', 0.51, -255, self.bright_changed)
        self.cont = cont
        self.bright = bright

        # layout
        self.bright_widget = QWidget()
        self.bright_widget.layout = QGridLayout(self.bright_widget)
        self.bright_widget.layout.addWidget(self.cont, 0, 0)
        self.bright_widget.layout.addWidget(self.bright, 0, 1)

        #----------------------------------------------------------------------
        # Gamma Slider
        #----------------------------------------------------------------------
        gamma = IntelligentSlider('gamma', 0.005, 0, self.gamma_changed)
        self.gamma = gamma

        # layout
        self.gamma_widget = QWidget()
        self.gamma_widget.layout = QGridLayout(self.gamma_widget)
        self.gamma_widget.layout.addWidget(self.gamma, 0, 0)

        #---------------------------------------------------------------
        # Sigmoid Gamma sliders
        #---------------------------------------------------------------
        # sliders
        alpha = IntelligentSlider('alpha', 0.011, 1, self.sig_gamma_changed)
        beta = IntelligentSlider('beta', 0.012, 0, self.sig_gamma_changed)
        self.a_gamma = alpha
        self.b_gamma = beta

        # layout
        self.sig_gamma_widget = QWidget()
        self.sig_gamma_widget.layout = QGridLayout(self.sig_gamma_widget)
        self.sig_gamma_widget.layout.addWidget(self.a_gamma, 0, 0)
        self.sig_gamma_widget.layout.addWidget(self.b_gamma, 0, 1)

        #---------------------------------------------------------------
        # Buttons
        #---------------------------------------------------------------
        self.commit_button = QPushButton('Commit')
        self.commit_button.clicked.connect(self.commit_changes)
        self.revert_button = QPushButton('Revert')
        self.revert_button.clicked.connect(self.revert_changes)

        #---------------------------------------------------------------
        # Mixer Layout
        #---------------------------------------------------------------
        self.sliders = QStackedWidget()
        self.sliders.addWidget(self.rgb_widget)
        self.sliders.addWidget(self.hsv_widget)
        self.sliders.addWidget(self.bright_widget)
        self.sliders.addWidget(self.gamma_widget)
        self.sliders.addWidget(self.sig_gamma_widget)

        self.layout = QGridLayout(self)
        self.layout.addWidget(self.combo_box, 0, 0)
        self.layout.addWidget(self.sliders, 1, 0)
        self.layout.addWidget(self.commit_button, 2, 0)
        self.layout.addWidget(self.revert_button, 3, 0)

        #---------------------------------------------------------------
        # State Initialization
        #---------------------------------------------------------------

        self.combo_box.setCurrentIndex(0)
        self.rgb_mul.setChecked(True)
        self.hsv_mul.setChecked(True)

    def set_callback(self, callback):
        self.callback = callback

    def combo_box_changed(self, index):
        self.sliders.setCurrentIndex(index)
        self.reset()

    def rgb_radio_changed(self):
        self.reset()

    def hsv_radio_changed(self):
        self.reset()

    def reset(self):
        self.reset_sliders()
        self.mixer.set_to_stateimg()
        if self.callback:
            self.callback()

    def reset_sliders(self):
        # handle changing the conversion factors necessary
        if self.rgb_add.isChecked():
            self.rs.set_conv_fac(0.51, -255)
            self.rs.set_value(0)
            self.gs.set_conv_fac(0.51, -255)
            self.gs.set_value(0)
            self.bs.set_conv_fac(0.51, -255)
            self.bs.set_value(0)
        else:
            self.rs.set_conv_fac(0.002, 0)
            self.rs.set_value(1.)
            self.gs.set_conv_fac(0.002, 0)
            self.gs.set_value(1.)
            self.bs.set_conv_fac(0.002, 0)
            self.bs.set_value(1.)

        self.hs.set_value(0)
        if self.hsv_add.isChecked():
            self.ss.set_conv_fac(0.002, -1)
            self.ss.set_value(0)
            self.vs.set_conv_fac(0.002, -1)
            self.vs.set_value(0)
        else:
            self.ss.set_conv_fac(0.002, 0)
            self.ss.set_value(1.)
            self.vs.set_conv_fac(0.002, 0)
            self.vs.set_value(1.)

        self.bright.set_value(0)
        self.cont.set_value(1.)

        self.gamma.set_value(1)
        self.a_gamma.set_value(1)
        self.b_gamma.set_value(0.5)

    def rgb_changed(self, name, val):
        if name == 'R':
            channel = self.mixer.RED
        elif name == 'G':
            channel = self.mixer.GREEN
        else:
            channel = self.mixer.BLUE

        if self.rgb_mul.isChecked():
            self.mixer.multiply(channel, val)
        elif self.rgb_add.isChecked():
            self.mixer.add(channel, val)
        else:
            pass

        if self.callback:
            self.callback()

    def hsv_changed(self, name, val):
        h = self.hs.val()
        s = self.ss.val()
        v = self.vs.val()

        if self.hsv_mul.isChecked():
            self.mixer.hsv_multiply(h, s, v)
        elif self.hsv_add.isChecked():
            self.mixer.hsv_add(h, s, v)
        else:
            pass

        if self.callback:
            self.callback()

    def bright_changed(self, name, val):
        b = self.bright.val()
        c = self.cont.val()
        self.mixer.brightness(c, b)

        if self.callback:
            self.callback()

    def gamma_changed(self, name, val):
        self.mixer.gamma(val)

        if self.callback:
            self.callback()

    def sig_gamma_changed(self, name, val):
        ag = self.a_gamma.val()
        bg = self.b_gamma.val()
        self.mixer.sigmoid_gamma(ag, bg)

        if self.callback:
            self.callback()

    def commit_changes(self):
        self.mixer.commit_changes()
        self.reset_sliders()

    def revert_changes(self):
        self.mixer.revert()
        self.reset_sliders()

        if self.callback:
            self.callback()
Exemplo n.º 14
0
class RateLawWidget(QWidget):
    """
    Rate Law widget
    """
    redirect_stdio = Signal(bool)

    def __init__(self, parent, max_entries=100):
        "Initialize Various list objects before assignment"

        displaylist = []
        displaynamelist = []
        infixmod = []
        infixlist = []
        desclist = []
        parameternamelist = []
        parameterdesclist = []
        buttonlist = []
        xmldoc = minidom.parse(
            os.path.dirname(os.path.abspath(__file__)) + '\\ratelaw2_0_3.xml')

        lawlistxml = xmldoc.getElementsByTagName('law')

        o = 0
        for s in lawlistxml:
            o = o + 1

        parameternamelistlist = [0 for x in range(o)]
        parameterdesclistlist = [0 for x in range(o)]
        """i is the number of laws currently in the xml file"""
        i = 0
        """
        Parsing xml: Acquiring rate law name, description, and list of parameter information
        """
        for s in lawlistxml:
            """Gets Latec Expression"""
            displaylist.append(s.getAttribute('display'))
            """Gets Rate-Law Name"""
            displaynamelist.append(s.getAttribute('displayName'))
            """"Gets Raw Rate-Law expression"""
            infixlist.append(s.getAttribute('infixExpression'))
            """Gets description statement"""
            desclist.append(s.getAttribute('description'))
            """Gets listOfParameters Object"""
            parameterlist = s.getElementsByTagName('listOfParameters')[0]
            """Gets a list of parameters within ListOfParameters object"""
            parameters = parameterlist.getElementsByTagName('parameter')

            for param in parameters:
                parameternamelist.append(param.attributes['name'].value)
                parameterdesclist.append(param.attributes['description'].value)

            parameternamelistlist[i] = parameternamelist
            parameterdesclistlist[i] = parameterdesclist

            parameternamelist = []
            parameterdesclist = []
            i = i + 1

        SLElistlist = [0 for x in range(i)]
        PLElistlist = [0 for x in range(i)]
        ILElistlist = [0 for x in range(i)]
        paramLElistlist = [0 for x in range(i)]
        numlistlist = [0 for x in range(i)]

        QWidget.__init__(self, parent)

        self.setWindowTitle("Rate Law Library")

        self.output = None
        self.error_output = None
        self._last_wdir = None
        self._last_args = None
        self._last_pythonpath = None

        self.lawlist = QListWidget()
        self.lawpage = QStackedWidget()
        for j in range(i):
            item = QListWidgetItem(displaynamelist[j])
            self.lawlist.addItem(item)
            self.lawdetailpage = QWidget()
            setup_group = QGroupBox(displaynamelist[j])
            infixmod.append(infixlist[j].replace("___", " "))
            setup_label = QLabel(infixmod[j])
            setup_label.setWordWrap(True)

            desc_group = QGroupBox("Description")
            desc_label = QLabel(desclist[j])
            desc_label.setWordWrap(True)

            param_label = QGridLayout()
            nm = QLabel("Name:")
            des = QLabel("Description:")
            repl = QLabel("Replace with:")
            param_label.addWidget(nm, 0, 0)
            param_label.addWidget(des, 0, 1)
            param_label.addWidget(repl, 0, 2)
            """g is the total number of alterable values"""
            """t is the total number of alterable non-parameters"""
            t = 1

            snum = 0
            pnum = 0
            inum = 0
            """range of N is the max number of possible substrates OR products"""
            N = 5
            for n in range(N):
                nl = n + 1
                if (infixmod[j].find('S%s' % nl) > -1):
                    z = QLabel('S%s is present' % nl)
                    param_label.addWidget(z, t, 0)
                    snum = snum + 1
                    t = t + 1

            for n in range(N):
                nl = n + 1
                if (infixmod[j].find('P%s' % nl) > -1):
                    z = QLabel('P%s is present' % nl)
                    param_label.addWidget(z, t, 0)
                    pnum = pnum + 1
                    t = t + 1

            for n in range(N):
                nl = n + 1
                if (infixmod[j].find('I%s' % nl) > -1):
                    z = QLabel('I%s is present' % nl)
                    param_label.addWidget(z, t, 0)
                    inum = inum + 1
                    t = t + 1
            """Initialize lists of list of parameter lineedit"""
            length = len(parameternamelistlist[j])
            for b in range(length):
                p = QLabel("%s :" % parameternamelistlist[j][b])
                param_label.addWidget(p, b + t, 0)
                d = QLabel("'%s'" % parameterdesclistlist[j][b])
                param_label.addWidget(d, b + t, 1)

            Slineeditlist = [0 for x in range(snum)]
            Plineeditlist = [0 for x in range(pnum)]
            Ilineeditlist = [0 for x in range(inum)]
            paramlineeditlist = [0 for x in range(length)]

            editcount = 1
            """Place lineedit widgets for parameters"""
            for s in range(snum):
                Slineeditlist[s] = QLineEdit()
                param_label.addWidget(Slineeditlist[s], editcount, 2)
                editcount = editcount + 1

            SLElistlist[j] = Slineeditlist

            for s in range(pnum):
                Plineeditlist[s] = QLineEdit()
                param_label.addWidget(Plineeditlist[s], editcount, 2)
                editcount = editcount + 1

            PLElistlist[j] = Plineeditlist

            for s in range(inum):
                Ilineeditlist[s] = QLineEdit()
                param_label.addWidget(Ilineeditlist[s], editcount, 2)
                editcount = editcount + 1

            ILElistlist[j] = Ilineeditlist

            for s in range(length):
                paramlineeditlist[s] = QLineEdit()
                param_label.addWidget(paramlineeditlist[s], editcount, 2)
                editcount = editcount + 1

            paramLElistlist[j] = paramlineeditlist
            """Necessary lists for editable parameters. Housekeeping essentially."""
            numlistlist[j] = [snum, pnum, inum, length]
            charlist = ["S", "P", "I"]

            buttonlist.append(QPushButton(self))
            buttonlist[j].setText("Insert Rate Law: %s" % displaynamelist[j])

            # Warning: do not try to regroup the following QLabel contents with
            # widgets above -- this string was isolated here in a single QLabel
            # on purpose: to fix Issue 863
            """Page formatting"""
            setup_layout = QVBoxLayout()
            setup_layout.addWidget(setup_label)
            setup_group.setLayout(setup_layout)

            desc_group.setLayout(param_label)

            vlayout2 = QVBoxLayout()
            vlayout2.addWidget(setup_group)
            vlayout2.addWidget(desc_group)
            vlayout2.addWidget(buttonlist[j])
            vlayout2.addStretch(1)
            self.lawdetailpage.setLayout(vlayout2)
            self.lawpage.addWidget(self.lawdetailpage)
        """Set up button functionality"""
        for k in range(47):
            buttonlist[k].clicked.connect(
                self.pressbutton(infixmod[k], SLElistlist[k], PLElistlist[k],
                                 ILElistlist[k], paramLElistlist[k],
                                 parameternamelistlist[k], numlistlist[k],
                                 charlist, k))

        self.lawlist.currentRowChanged.connect(self.lawpage.setCurrentIndex)
        self.lawlist.setCurrentRow(0)
        self.lawlist.setMinimumWidth(self.lawlist.sizeHintForColumn(0))
        """Set up high-level widget formatting."""
        hsplitter = QSplitter()
        hsplitter.addWidget(self.lawlist)
        hsplitter.addWidget(self.lawpage)

        layout = QVBoxLayout()
        layout.addWidget(hsplitter)
        self.setLayout(layout)

    """Testing LineEdit functionality"""

    def entertext(self, le, string, stringlist):
        def entertextreal():
            stuff = self.le.text()
            string = "%s" % stuff
            stringlist[0].replace("vo", "%s" % string)

        return entertextreal

    """Analyze lineedits on page, replace appropriate text, then paste text in console"""

    def pressbutton(ratelaw, string, SLElist, PLElist, ILElist, paramLElist,
                    paramlist, numlist, charlist, j):
        def pressbuttoninsert():
            stringmod = string
            for k in range(4):

                for i in range(numlist[k]):
                    l = i + 1
                    if (k == 0):
                        stuff = SLElist[i].text()
                        if (stuff.find(" ") != -1 or stuff.find('"') != -1):
                            pass
                        elif (stuff == ""):
                            pass
                        else:
                            stringmod = stringmod.replace(
                                charlist[k] + str(l), "%s" % stuff)
                    if (k == 1):
                        stuff = PLElist[i].text()
                        if (stuff.find(" ") != -1 or stuff.find('"') != -1):
                            pass
                        elif (stuff == ""):
                            pass
                        else:
                            stringmod = stringmod.replace(
                                charlist[k] + str(l), "%s" % stuff)
                    if (k == 2):
                        stuff = ILElist[i].text()
                        if (stuff.find(" ") != -1 or stuff.find('"') != -1):
                            pass
                        elif (stuff == ""):
                            pass
                        else:
                            stringmod = stringmod.replace(
                                charlist[k] + str(l), "%s" % stuff)
                    if (k == 3):
                        stuff = paramLElist[i].text()
                        if (stuff.find(" ") != -1 or stuff.find('"') != -1):
                            pass
                        elif (stuff == ""):
                            pass
                        else:
                            stringmod = stringmod.replace(
                                paramlist[i], "%s" % stuff)
            ratelaw.insertText(stringmod)

        return pressbuttoninsert
Exemplo n.º 15
0
class Plots(SpyderPluginWidget):
    """Plots plugin."""

    CONF_SECTION = 'plots'
    CONFIGWIDGET_CLASS = PlotsConfigPage
    DISABLE_ACTIONS_WHEN_HIDDEN = False
    sig_option_changed = Signal(str, object)

    def __init__(self, parent):
        SpyderPluginWidget.__init__(self, parent)

        # Widgets
        self.stack = QStackedWidget(self)
        self.shellwidgets = {}

        # Layout
        layout = QGridLayout(self)
        layout.addWidget(self.stack)

        # Initialize plugin
        self.initialize_plugin()

    def get_settings(self):
        """Retrieve all Plots configuration settings."""
        return {
            name: self.get_option(name)
            for name in ['mute_inline_plotting', 'show_plot_outline']
        }

    # ---- Stack accesors
    def set_current_widget(self, fig_browser):
        """
        Set the currently visible fig_browser in the stack widget, refresh the
        actions of the cog menu button and move it to the layout of the new
        fig_browser.
        """
        self.stack.setCurrentWidget(fig_browser)
        # We update the actions of the options button (cog menu) and
        # we move it to the layout of the current widget.
        self.refresh_actions()
        fig_browser.setup_options_button()

    def current_widget(self):
        return self.stack.currentWidget()

    def count(self):
        return self.stack.count()

    def remove_widget(self, fig_browser):
        self.stack.removeWidget(fig_browser)

    def add_widget(self, fig_browser):
        self.stack.addWidget(fig_browser)

    # ---- Public API
    def add_shellwidget(self, shellwidget):
        """
        Register shell with figure explorer.

        This function opens a new FigureBrowser for browsing the figures
        in the shell.
        """
        shellwidget_id = id(shellwidget)
        if shellwidget_id not in self.shellwidgets:
            self.options_button.setVisible(True)
            fig_browser = FigureBrowser(self,
                                        options_button=self.options_button)
            fig_browser.set_shellwidget(shellwidget)
            fig_browser.setup(**self.get_settings())
            fig_browser.sig_option_changed.connect(
                self.sig_option_changed.emit)
            fig_browser.thumbnails_sb.redirect_stdio.connect(
                self.main.redirect_internalshell_stdio)
            self.add_widget(fig_browser)
            self.shellwidgets[shellwidget_id] = fig_browser
            self.set_shellwidget_from_id(shellwidget_id)
            return fig_browser

    def remove_shellwidget(self, shellwidget_id):
        # If shellwidget_id is not in self.shellwidgets, it simply means
        # that shell was not a Python-based console (it was a terminal)
        if shellwidget_id in self.shellwidgets:
            fig_browser = self.shellwidgets.pop(shellwidget_id)
            self.remove_widget(fig_browser)
            fig_browser.close()

    def set_shellwidget_from_id(self, shellwidget_id):
        if shellwidget_id in self.shellwidgets:
            fig_browser = self.shellwidgets[shellwidget_id]
            self.set_current_widget(fig_browser)

    # ---- SpyderPluginWidget API
    def get_plugin_title(self):
        """Return widget title"""
        return _('Plots')

    def get_plugin_icon(self):
        """Return plugin icon"""
        return ima.icon('hist')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.current_widget()

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True

    def refresh_plugin(self):
        """Refresh widget"""
        pass

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return self.current_widget().actions if self.current_widget() else []

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.main.add_dockwidget(self)

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        for fig_browser in list(self.shellwidgets.values()):
            fig_browser.setup(**self.get_settings())
Exemplo n.º 16
0
class ArrayEditor(QDialog):
    """Array Editor Dialog"""    
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        
        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)
        
        self.data = None
        self.arraywidget = None
        self.stack = None
        self.layout = None
        # Values for 3d array editor
        self.dim_indexes = [{}, {}, {}]
        self.last_dim = 0  # Adjust this for changing the startup dimension
        
    def setup_and_check(self, data, title='', readonly=False,
                        xlabels=None, ylabels=None):
        """
        Setup ArrayEditor:
        return False if data is not supported, True otherwise
        """
        self.data = data
        is_record_array = data.dtype.names is not None
        is_masked_array = isinstance(data, np.ma.MaskedArray)
        if data.size == 0:
            self.error(_("Array is empty"))
            return False
        if data.ndim > 3:
            self.error(_("Arrays with more than 3 dimensions are not supported"))
            return False
        if xlabels is not None and len(xlabels) != self.data.shape[1]:
            self.error(_("The 'xlabels' argument length do no match array "
                         "column number"))
            return False
        if ylabels is not None and len(ylabels) != self.data.shape[0]:
            self.error(_("The 'ylabels' argument length do no match array row "
                         "number"))
            return False
        if not is_record_array:
            dtn = data.dtype.name
            if dtn not in SUPPORTED_FORMATS and not dtn.startswith('str') \
               and not dtn.startswith('unicode'):
                arr = _("%s arrays") % data.dtype.name
                self.error(_("%s are currently not supported") % arr)
                return False
        
        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.setWindowIcon(ima.icon('arredit'))
        if title:
            title = to_text_string(title) + " - " + _("NumPy array")
        else:
            title = _("Array editor")
        if readonly:
            title += ' (' + _('read only') + ')'
        self.setWindowTitle(title)
        self.resize(600, 500)
        
        # Stack widget
        self.stack = QStackedWidget(self)
        if is_record_array:
            for name in data.dtype.names:
                self.stack.addWidget(ArrayEditorWidget(self, data[name],
                                                   readonly, xlabels, ylabels))
        elif is_masked_array:
            self.stack.addWidget(ArrayEditorWidget(self, data, readonly,
                                                   xlabels, ylabels))
            self.stack.addWidget(ArrayEditorWidget(self, data.data, readonly,
                                                   xlabels, ylabels))
            self.stack.addWidget(ArrayEditorWidget(self, data.mask, readonly,
                                                   xlabels, ylabels))
        elif data.ndim == 3:
            pass
        else:
            self.stack.addWidget(ArrayEditorWidget(self, data, readonly,
                                                   xlabels, ylabels))
        self.arraywidget = self.stack.currentWidget()
        self.stack.currentChanged.connect(self.current_widget_changed)
        self.layout.addWidget(self.stack, 1, 0)

        # Buttons configuration
        btn_layout = QHBoxLayout()
        if is_record_array or is_masked_array or data.ndim == 3:
            if is_record_array:
                btn_layout.addWidget(QLabel(_("Record array fields:")))
                names = []
                for name in data.dtype.names:
                    field = data.dtype.fields[name]
                    text = name
                    if len(field) >= 3:
                        title = field[2]
                        if not is_text_string(title):
                            title = repr(title)
                        text += ' - '+title
                    names.append(text)
            else:
                names = [_('Masked data'), _('Data'), _('Mask')]
            if data.ndim == 3:
                # QSpinBox
                self.index_spin = QSpinBox(self, keyboardTracking=False)
                self.index_spin.valueChanged.connect(self.change_active_widget)
                # QComboBox
                names = [str(i) for i in range(3)]
                ra_combo = QComboBox(self)
                ra_combo.addItems(names)
                ra_combo.currentIndexChanged.connect(self.current_dim_changed)    
                # Adding the widgets to layout
                label = QLabel(_("Axis:"))
                btn_layout.addWidget(label)
                btn_layout.addWidget(ra_combo)
                self.shape_label = QLabel()
                btn_layout.addWidget(self.shape_label)
                label = QLabel(_("Index:"))
                btn_layout.addWidget(label)
                btn_layout.addWidget(self.index_spin)
                self.slicing_label = QLabel()
                btn_layout.addWidget(self.slicing_label)
                # set the widget to display when launched
                self.current_dim_changed(self.last_dim)
            else:
                ra_combo = QComboBox(self)
                ra_combo.currentIndexChanged.connect(self.stack.setCurrentIndex)
                ra_combo.addItems(names)
                btn_layout.addWidget(ra_combo)
            if is_masked_array:
                label = QLabel(_("<u>Warning</u>: changes are applied separately"))
                label.setToolTip(_("For performance reasons, changes applied "\
                                   "to masked array won't be reflected in "\
                                   "array's data (and vice-versa)."))
                btn_layout.addWidget(label)
            btn_layout.addStretch()
        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        bbox.accepted.connect(self.accept)
        bbox.rejected.connect(self.reject)
        btn_layout.addWidget(bbox)
        self.layout.addLayout(btn_layout, 2, 0)

        self.setMinimumSize(400, 300)
        
        # Make the dialog act as a window
        self.setWindowFlags(Qt.Window)
        
        return True
            
    def current_widget_changed(self, index):
        self.arraywidget = self.stack.widget(index)     
            
    def change_active_widget(self, index):
        """
        This is implemented for handling negative values in index for
        3d arrays, to give the same behavior as slicing
        """
        string_index = [':']*3
        string_index[self.last_dim] = '<font color=red>%i</font>'
        self.slicing_label.setText((r"Slicing: [" + ", ".join(string_index) +
                                "]") % index)
        if index < 0:
            data_index = self.data.shape[self.last_dim] + index
        else:
            data_index = index
        slice_index = [slice(None)]*3
        slice_index[self.last_dim] = data_index

        stack_index = self.dim_indexes[self.last_dim].get(data_index)
        if stack_index == None:
            stack_index = self.stack.count()
            self.stack.addWidget(ArrayEditorWidget(self,
                                                   self.data[slice_index]))
            self.dim_indexes[self.last_dim][data_index] = stack_index
            self.stack.update()
        self.stack.setCurrentIndex(stack_index)

    def current_dim_changed(self, index):
        """
        This change the active axis the array editor is plotting over
        in 3D
        """
        self.last_dim = index
        string_size = ['%i']*3
        string_size[index] = '<font color=red>%i</font>'
        self.shape_label.setText(('Shape: (' + ', '.join(string_size) +
                                 ')    ') % self.data.shape)
        if self.index_spin.value() != 0:
            self.index_spin.setValue(0)
        else:
            # this is done since if the value is currently 0 it does not emit
            # currentIndexChanged(int)
            self.change_active_widget(0)
        self.index_spin.setRange(-self.data.shape[index],
                                 self.data.shape[index]-1)

    @Slot()
    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.stack.count()):
            self.stack.widget(index).accept_changes()
        QDialog.accept(self)
        
    def get_value(self):
        """Return modified array -- this is *not* a copy"""
        # It is import to avoid accessing Qt C++ object as it has probably
        # already been destroyed, due to the Qt.WA_DeleteOnClose attribute
        return self.data

    def error(self, message):
        """An error occured, closing the dialog box"""
        QMessageBox.critical(self, _("Array editor"), message)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.reject()

    @Slot()
    def reject(self):
        """Reimplement Qt method"""
        if self.arraywidget is not None:
            for index in range(self.stack.count()):
                self.stack.widget(index).reject_changes()
        QDialog.reject(self)
Exemplo n.º 17
0
class FeaturesPlotWidget(CustomWidget):
    call_manage_settings = Signal()

    def __init__(self, *args, **kwargs):

        # define status images
        self.status_icons = {
            -2:
            QPixmap(
                os.path.join(os.path.dirname(__file__), 'icons',
                             'depth_status_delay.png')),
            -1:
            QPixmap(
                os.path.join(os.path.dirname(__file__), 'icons',
                             'depth_status_in_use.png')),
            1:
            QPixmap(
                os.path.join(os.path.dirname(__file__), 'icons',
                             'depth_status_done.png')),
            0:
            QPixmap(
                os.path.join(os.path.dirname(__file__), 'icons',
                             'depth_status_off.png')),
        }

        # Settings
        self.subject_settings = None
        self.procedure_settings = None
        self.depth_settings = None
        self.features_settings = None

        # Plot options
        self.plot_config = {}
        self.y_range = uVRANGE
        self.plot_stack = QStackedWidget()
        # generate a dict {chan_label: {Feature:[stack idx, latest_datum]}}
        self.stack_dict = {}

        # shared memory to display the currently monitored electrode
        self.monitored_channel_mem = QSharedMemory()
        self.monitored_channel_mem.setKey("MonitoredChannelMemory")
        self.monitored_channel_mem.attach(QSharedMemory.ReadOnly)

        # wrap up init
        super(FeaturesPlotWidget, self).__init__(*args, **kwargs)
        self.move(WINDOWDIMS_FEATURES[0], WINDOWDIMS_FEATURES[1])
        self.resize(WINDOWDIMS_FEATURES[2], WINDOWDIMS_FEATURES[3])
        self.setMaximumWidth(WINDOWDIMS_FEATURES[2])

        # initialize plots
        self.layout().addWidget(self.plot_stack)
        self.refresh_axes()  # Extra time on purpose.

        # Define and start processes
        # will only start processes when settings are received
        self.depth_wrapper = ProcessWrapper('Depth_Process')
        self.depth_process_running = False

        self.features_wrapper = ProcessWrapper('Features_Process')
        self.features_process_running = False

    def create_control_panel(self):
        # define Qt GUI elements
        layout = QHBoxLayout()

        layout_L = QVBoxLayout()
        layout_L1 = QHBoxLayout()

        # layout_L1.addSpacing(10)
        layout_L1.addWidget(
            QLabel("Electrode: ", alignment=Qt.AlignVCenter | Qt.AlignRight))
        # Channel selection
        self.chan_select = QComboBox()
        self.chan_select.addItem("None")
        self.chan_select.setMinimumWidth(70)
        self.chan_select.setEnabled(False)
        layout_L1.addWidget(self.chan_select)

        layout_L1.addSpacing(20)

        # features selection
        layout_L1.addWidget(
            QLabel("Feature set: ", alignment=Qt.AlignVCenter | Qt.AlignRight))
        self.feature_select = QComboBox()
        self.feature_select.setMinimumWidth(60)
        self.feature_select.addItems(['Raw', 'Mapping'])
        self.feature_select.setCurrentIndex(0)
        layout_L1.addWidget(self.feature_select)

        layout_L.addLayout(layout_L1)
        layout_L.addSpacing(5)

        layout_L2 = QHBoxLayout()
        layout_L2.addSpacing(10)
        layout_L2.addWidget(
            QLabel("+/- ", alignment=Qt.AlignVCenter | Qt.AlignRight))
        self.range_edit = QLineEdit("{:.2f}".format(uVRANGE))
        self.range_edit.setMaximumWidth(50)
        layout_L2.addWidget(self.range_edit)

        layout_L2.addSpacing(30)

        self.do_hp = QCheckBox('HP')
        self.do_hp.setChecked(True)
        layout_L2.addWidget(self.do_hp)

        layout_L2.addSpacing(30)

        self.sweep_control = QCheckBox("Match SweepGUI.")
        self.sweep_control.setChecked(True)
        self.sweep_control.setEnabled(True)
        layout_L2.addWidget(self.sweep_control)

        layout_L.addLayout(layout_L2)

        layout_R = QHBoxLayout()

        self.bt_refresh = QPushButton("Refresh")
        self.bt_refresh.setMaximumWidth(50)
        layout_R.addWidget(self.bt_refresh)

        layout_R.addSpacing(20)

        self.btn_settings = QPushButton("Settings")
        self.btn_settings.setMaximumWidth(50)
        layout_R.addWidget(self.btn_settings)

        layout_R.addSpacing(20)

        self.features_process_btn = QPushButton('Features')
        self.features_process_btn.setMaximumWidth(50)
        self.features_process_btn.setStyleSheet("QPushButton { color: white; "
                                                "background-color : red; "
                                                "border-color : red; "
                                                "border-width: 2px}")
        self.features_process_btn.clicked.connect(
            self.features_process_btn_callback)
        layout_R.addWidget(self.features_process_btn)

        layout_R.addSpacing(5)

        self.depth_process_btn = QPushButton('Record')
        self.depth_process_btn.setMaximumWidth(50)
        self.depth_process_btn.setStyleSheet("QPushButton { color: white; "
                                             "background-color : red; "
                                             "border-color : red; "
                                             "border-width: 2px}")
        self.depth_process_btn.clicked.connect(self.depth_process_btn_callback)
        layout_R.addWidget(self.depth_process_btn)

        layout_R.addSpacing(20)

        self.status_label = QLabel()
        self.status_label.setPixmap(self.status_icons[0])
        layout_R.addWidget(self.status_label)
        layout_R.addSpacing(10)

        layout.addLayout(layout_L)
        layout.addStretch()
        layout.addLayout(layout_R)

        # layout.addSpacing(10)
        self.layout().addLayout(layout)

        # callbacks
        self.btn_settings.clicked.connect(self.call_manage_settings.emit)
        self.chan_select.currentIndexChanged.connect(
            self.manage_feat_chan_select)
        self.feature_select.currentIndexChanged.connect(
            self.manage_feat_chan_select)
        self.sweep_control.clicked.connect(self.manage_sweep_control)
        self.range_edit.editingFinished.connect(self.manage_range_edit)
        self.bt_refresh.clicked.connect(self.manage_refresh)

    def depth_process_btn_callback(self):
        # kill
        if self.depth_process_running:
            self.manage_depth_process(False)
            self.manage_nsp(False)
        else:
            # if we terminate and re-start the processes, we need to re-enable the shared memory
            self.depth_wrapper.manage_shared_memory()

            # re-send the settings
            self.depth_wrapper.send_settings(self.depth_settings)

            # start nsp recording
            if self.manage_nsp(True) == 0:
                # re-start the worker
                self.manage_depth_process(True)

    def features_process_btn_callback(self):
        # kill
        if self.features_process_running:
            self.manage_feature_process(False)
        else:
            # if we terminate and re-start the processes, we need to re-enable the shared memory
            self.features_wrapper.manage_shared_memory()

            # re-send the settings
            self.features_wrapper.send_settings(self.features_settings)

            # re-start the worker
            self.manage_feature_process(True)

    # GUI Callbacks
    def manage_feat_chan_select(self):
        self.plot_stack.setCurrentIndex(
            self.stack_dict[self.chan_select.currentText()][
                self.feature_select.currentText()][0])

    def manage_sweep_control(self):
        if self.sweep_control.isChecked(
        ) and self.monitored_channel_mem.isAttached():
            self.chan_select.setEnabled(False)
            self.do_hp.setEnabled(False)
            self.range_edit.setEnabled(False)
            self.read_from_shared_memory()
        else:
            self.chan_select.setEnabled(True)
            self.do_hp.setEnabled(True)
            self.range_edit.setEnabled(True)

    def manage_range_edit(self):
        # need to do it like this because if we simply read the QLineEdit.text() on update calls, it breaks during
        # typing the new range values.
        self.y_range = float(self.range_edit.text())

    @staticmethod
    def parse_patient_name(full_name):
        # parse the subject information
        names = full_name.split(' ')
        m_name = ''
        m_idx = -1
        l_idx = -1
        for idx, n in enumerate(names):
            if all([x.isupper() for x in n]):
                m_idx = idx
                l_idx = idx + 1
                m_name = n
                break

        f_name = str.join(' ', names[:m_idx])
        l_name = str.join(' ', names[l_idx:])
        return f_name, m_name, l_name

    def manage_nsp(self, on_off):
        f_name, m_name, l_name = self.parse_patient_name(
            self.subject_settings['name'])
        file_info = {
            'filename':
            os.path.normpath(
                os.path.join(
                    BASEPATH, self.subject_settings['id'],
                    self.procedure_settings['date'].strftime('%m%d%y') + '_' +
                    self.subject_settings['id'] + '_' +
                    self.procedure_settings['target_name'] + '_' +
                    self.procedure_settings['recording_config'])),
            'comment':
            self.subject_settings['NSP_comment'],
            'patient_info': {
                'ID': self.subject_settings['id'],
                # if only single name, returned in l_name
                'firstname': f_name if f_name else l_name,
                'middlename': m_name,  # TODO: implement MiddleName
                'lastname': l_name,
                'DOBMonth': self.subject_settings['birthday'].month,
                'DOBDay': self.subject_settings['birthday'].day,
                'DOBYear': self.subject_settings['birthday'].year
            }
        }

        if not CbSdkConnection().is_connected:
            CbSdkConnection().connect()

        return CbSdkConnection().set_recording_state(on_off, file_info)

    def manage_depth_process(self, on_off):
        # start process
        if on_off and not self.depth_process_running:
            self.depth_wrapper.start_worker()
            self.depth_process_running = True
        else:
            self.depth_wrapper.kill_worker()
            self.depth_process_running = False

    def manage_feature_process(self, on_off):
        if on_off and not self.features_process_running:
            self.features_wrapper.start_worker()
            self.features_process_running = True
        else:
            self.features_wrapper.kill_worker()
            self.features_process_running = False

    def manage_refresh(self):
        self.plot_stack.widget(self.stack_dict[self.chan_select.currentText()][
            self.feature_select.currentText()][0]).clear_plot()
        self.stack_dict[self.chan_select.currentText()][
            self.feature_select.currentText()][1] = 0

    def process_settings(self, sub_sett, proc_sett, depth_sett, feat_sett):
        self.subject_settings = dict(sub_sett)
        self.procedure_settings = dict(proc_sett)
        self.depth_settings = dict(depth_sett)
        self.features_settings = dict(feat_sett)

        # validate that we have some data in the electrode_settings. If the NSP is not connected we will have
        # to load the channel names from the DB. Also we want to keep the FeaturesGUI unaware of the DB channels.
        if len(self.depth_settings['electrode_settings']) == 0:
            self.depth_settings['electrode_settings'] = {}
            for lbl in DBWrapper().list_channel_labels():
                self.depth_settings['electrode_settings'][lbl] = DEPTHSETTINGS
            CbSdkConnection().is_simulating = True

        # set new features
        self.feature_select.setCurrentIndex(0)  # Raw
        while self.feature_select.count() > 2:  # Raw and Mapping
            self.feature_select.removeItem(2)
        self.feature_select.addItems(self.features_settings['features'].keys())

        # set new channels
        self.chan_select.setCurrentIndex(0)  # None
        while self.chan_select.count() > 1:
            self.chan_select.removeItem(1)
        self.chan_select.addItems(
            self.depth_settings['electrode_settings'].keys())

        # clear and update stacked widget
        to_delete = [
            self.plot_stack.widget(x) for x in range(self.plot_stack.count())
        ]
        for wid in to_delete:
            self.plot_stack.removeWidget(wid)
            wid.deleteLater()
            wid = None
        self.stack_dict = {}
        self.create_plots()

        self.depth_wrapper.send_settings(self.depth_settings)
        self.features_wrapper.send_settings(self.features_settings)

        if not self.features_process_running:
            self.manage_feature_process(True)

        # self.clear()
        self.read_from_shared_memory()

    def create_plots(self, theme='dark', **kwargs):
        # Collect PlotWidget configuration
        self.plot_config['theme'] = theme
        self.plot_config['color_iterator'] = -1
        self.plot_config['x_range'] = XRANGE_FEATURES
        self.plot_config['y_range'] = uVRANGE
        self.plot_config['do_hp'] = True

        labels = []
        for ii in range(0, self.chan_select.count()):
            labels.append(self.chan_select.itemText(ii))
        # labels.extend(self.channel_labels)

        features = []
        for ii in range(0, self.feature_select.count()):
            features.append(self.feature_select.itemText(ii))

        stack_idx = 0
        for lbl_idx, lbl in enumerate(labels):
            self.stack_dict[lbl] = {}
            self.plot_config['color_iterator'] = lbl_idx - 1
            self.plot_config['title'] = lbl
            for feat in features:
                self.stack_dict[lbl][feat] = [stack_idx, 0]
                # TODO: not hard-coding??
                if feat == 'Raw':
                    self.plot_stack.addWidget(RawPlots(dict(self.plot_config)))
                elif feat == 'Mapping':
                    self.plot_stack.addWidget(
                        MappingPlots(dict(self.plot_config)))
                elif feat == 'STN':
                    self.plot_stack.addWidget(STNPlots(dict(self.plot_config)))
                elif feat == 'LFP':
                    self.plot_stack.addWidget(LFPPlots(dict(self.plot_config)))
                elif feat == 'Spikes':
                    self.plot_stack.addWidget(
                        SpikePlots(dict(self.plot_config)))
                else:
                    self.plot_stack.addWidget(
                        NullPlotWidget(dict(self.plot_config)))
                stack_idx += 1

        self.plot_stack.setCurrentIndex(0)

    def refresh_axes(self):
        pass

    def clear(self):
        # set the current datum of all stacks to 0
        for lbl in self.stack_dict:
            for feat in self.stack_dict[lbl]:
                self.stack_dict[lbl][feat][1] = 0
                self.plot_stack.widget(
                    self.stack_dict[lbl][feat][0]).clear_plot()

    def update(self):
        # Depth process
        output = self.depth_wrapper.worker_status()
        self.status_label.setPixmap(self.status_icons[output])

        if self.depth_wrapper.is_running():
            self.depth_process_btn.setStyleSheet("QPushButton { color: white; "
                                                 "background-color : green; "
                                                 "border-color : green; "
                                                 "border-width: 2px}")
        else:
            self.depth_process_running = False
            self.depth_process_btn.setStyleSheet("QPushButton { color: white; "
                                                 "background-color : red; "
                                                 "border-color : red; "
                                                 "border-width: 2px}")

        if self.features_wrapper.is_running():
            self.features_process_btn.setStyleSheet(
                "QPushButton { color: white; "
                "background-color : green; "
                "border-color : green; "
                "border-width: 2px}")
        else:
            self.features_process_running = False
            self.features_process_btn.setStyleSheet(
                "QPushButton { color: white; "
                "background-color : red; "
                "border-color : red; "
                "border-width: 2px}")

        if self.sweep_control.isChecked():
            self.read_from_shared_memory()

        # features plot
        curr_chan_lbl = self.chan_select.currentText()
        if curr_chan_lbl != 'None':
            curr_feat = self.feature_select.currentText()
            do_hp = self.do_hp.isChecked()

            if do_hp != self.plot_stack.currentWidget().plot_config['do_hp'] or \
                    self.y_range != self.plot_stack.currentWidget().plot_config['y_range']:
                self.plot_stack.currentWidget().clear_plot()
                self.stack_dict[curr_chan_lbl][curr_feat][1] = 0
                self.plot_stack.currentWidget().plot_config['do_hp'] = do_hp
                self.plot_stack.currentWidget(
                ).plot_config['y_range'] = self.y_range

            curr_datum = self.stack_dict[curr_chan_lbl][curr_feat][1]
            if curr_feat == 'Raw':
                all_data = DBWrapper().load_depth_data(chan_lbl=curr_chan_lbl,
                                                       gt=curr_datum,
                                                       do_hp=do_hp,
                                                       return_uV=True)
            elif curr_feat == 'Mapping':
                all_data = DBWrapper().load_mapping_response(
                    chan_lbl=curr_chan_lbl, gt=curr_datum)
            else:
                all_data = DBWrapper().load_features_data(
                    category=curr_feat, chan_lbl=curr_chan_lbl, gt=curr_datum)
            if all_data:
                self.plot_stack.currentWidget().update_plot(dict(all_data))
                self.stack_dict[curr_chan_lbl][curr_feat][1] = max(
                    all_data.keys())

    def kill_processes(self):
        self.manage_depth_process(False)
        self.manage_feature_process(False)

    def read_from_shared_memory(self):
        if self.monitored_channel_mem.isAttached():
            self.monitored_channel_mem.lock()
            settings = np.frombuffer(self.monitored_channel_mem.data(),
                                     dtype=np.float)[-3:]
            self.chan_select.setCurrentIndex(int(settings[0]))
            self.range_edit.setText(str(settings[1]))
            self.manage_range_edit()
            self.do_hp.setChecked(bool(settings[2]))
            self.monitored_channel_mem.unlock()
        else:
            self.monitored_channel_mem.attach()
            # self.sweep_control.setChecked(False)
            self.manage_sweep_control()
Exemplo n.º 18
0
class VariableExplorer(SpyderPluginWidget):
    """Variable Explorer plugin."""

    CONF_SECTION = 'variable_explorer'
    CONFIGWIDGET_CLASS = VariableExplorerConfigPage
    DISABLE_ACTIONS_WHEN_HIDDEN = False
    INITIAL_FREE_MEMORY_TIME_TRIGGER = 60 * 1000  # ms
    SECONDARY_FREE_MEMORY_TIME_TRIGGER = 180 * 1000  # ms

    def __init__(self, parent):
        SpyderPluginWidget.__init__(self, parent)

        # Widgets
        self.stack = QStackedWidget(self)
        self.stack.setStyleSheet("QStackedWidget{padding: 0px; border: 0px}")
        self.shellwidgets = {}

        # Layout
        layout = QVBoxLayout()
        layout.addWidget(self.stack)
        self.setLayout(layout)

    def get_settings(self):
        """
        Retrieve all Variable Explorer configuration settings.
        
        Specifically, return the settings in CONF_SECTION with keys in 
        REMOTE_SETTINGS, and the setting 'dataframe_format'.
        
        Returns:
            dict: settings
        """
        settings = {}
        for name in REMOTE_SETTINGS:
            settings[name] = self.get_option(name)

        # dataframe_format is stored without percent sign in config
        # to avoid interference with ConfigParser's interpolation
        name = 'dataframe_format'
        settings[name] = '%{0}'.format(self.get_option(name))
        return settings

    @Slot(str, object)
    def change_option(self, option_name, new_value):
        """
        Change a config option.

        This function is called if sig_option_changed is received. If the
        option changed is the dataframe format, then the leading '%' character
        is stripped (because it can't be stored in the user config). Then,
        the signal is emitted again, so that the new value is saved in the
        user config.
        """
        if option_name == 'dataframe_format':
            assert new_value.startswith('%')
            new_value = new_value[1:]
        self.sig_option_changed.emit(option_name, new_value)

    @Slot()
    def free_memory(self):
        """Free memory signal."""
        self.main.free_memory()
        QTimer.singleShot(self.INITIAL_FREE_MEMORY_TIME_TRIGGER,
                          lambda: self.main.free_memory())
        QTimer.singleShot(self.SECONDARY_FREE_MEMORY_TIME_TRIGGER,
                          lambda: self.main.free_memory())

    # ----- Stack accesors ----------------------------------------------------
    def set_current_widget(self, nsb):
        self.stack.setCurrentWidget(nsb)
        # We update the actions of the options button (cog menu) and we move
        # it to the layout of the current widget.
        self._refresh_actions()
        nsb.setup_options_button()

    def current_widget(self):
        return self.stack.currentWidget()

    def count(self):
        return self.stack.count()

    def remove_widget(self, nsb):
        self.stack.removeWidget(nsb)

    def add_widget(self, nsb):
        self.stack.addWidget(nsb)

    # ----- Public API --------------------------------------------------------
    def add_shellwidget(self, shellwidget):
        """
        Register shell with variable explorer.

        This function opens a new NamespaceBrowser for browsing the variables
        in the shell.
        """
        shellwidget_id = id(shellwidget)
        if shellwidget_id not in self.shellwidgets:
            self.options_button.setVisible(True)
            nsb = NamespaceBrowser(self, options_button=self.options_button)
            nsb.set_shellwidget(shellwidget)
            nsb.setup(**self.get_settings())
            nsb.sig_option_changed.connect(self.change_option)
            nsb.sig_free_memory.connect(self.free_memory)
            self.add_widget(nsb)
            self.shellwidgets[shellwidget_id] = nsb
            self.set_shellwidget_from_id(shellwidget_id)
            return nsb

    def remove_shellwidget(self, shellwidget_id):
        # If shellwidget_id is not in self.shellwidgets, it simply means
        # that shell was not a Python-based console (it was a terminal)
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets.pop(shellwidget_id)
            self.remove_widget(nsb)
            nsb.close()

    def set_shellwidget_from_id(self, shellwidget_id):
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets[shellwidget_id]
            self.set_current_widget(nsb)

    def import_data(self, fname):
        """Import data in current namespace"""
        if self.count():
            nsb = self.current_widget()
            nsb.refresh_table()
            nsb.import_data(filenames=fname)
            if self.dockwidget:
                self.switch_to_plugin()

    #------ SpyderPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _('Variable explorer')

    def get_plugin_icon(self):
        """Return plugin icon"""
        return ima.icon('dictedit')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.current_widget()

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return self.current_widget().actions if self.current_widget() else []

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        for nsb in list(self.shellwidgets.values()):
            nsb.setup(**self.get_settings())
Exemplo n.º 19
0
class ConfigDialog(QDialog):
    """Spyder configuration ('Preferences') dialog box"""
    
    # Signals
    check_settings = Signal()
    size_change = Signal(QSize)
    
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        self.main = parent

        # Widgets
        self.pages_widget = QStackedWidget()
        self.pages_widget.setMinimumWidth(600)
        self.contents_widget = QListWidget()
        self.button_reset = QPushButton(_('Reset to defaults'))

        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply |
                                QDialogButtonBox.Cancel)
        self.apply_btn = bbox.button(QDialogButtonBox.Apply)

        # Widgets setup
        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setWindowTitle(_('Preferences'))
        self.setWindowIcon(ima.icon('configure'))
        self.contents_widget.setMovement(QListView.Static)
        self.contents_widget.setSpacing(1)
        self.contents_widget.setCurrentRow(0)
        self.contents_widget.setMinimumWidth(220)
        self.contents_widget.setMinimumHeight(400)

        # Layout
        hsplitter = QSplitter()
        hsplitter.addWidget(self.contents_widget)
        hsplitter.addWidget(self.pages_widget)
        hsplitter.setStretchFactor(0, 1)
        hsplitter.setStretchFactor(1, 2)

        btnlayout = QHBoxLayout()
        btnlayout.addWidget(self.button_reset)
        btnlayout.addStretch(1)
        btnlayout.addWidget(bbox)

        vlayout = QVBoxLayout()
        vlayout.addWidget(hsplitter)
        vlayout.addLayout(btnlayout)

        self.setLayout(vlayout)

        # Signals and slots
        if self.main:
            self.button_reset.clicked.connect(self.main.reset_spyder)
        self.pages_widget.currentChanged.connect(self.current_page_changed)
        self.contents_widget.currentRowChanged.connect(
                                             self.pages_widget.setCurrentIndex)
        bbox.accepted.connect(self.accept)
        bbox.rejected.connect(self.reject)
        bbox.clicked.connect(self.button_clicked)

        # Ensures that the config is present on spyder first run
        CONF.set('main', 'interface_language', load_lang_conf())

    def get_current_index(self):
        """Return current page index"""
        return self.contents_widget.currentRow()
        
    def set_current_index(self, index):
        """Set current page index"""
        self.contents_widget.setCurrentRow(index)
        
    def get_page(self, index=None):
        """Return page widget"""
        if index is None:
            widget = self.pages_widget.currentWidget()
        else:
            widget = self.pages_widget.widget(index)
        return widget.widget()
    
    @Slot()
    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.pages_widget.count()):
            configpage = self.get_page(index)
            if not configpage.is_valid():
                return
            configpage.apply_changes()
        QDialog.accept(self)
        
    def button_clicked(self, button):
        if button is self.apply_btn:
            # Apply button was clicked
            configpage = self.get_page()
            if not configpage.is_valid():
                return
            configpage.apply_changes()
            
    def current_page_changed(self, index):
        widget = self.get_page(index)
        self.apply_btn.setVisible(widget.apply_callback is not None)
        self.apply_btn.setEnabled(widget.is_modified)
        
    def add_page(self, widget):
        self.check_settings.connect(widget.check_settings)
        widget.show_this_page.connect(lambda row=self.contents_widget.count():
                                      self.contents_widget.setCurrentRow(row))
        widget.apply_button_enabled.connect(self.apply_btn.setEnabled)
        scrollarea = QScrollArea(self)
        scrollarea.setWidgetResizable(True)
        scrollarea.setWidget(widget)
        self.pages_widget.addWidget(scrollarea)
        item = QListWidgetItem(self.contents_widget)
        try:
            item.setIcon(widget.get_icon())
        except TypeError:
            pass
        item.setText(widget.get_name())
        item.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
        item.setSizeHint(QSize(0, 25))
        
    def check_all_settings(self):
        """This method is called to check all configuration page settings
        after configuration dialog has been shown"""
        self.check_settings.emit()
    
    def resizeEvent(self, event):
        """
        Reimplement Qt method to be able to save the widget's size from the
        main application
        """
        QDialog.resizeEvent(self, event)
        self.size_change.emit(self.size())
Exemplo n.º 20
0
class PackagesDialog(DialogBase):
    """Package dependencies dialog."""

    sig_setup_ready = Signal()

    def __init__(
        self,
        parent=None,
        packages=None,
        pip_packages=None,
        remove_only=False,
        update_only=False,
    ):
        """About dialog."""
        super(PackagesDialog, self).__init__(parent=parent)

        # Variables
        self.api = AnacondaAPI()
        self.actions = None
        self.packages = packages or []
        self.pip_packages = pip_packages or []

        # Widgets
        self.stack = QStackedWidget()
        self.table = QTableWidget()
        self.text = QTextEdit()
        self.label_description = LabelBase()
        self.label_status = LabelBase()
        self.progress_bar = QProgressBar()
        self.button_ok = ButtonPrimary('Apply')
        self.button_cancel = ButtonNormal('Cancel')

        # Widget setup
        self.text.setReadOnly(True)
        self.stack.addWidget(self.table)
        self.stack.addWidget(self.text)
        if remove_only:
            text = 'The following packages will be removed:<br>'
        else:
            text = 'The following packages will be modified:<br>'
        self.label_description.setText(text)
        self.label_description.setWordWrap(True)
        self.label_description.setWordWrap(True)
        self.label_status.setWordWrap(True)
        self.table.horizontalScrollBar().setVisible(False)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.setAlternatingRowColors(True)
        self.table.setSelectionMode(QAbstractItemView.NoSelection)
        self.table.setSortingEnabled(True)
        self._hheader = self.table.horizontalHeader()
        self._vheader = self.table.verticalHeader()
        self._hheader.setStretchLastSection(True)
        self._hheader.setDefaultAlignment(Qt.AlignLeft)
        self._hheader.setSectionResizeMode(self._hheader.Fixed)
        self._vheader.setSectionResizeMode(self._vheader.Fixed)
        self.button_ok.setMinimumWidth(70)
        self.button_ok.setDefault(True)
        self.base_minimum_width = 300 if remove_only else 420
        if remove_only:
            self.setWindowTitle("Remove Packages")
        elif update_only:
            self.setWindowTitle("Update Packages")
        else:
            self.setWindowTitle("Install Packages")

        self.setMinimumWidth(self.base_minimum_width)

        # Layouts
        layout_progress = QHBoxLayout()
        layout_progress.addWidget(self.label_status)
        layout_progress.addWidget(SpacerHorizontal())
        layout_progress.addWidget(self.progress_bar)

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_cancel)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_ok)

        layout = QVBoxLayout()
        layout.addWidget(self.label_description)
        layout.addWidget(SpacerVertical())
        layout.addWidget(self.stack)
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_progress)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)
        self.setLayout(layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.button_ok.setDisabled(True)

        # Setup
        self.table.setDisabled(True)
        self.update_status('Solving package specifications',
                           value=0,
                           max_value=0)

    def setup(self, worker, output, error):
        """Setup the widget to include the list of dependencies."""
        if not isinstance(output, dict):
            output = {}

        packages = sorted(pkg.split('==')[0] for pkg in self.packages)
        success = output.get('success')
        error = output.get('error', '')
        exception_name = output.get('exception_name', '')
        actions = output.get('actions', [])
        prefix = worker.prefix

        if exception_name:
            message = exception_name
        else:
            # All requested packages already installed
            message = output.get('message', ' ')

        navi_deps_error = self.api.check_navigator_dependencies(
            actions, prefix)
        description = self.label_description.text()

        if error:
            description = 'No packages will be modified.'
            self.stack.setCurrentIndex(1)
            self.button_ok.setDisabled(True)
            if self.api.is_offline():
                error = ("Some of the functionality of Anaconda Navigator "
                         "will be limited in <b>offline mode</b>. <br><br>"
                         "Installation and upgrade actions will be subject to "
                         "the packages currently available on your package "
                         "cache.")
            self.text.setText(error)
        elif navi_deps_error:
            description = 'No packages will be modified.'
            error = ('Downgrading/removing these packages will modify '
                     'Anaconda Navigator dependencies.')
            self.text.setText(error)
            self.stack.setCurrentIndex(1)
            message = 'NavigatorDependenciesError'
            self.button_ok.setDisabled(True)
        elif success and actions:
            self.stack.setCurrentIndex(0)
            # Conda 4.3.x
            if isinstance(actions, list):
                actions_link = actions[0].get('LINK', [])
                actions_unlink = actions[0].get('UNLINK', [])
            # Conda 4.4.x
            else:
                actions_link = actions.get('LINK', [])
                actions_unlink = actions.get('UNLINK', [])

            deps = set()
            deps = deps.union({p['name'] for p in actions_link})
            deps = deps.union({p['name'] for p in actions_unlink})
            deps = deps - set(packages)
            deps = sorted(list(deps))

            count_total_packages = len(packages) + len(deps)
            plural_total = 's' if count_total_packages != 1 else ''
            plural_selected = 's' if len(packages) != 1 else ''

            self.table.setRowCount(count_total_packages)
            self.table.setColumnCount(4)
            if actions_link:
                description = '{0} package{1} will be installed'.format(
                    count_total_packages, plural_total)
                self.table.showColumn(2)
                self.table.showColumn(3)
            elif actions_unlink and not actions_link:
                self.table.hideColumn(2)
                self.table.hideColumn(3)
                self.table.setHorizontalHeaderLabels(
                    ['Name', 'Unlink', 'Link', 'Channel'])
                description = '{0} package{1} will be removed'.format(
                    count_total_packages, plural_total)

            for row, pkg in enumerate(packages + deps):
                link_item = [p for p in actions_link if p['name'] == pkg]
                if not link_item:
                    link_item = {
                        'version': '-'.center(len('link')),
                        'channel': '-'.center(len('channel')),
                    }
                else:
                    link_item = link_item[0]

                unlink_item = [p for p in actions_unlink if p['name'] == pkg]
                if not unlink_item:
                    unlink_item = {
                        'version': '-'.center(len('link')),
                    }
                else:
                    unlink_item = unlink_item[0]

                unlink_version = str(unlink_item['version'])
                link_version = str(link_item['version'])

                item_unlink_v = QTableWidgetItem(unlink_version)
                item_link_v = QTableWidgetItem(link_version)
                item_link_c = QTableWidgetItem(link_item['channel'])
                if pkg in packages:
                    item_name = QTableWidgetItem(pkg)
                else:
                    item_name = QTableWidgetItem('*' + pkg)

                items = [item_name, item_unlink_v, item_link_v, item_link_c]
                for column, item in enumerate(items):
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    self.table.setItem(row, column, item)

            if deps:
                message = (
                    '<b>*</b> indicates the package is a dependency of a '
                    'selected package{0}<br>').format(plural_selected)

            self.button_ok.setEnabled(True)
            self.table.resizeColumnsToContents()
            unlink_width = self.table.columnWidth(1)
            if unlink_width < 60:
                self.table.setColumnWidth(1, 60)
            self.table.setHorizontalHeaderLabels(
                ['Name  ', 'Unlink  ', 'Link  ', 'Channel  '])

        self.table.setEnabled(True)
        self.update_status(message=message)
        self.label_description.setText(description)

        # Adjust size after data has populated the table
        self.table.resizeColumnsToContents()
        width = sum(
            self.table.columnWidth(i) for i in range(self.table.columnCount()))
        delta = (self.width() - self.table.width() +
                 self.table.verticalHeader().width() + 10)

        new_width = width + delta

        if new_width < self.base_minimum_width:
            new_width = self.base_minimum_width

        self.setMinimumWidth(new_width)
        self.setMaximumWidth(new_width)

        self.sig_setup_ready.emit()

    def update_status(self, message='', value=None, max_value=None):
        """Update status of packages dialog."""
        self.label_status.setText(message)

        if max_value is None and value is None:
            self.progress_bar.setVisible(False)
        else:
            self.progress_bar.setVisible(True)
            self.progress_bar.setMaximum(max_value)
            self.progress_bar.setValue(value)
Exemplo n.º 21
0
class VariableExplorer(QWidget, SpyderPluginMixin):
    """
    Variable Explorer Plugin
    """
    CONF_SECTION = 'variable_explorer'
    CONFIGWIDGET_CLASS = VariableExplorerConfigPage
    sig_option_changed = Signal(str, object)

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        SpyderPluginMixin.__init__(self, parent)

        # Widgets
        self.stack = QStackedWidget(self)
        self.shellwidgets = {}

        # Layout
        layout = QVBoxLayout()
        layout.addWidget(self.stack)
        self.setLayout(layout)

        # Initialize plugin
        self.initialize_plugin()

    def get_settings(self):
        """
        Retrieve all Variable Explorer configuration settings.
        
        Specifically, return the settings in CONF_SECTION with keys in 
        REMOTE_SETTINGS, and the setting 'dataframe_format'.
        
        Returns:
            dict: settings
        """
        settings = {}
        for name in REMOTE_SETTINGS:
            settings[name] = self.get_option(name)

        # dataframe_format is stored without percent sign in config
        # to avoid interference with ConfigParser's interpolation
        name = 'dataframe_format'
        settings[name] = '%{0}'.format(self.get_option(name))
        return settings

    @Slot(str, object)
    def change_option(self, option_name, new_value):
        """
        Change a config option.

        This function is called if sig_option_changed is received. If the
        option changed is the dataframe format, then the leading '%' character
        is stripped (because it can't be stored in the user config). Then,
        the signal is emitted again, so that the new value is saved in the
        user config.
        """
        if option_name == 'dataframe_format':
            assert new_value.startswith('%')
            new_value = new_value[1:]
        self.sig_option_changed.emit(option_name, new_value)

    # ----- Stack accesors ----------------------------------------------------
    def set_current_widget(self, nsb):
        self.stack.setCurrentWidget(nsb)

    def current_widget(self):
        return self.stack.currentWidget()

    def count(self):
        return self.stack.count()

    def remove_widget(self, nsb):
        self.stack.removeWidget(nsb)

    def add_widget(self, nsb):
        self.stack.addWidget(nsb)

    # ----- Public API --------------------------------------------------------
    def add_shellwidget(self, shellwidget):
        """
        Register shell with variable explorer.

        This function opens a new NamespaceBrowser for browsing the variables
        in the shell.
        """
        shellwidget_id = id(shellwidget)
        if shellwidget_id not in self.shellwidgets:
            nsb = NamespaceBrowser(self)
            nsb.set_shellwidget(shellwidget)
            nsb.setup(**self.get_settings())
            nsb.sig_option_changed.connect(self.change_option)
            self.add_widget(nsb)
            self.shellwidgets[shellwidget_id] = nsb
            self.set_shellwidget_from_id(shellwidget_id)
            return nsb

    def remove_shellwidget(self, shellwidget_id):
        # If shellwidget_id is not in self.shellwidgets, it simply means
        # that shell was not a Python-based console (it was a terminal)
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets.pop(shellwidget_id)
            self.remove_widget(nsb)
            nsb.close()

    def set_shellwidget_from_id(self, shellwidget_id):
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets[shellwidget_id]
            self.set_current_widget(nsb)

    def import_data(self, fname):
        """Import data in current namespace"""
        if self.count():
            nsb = self.current_widget()
            nsb.refresh_table()
            nsb.import_data(filenames=fname)
            if self.dockwidget and not self.ismaximized:
                self.dockwidget.setVisible(True)
                self.dockwidget.raise_()

    #------ SpyderPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _('Variable explorer')

    def get_plugin_icon(self):
        """Return plugin icon"""
        return ima.icon('dictedit')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.current_widget()

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True

    def refresh_plugin(self):
        """Refresh widget"""
        pass

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return []

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.main.add_dockwidget(self)

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        for nsb in list(self.shellwidgets.values()):
            nsb.setup(**self.get_settings())
Exemplo n.º 22
0
class MxStackedMixin:
    """Mixin to Plugin classes to stacked child widgets.

    The stacked child widgets are of MX_WIDGET_CLASS.
    Each of the stacked child widgets is connected to each MxShellWidget.

    """
    MX_WIDGET_CLASS = None  # To be defined in sub class

    def __init__(self, parent):

        self.main = parent  # Spyder3

        # Widgets
        self.stack = QStackedWidget(self)
        self.shellwidgets = {}

        # Fallback widget when no MxConsole available.
        # Spyder 4 output internal error message without this.
        self.blankwidget = QLabel(text="No MxConsole Available")
        self.blankwidget.setAlignment(Qt.AlignCenter)
        self.stack.addWidget(self.blankwidget)

        # On active tab in IPython console change
        if spyder.version_info > (5, 2):
            self.main.ipyconsole.get_widget().tabwidget.currentChanged.connect(
                self.on_ipyconsole_current_changed)
        else:
            self.main.ipyconsole.tabwidget.currentChanged.connect(
                self.on_ipyconsole_current_changed)

    # ----- Stack accesors ----------------------------------------------------
    # Modified from https://github.com/spyder-ide/spyder/blob/v3.3.2/spyder/plugins/variableexplorer.py#L140

    def set_current_widget(self, mxwidget):
        self.stack.setCurrentWidget(mxwidget)
        # self.refresh_actions()
        if spyder.version_info < (5,):
            mxwidget.setup_options_button()

    def current_widget(self):
        return self.stack.currentWidget()

    def count(self):
        return self.stack.count()

    def remove_widget(self, mxwidget):
        self.stack.removeWidget(mxwidget)

    def add_widget(self, mxwidget):
        self.stack.addWidget(mxwidget)

    # ----- Public API --------------------------------------------------------
    # Modified from https://github.com/spyder-ide/spyder/blob/v3.3.2/spyder/plugins/variableexplorer.py#L156

    def add_shellwidget(self, shellwidget):
        """
        Register shell with variable explorer.

        This function opens a new NamespaceBrowser for browsing the variables
        in the shell.
        """
        shellwidget_id = id(shellwidget)
        if shellwidget_id not in self.shellwidgets:
            if spyder.version_info < (4,) or spyder.version_info > (5,):
                mxwidget = self.MX_WIDGET_CLASS(self, options_button=None)
            else:
                self.options_button.setVisible(True)
                mxwidget = self.MX_WIDGET_CLASS(
                        self, options_button=self.options_button)
            mxwidget.set_shellwidget(shellwidget)
            # analyzer.sig_option_changed.connect(self.change_option)
            # analyzer.sig_free_memory.connect(self.free_memory)
            self.add_widget(mxwidget)
            self.shellwidgets[shellwidget_id] = mxwidget
            self.set_shellwidget_from_id(shellwidget_id)
            return mxwidget

    def remove_shellwidget(self, shellwidget_id):
        # If shellwidget_id is not in self.shellwidgets, it simply means
        # that shell was not a Python-based console (it was a terminal)
        if shellwidget_id in self.shellwidgets:
            mxwidget = self.shellwidgets.pop(shellwidget_id)
            self.remove_widget(mxwidget)
            mxwidget.close()

    def set_shellwidget_from_id(self, shellwidget_id):
        if shellwidget_id in self.shellwidgets:
            mxwidget = self.shellwidgets[shellwidget_id]
            self.set_current_widget(mxwidget)

    def on_ipyconsole_current_changed(self):
        # Slot like IPythonConsole.reflesh_plugin
        if spyder.version_info > (5, 2):
            client = self.main.ipyconsole.get_widget().tabwidget.currentWidget()
        else:
            client = self.main.ipyconsole.tabwidget.currentWidget()
        if client:
            sw = client.shellwidget
            self.set_shellwidget_from_id(id(sw))
Exemplo n.º 23
0
class PreferencesDialog(QDialog):
    """Preferences Dialog for Napari user settings."""

    resized = Signal(QSize)

    def __init__(self, parent=None):
        super().__init__(parent)

        self._list = QListWidget(self)
        self._stack = QStackedWidget(self)

        self._list.setObjectName("Preferences")

        # Set up buttons
        self._button_cancel = QPushButton(trans._("Cancel"))
        self._button_ok = QPushButton(trans._("OK"))
        self._default_restore = QPushButton(trans._("Restore defaults"))

        # Setup
        self.setWindowTitle(trans._("Preferences"))

        # Layout
        left_layout = QVBoxLayout()
        left_layout.addWidget(self._list)
        left_layout.addStretch()
        left_layout.addWidget(self._default_restore)
        left_layout.addWidget(self._button_cancel)
        left_layout.addWidget(self._button_ok)

        main_layout = QHBoxLayout()
        main_layout.addLayout(left_layout, 1)
        main_layout.addWidget(self._stack, 3)

        self.setLayout(main_layout)

        # Signals

        self._list.currentRowChanged.connect(
            lambda index: self._stack.setCurrentIndex(index))
        self._button_cancel.clicked.connect(self.on_click_cancel)
        self._button_ok.clicked.connect(self.on_click_ok)
        self._default_restore.clicked.connect(self.restore_defaults)

        # Make widget

        self.make_dialog()
        self._list.setCurrentRow(0)

    def resizeEvent(self, event):
        """Override to emit signal."""
        self.resized.emit(event.size())
        super().resizeEvent(event)

    def make_dialog(self):
        """Removes settings not to be exposed to user and creates dialog pages."""
        # Because there are multiple pages, need to keep a list of values sets.
        self._values_orig_set_list = []
        self._values_set_list = []
        for _key, setting in SETTINGS.schemas().items():
            schema = json.loads(setting['json_schema'])
            # Need to remove certain properties that will not be displayed on the GUI
            properties = schema.pop('properties')
            model = setting['model']
            values = model.dict()
            napari_config = getattr(model, "NapariConfig", None)
            if napari_config is not None:
                for val in napari_config.preferences_exclude:
                    properties.pop(val)
                    values.pop(val)

            schema['properties'] = properties
            self._values_orig_set_list.append(set(values.items()))
            self._values_set_list.append(set(values.items()))

            # Only add pages if there are any properties to add.
            if properties:
                self.add_page(schema, values)

    def restore_defaults(self):
        """Launches dialog to confirm restore settings choice."""

        widget = ConfirmDialog(
            parent=self,
            text=trans._("Are you sure you want to restore default settings?"),
        )
        widget.valueChanged.connect(self._reset_widgets)
        widget.exec_()

    def _reset_widgets(self):
        """Deletes the widgets and rebuilds with defaults."""
        self.close()
        self._list.clear()

        for n in range(self._stack.count()):
            widget = self._stack.removeWidget(self._stack.currentWidget())
            del widget

        self.make_dialog()
        self._list.setCurrentRow(0)
        self.show()

    def on_click_ok(self):
        """Keeps the selected preferences saved to SETTINGS."""
        self.close()

    def on_click_cancel(self):
        """Restores the settings in place when dialog was launched."""
        # Need to check differences for each page.
        for n in range(self._stack.count()):
            # Must set the current row so that the proper set list is updated
            # in check differences.
            self._list.setCurrentRow(n)
            self.check_differences(
                self._values_orig_set_list[n],
                self._values_set_list[n],
            )
        self._list.setCurrentRow(0)
        self.close()

    def add_page(self, schema, values):
        """Creates a new page for each section in dialog.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """
        widget = self.build_page_dialog(schema, values)
        self._list.addItem(schema["title"])
        self._stack.addWidget(widget)

    def build_page_dialog(self, schema, values):
        """Builds the preferences widget using the json schema builder.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """

        builder = WidgetBuilder()
        form = builder.create_form(schema, {})
        # set state values for widget
        form.widget.state = values
        form.widget.on_changed.connect(lambda d: self.check_differences(
            set(d.items()),
            self._values_set_list[self._list.currentIndex().row()],
        ))

        return form

    def check_differences(self, new_set, values_set):
        """Changes settings in settings manager with changes from dialog.

        Parameters
        ----------
        new_set : set
            The set of new values, with tuples of key value pairs for each
            setting.
        values_set : set
            The old set of values.
        """

        page = self._list.currentItem().text().split(" ")[0].lower()
        different_values = list(new_set - values_set)

        if len(different_values) > 0:
            # change the values in SETTINGS
            for val in different_values:
                try:
                    setattr(SETTINGS._settings[page], val[0], val[1])
                    self._values_set_list[
                        self._list.currentIndex().row()] = new_set
                except:  # noqa: E722
                    continue
Exemplo n.º 24
0
class Plots(SpyderPluginWidget):
    """Plots plugin."""

    CONF_SECTION = 'plots'
    CONFIGWIDGET_CLASS = PlotsConfigPage
    DISABLE_ACTIONS_WHEN_HIDDEN = False
    sig_option_changed = Signal(str, object)

    def __init__(self, parent):
        SpyderPluginWidget.__init__(self, parent)

        # Widgets
        self.stack = QStackedWidget(self)
        self.shellwidgets = {}

        # Layout
        layout = QGridLayout(self)
        layout.addWidget(self.stack)

        # Initialize plugin
        self.initialize_plugin()

    def get_settings(self):
        """Retrieve all Plots configuration settings."""
        return {name: self.get_option(name) for name in
                ['mute_inline_plotting',  'show_plot_outline']}

    # ---- Stack accesors
    def set_current_widget(self, fig_browser):
        """
        Set the currently visible fig_browser in the stack widget, refresh the
        actions of the cog menu button and move it to the layout of the new
        fig_browser.
        """
        self.stack.setCurrentWidget(fig_browser)
        # We update the actions of the options button (cog menu) and
        # we move it to the layout of the current widget.
        self.refresh_actions()
        fig_browser.setup_options_button()

    def current_widget(self):
        return self.stack.currentWidget()

    def count(self):
        return self.stack.count()

    def remove_widget(self, fig_browser):
        self.stack.removeWidget(fig_browser)

    def add_widget(self, fig_browser):
        self.stack.addWidget(fig_browser)

    # ---- Public API
    def add_shellwidget(self, shellwidget):
        """
        Register shell with figure explorer.

        This function opens a new FigureBrowser for browsing the figures
        in the shell.
        """
        shellwidget_id = id(shellwidget)
        if shellwidget_id not in self.shellwidgets:
            self.options_button.setVisible(True)
            fig_browser = FigureBrowser(
                self, options_button=self.options_button,
                background_color=MAIN_BG_COLOR)
            fig_browser.set_shellwidget(shellwidget)
            fig_browser.setup(**self.get_settings())
            fig_browser.sig_option_changed.connect(
                self.sig_option_changed.emit)
            fig_browser.thumbnails_sb.redirect_stdio.connect(
                self.main.redirect_internalshell_stdio)
            self.add_widget(fig_browser)
            self.shellwidgets[shellwidget_id] = fig_browser
            self.set_shellwidget_from_id(shellwidget_id)
            return fig_browser

    def remove_shellwidget(self, shellwidget_id):
        # If shellwidget_id is not in self.shellwidgets, it simply means
        # that shell was not a Python-based console (it was a terminal)
        if shellwidget_id in self.shellwidgets:
            fig_browser = self.shellwidgets.pop(shellwidget_id)
            self.remove_widget(fig_browser)
            fig_browser.close()

    def set_shellwidget_from_id(self, shellwidget_id):
        if shellwidget_id in self.shellwidgets:
            fig_browser = self.shellwidgets[shellwidget_id]
            self.set_current_widget(fig_browser)

    # ---- SpyderPluginWidget API
    def get_plugin_title(self):
        """Return widget title"""
        return _('Plots')

    def get_plugin_icon(self):
        """Return plugin icon"""
        return ima.icon('hist')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.current_widget()

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True

    def refresh_plugin(self):
        """Refresh widget"""
        pass

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return self.current_widget().actions if self.current_widget() else []

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.main.add_dockwidget(self)

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        for fig_browser in list(self.shellwidgets.values()):
            fig_browser.setup(**self.get_settings())
Exemplo n.º 25
0
class CalibrationWidget(ParameterWidget):

    def __init__(self, parent=None):
        ParameterWidget.__init__(self, _Calibration, parent)

    def _init_ui(self):
        # Widgets
        self._combobox = QComboBox()
        self._stack = QStackedWidget()

        # Layouts
        layout = ParameterWidget._init_ui(self)
        layout.addRow(self._combobox)
        layout.addRow(self._stack)

        # Register classes
        self._widget_indexes = {}

        for entry_point in iter_entry_points('pyhmsa_gui.spec.condition.calibration'):
            widget_class = entry_point.load(require=False)
            widget = widget_class()
            self._combobox.addItem(widget.accessibleName().title())
            self._widget_indexes[widget.CLASS] = self._stack.addWidget(widget)
            widget.edited.connect(self.edited)

        # Signals
        self._combobox.currentIndexChanged.connect(self._on_combo_box)
        self._combobox.currentIndexChanged.connect(self.edited)

        return layout

    def _on_combo_box(self):
        # Old values
        oldwidget = self._stack.currentWidget()
        try:
            quantity = oldwidget._txt_quantity.text()
        except:
            quantity = None
        try:
            unit = oldwidget._txt_unit.text()
        except:
            unit = None

        # Change widget
        current_index = self._combobox.currentIndex()
        self._stack.setCurrentIndex(current_index)

        # Update values
        widget = self._stack.currentWidget()
        widget._txt_quantity.setText(quantity)
        widget._txt_unit.setText(unit)

    def parameter(self):
        return self._stack.currentWidget().calibration()

    def setParameter(self, calibration):
        index = self._widget_indexes[type(calibration)]
        self._combobox.setCurrentIndex(index)
        self._stack.setCurrentIndex(index)
        self._stack.currentWidget().setParameter(calibration)

    def calibration(self):
        return self.parameter()

    def setCalibration(self, calibration):
        self.setParameter(calibration)

    def setReadOnly(self, state):
        ParameterWidget.setReadOnly(self, state)
        self._combobox.setEnabled(not state)
        self._stack.currentWidget().setReadOnly(state)

    def isReadOnly(self):
        return ParameterWidget.isReadOnly(self) and \
            not self._combobox.isEnabled() and \
            self._stack.currentWidget().isReadOnly()

    def hasAcceptableInput(self):
        return ParameterWidget.hasAcceptableInput(self) and \
            self._stack.currentWidget().hasAcceptableInput()
Exemplo n.º 26
0
class ConfigDialog(QDialog):
    def __init__(self):
        super(ConfigDialog, self).__init__()

        # Set size and position
        self.setGeometry(0, 0, 900, 550)
        frameGm = self.frameGeometry()
        screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos())
        centerPoint = QApplication.desktop().screenGeometry(screen).center()
        frameGm.moveCenter(centerPoint)
        self.move(frameGm.topLeft())

        self.contentsWidget = QListView()
        self.contentsWidget.setViewMode(QListView.IconMode)
        # self.contentsWidget.setIconSize(QSize(96, 84))
        self.contentsWidget.setMovement(QListView.Static)
        self.contentsWidget.setMaximumWidth(174)
        self.contentsWidget.setSpacing(12)
        self.contentsWidget.setSelectionMode(QAbstractItemView.SingleSelection)

        self.contentsModel = QStandardItemModel()
        self.contentsWidget.setModel(self.contentsModel)
        self.contentsWidget.selectionModel().currentChanged.connect(self.changePage)

        self.buttonboxWidget = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel | QDialogButtonBox.Apply# | QDialogButtonBox.Help
        )
        self.buttonboxWidget.button(QDialogButtonBox.Ok).clicked.connect(self.ok)
        self.buttonboxWidget.button(QDialogButtonBox.Apply).clicked.connect(self.apply)
        self.buttonboxWidget.button(QDialogButtonBox.Cancel).clicked.connect(self.close)

        self.pagesWidget = QStackedWidget()

        horizontalLayout = QHBoxLayout()
        horizontalLayout.addWidget(self.contentsWidget)
        horizontalLayout.addWidget(self.pagesWidget, 1)

        mainLayout = QVBoxLayout()
        mainLayout.addLayout(horizontalLayout)
        # mainLayout.addStretch(1)
        mainLayout.addSpacing(12)
        mainLayout.addWidget(self.buttonboxWidget)

        self.setLayout(mainLayout)
        self.setWindowTitle("Config Dialog")

        # Set modality
        self.setModal(True)

        self.lastwidget = None

        self.createIcons()
        self.restore()

        pluginmanager.attach(self.pluginsChanged)

    def createIcons(self):
        self.contentsModel.clear()
        for pluginInfo in pluginmanager.getPluginsOfCategory("SettingsPlugin"):
            item = QStandardItem(pluginInfo.plugin_object.icon, pluginInfo.plugin_object.name())
            item.widget = pluginInfo.plugin_object.widget
            item.setTextAlignment(Qt.AlignHCenter)
            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            item.setSizeHint(QSize(136, 80))
            self.contentsModel.appendRow(item)

    def show(self):
        if self.lastwidget:
            self.pagesWidget.addWidget(self.lastwidget)
            self.pagesWidget.setCurrentWidget(self.lastwidget)
        self.restore()
        super(ConfigDialog, self).show()

    def changePage(self, current, previous):
        if not current:
            current = previous
        current = self.contentsModel.itemFromIndex(current)
        self.pagesWidget.addWidget(current.widget)
        self.pagesWidget.setCurrentWidget(current.widget)
        self.lastwidget = current.widget

    def pluginsChanged(self):
        self.createIcons()

    def restore(self):
        for pluginInfo in pluginmanager.getPluginsOfCategory("SettingsPlugin"):
            pluginInfo.plugin_object.restore()

        self.apply()

    def ok(self):
        self._empty()
        self.apply()
        self.accept()

    def apply(self):
        for pluginInfo in pluginmanager.getPluginsOfCategory("SettingsPlugin"):
            pluginInfo.plugin_object.save()

    def close(self):
        self._empty()
        self.restore()
        self.reject()

    def _empty(self):
        """
        Disown all widget children (otherwise their c++ objects are force deleted when the dialog closes).
        Must be run in reverse to avoid index update errors
        """
        for i in reversed(range(self.pagesWidget.count())):
            self.pagesWidget.widget(i).setParent(None)

    def closeEvent(self, event):
        self.close()
        event.accept()

    def keyPressEvent(self, e: QKeyEvent):
        if e.key() != Qt.Key_Escape:
            super(ConfigDialog, self).keyPressEvent(e)
        else:
            self.close()
Exemplo n.º 27
0
class VariableExplorer(SpyderPluginWidget):
    """Variable Explorer plugin."""

    CONF_SECTION = 'variable_explorer'
    CONFIGWIDGET_CLASS = VariableExplorerConfigPage
    DISABLE_ACTIONS_WHEN_HIDDEN = False
    INITIAL_FREE_MEMORY_TIME_TRIGGER = 60 * 1000  # ms
    SECONDARY_FREE_MEMORY_TIME_TRIGGER = 180 * 1000  # ms
    sig_option_changed = Signal(str, object)

    def __init__(self, parent):
        SpyderPluginWidget.__init__(self, parent)

        # Widgets
        self.stack = QStackedWidget(self)
        self.shellwidgets = {}

        # Layout
        layout = QVBoxLayout()
        layout.addWidget(self.stack)
        self.setLayout(layout)


        # Initialize plugin
        self.initialize_plugin()

    def get_settings(self):
        """
        Retrieve all Variable Explorer configuration settings.
        
        Specifically, return the settings in CONF_SECTION with keys in 
        REMOTE_SETTINGS, and the setting 'dataframe_format'.
        
        Returns:
            dict: settings
        """
        settings = {}
        for name in REMOTE_SETTINGS:
            settings[name] = self.get_option(name)

        # dataframe_format is stored without percent sign in config
        # to avoid interference with ConfigParser's interpolation
        name = 'dataframe_format'
        settings[name] = '%{0}'.format(self.get_option(name))
        return settings

    @Slot(str, object)
    def change_option(self, option_name, new_value):
        """
        Change a config option.

        This function is called if sig_option_changed is received. If the
        option changed is the dataframe format, then the leading '%' character
        is stripped (because it can't be stored in the user config). Then,
        the signal is emitted again, so that the new value is saved in the
        user config.
        """
        if option_name == 'dataframe_format':
            assert new_value.startswith('%')
            new_value = new_value[1:]
        self.sig_option_changed.emit(option_name, new_value)

    @Slot()
    def free_memory(self):
        """Free memory signal."""
        self.main.free_memory()
        QTimer.singleShot(self.INITIAL_FREE_MEMORY_TIME_TRIGGER,
                          lambda: self.main.free_memory())
        QTimer.singleShot(self.SECONDARY_FREE_MEMORY_TIME_TRIGGER,
                          lambda: self.main.free_memory())

    # ----- Stack accesors ----------------------------------------------------
    def set_current_widget(self, nsb):
        self.stack.setCurrentWidget(nsb)
        # We update the actions of the options button (cog menu) and we move
        # it to the layout of the current widget.
        self.refresh_actions()
        nsb.setup_options_button()

    def current_widget(self):
        return self.stack.currentWidget()

    def count(self):
        return self.stack.count()

    def remove_widget(self, nsb):
        self.stack.removeWidget(nsb)

    def add_widget(self, nsb):
        self.stack.addWidget(nsb)

    # ----- Public API --------------------------------------------------------
    def add_shellwidget(self, shellwidget):
        """
        Register shell with variable explorer.

        This function opens a new NamespaceBrowser for browsing the variables
        in the shell.
        """
        shellwidget_id = id(shellwidget)
        if shellwidget_id not in self.shellwidgets:
            self.options_button.setVisible(True)
            nsb = NamespaceBrowser(self, options_button=self.options_button)
            nsb.set_shellwidget(shellwidget)
            nsb.setup(**self.get_settings())
            nsb.sig_option_changed.connect(self.change_option)
            nsb.sig_free_memory.connect(self.free_memory)
            self.add_widget(nsb)
            self.shellwidgets[shellwidget_id] = nsb
            self.set_shellwidget_from_id(shellwidget_id)
            return nsb
        
    def remove_shellwidget(self, shellwidget_id):
        # If shellwidget_id is not in self.shellwidgets, it simply means
        # that shell was not a Python-based console (it was a terminal)
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets.pop(shellwidget_id)
            self.remove_widget(nsb)
            nsb.close()
    
    def set_shellwidget_from_id(self, shellwidget_id):
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets[shellwidget_id]
            self.set_current_widget(nsb)

    def import_data(self, fname):
        """Import data in current namespace"""
        if self.count():
            nsb = self.current_widget()
            nsb.refresh_table()
            nsb.import_data(filenames=fname)
            if self.dockwidget and not self.ismaximized:
                self.dockwidget.setVisible(True)
                self.dockwidget.raise_()

    #------ SpyderPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _('Variable explorer')

    def get_plugin_icon(self):
        """Return plugin icon"""
        return ima.icon('dictedit')
    
    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.current_widget()
        
    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True
        
    def refresh_plugin(self):
        """Refresh widget"""
        pass

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return self.current_widget().actions if self.current_widget() else []

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.main.add_dockwidget(self)

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        for nsb in list(self.shellwidgets.values()):
            nsb.setup(**self.get_settings())
Exemplo n.º 28
0
class SimulationPanel(QWidget):
    def __init__(self, config_file):
        QWidget.__init__(self)
        self._config_file = config_file
        self.setObjectName("Simulation_panel")
        layout = QVBoxLayout()

        self._simulation_mode_combo = QComboBox()
        self._simulation_mode_combo.setObjectName("Simulation_mode")
        addHelpToWidget(self._simulation_mode_combo, "run/simulation_mode")

        self._simulation_mode_combo.currentIndexChanged.connect(self.toggleSimulationMode)

        simulation_mode_layout = QHBoxLayout()
        simulation_mode_layout.addSpacing(10)
        simulation_mode_layout.addWidget(QLabel("Simulation mode:"), 0, Qt.AlignVCenter)
        simulation_mode_layout.addWidget(self._simulation_mode_combo, 0, Qt.AlignVCenter)

        simulation_mode_layout.addSpacing(20)

        self.run_button = QToolButton()
        self.run_button.setObjectName('start_simulation')
        self.run_button.setIconSize(QSize(32, 32))
        self.run_button.setText("Start Simulation")
        self.run_button.setIcon(resourceIcon("ide/gear_in_play"))
        self.run_button.clicked.connect(self.runSimulation)
        self.run_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        addHelpToWidget(self.run_button, "run/start_simulation")

        simulation_mode_layout.addWidget(self.run_button)
        simulation_mode_layout.addStretch(1)

        layout.addSpacing(5)
        layout.addLayout(simulation_mode_layout)
        layout.addSpacing(10)

        self._simulation_stack = QStackedWidget()
        self._simulation_stack.setLineWidth(1)
        self._simulation_stack.setFrameStyle(QFrame.StyledPanel)

        layout.addWidget(self._simulation_stack)

        self._simulation_widgets = OrderedDict()
        """ :type: OrderedDict[BaseRunModel,SimulationConfigPanel]"""

        self.addSimulationConfigPanel(SingleTestRunPanel())
        self.addSimulationConfigPanel(EnsembleExperimentPanel())
        if(ERT.ert.have_observations()):
            self.addSimulationConfigPanel(EnsembleSmootherPanel())
            self.addSimulationConfigPanel(MultipleDataAssimilationPanel())
            self.addSimulationConfigPanel(IteratedEnsembleSmootherPanel())

        self.setLayout(layout)

    def addSimulationConfigPanel(self, panel):

        assert isinstance(panel, SimulationConfigPanel)
        self._simulation_stack.addWidget(panel)
        simulation_model = panel.getSimulationModel()
        self._simulation_widgets[simulation_model] = panel
        self._simulation_mode_combo.addItem(simulation_model.name(),simulation_model)
        panel.simulationConfigurationChanged.connect(self.validationStatusChanged)

    def getActions(self):
        return []

    def getCurrentSimulationModel(self):
        return self._simulation_mode_combo.itemData(self._simulation_mode_combo.currentIndex(), Qt.UserRole)

    def getSimulationArguments(self):
        """ @rtype: dict[str,object]"""
        simulation_widget = self._simulation_widgets[self.getCurrentSimulationModel()]
        return simulation_widget.getSimulationArguments()

    def runSimulation(self):
        case_name = getCurrentCaseName()
        message = "Are you sure you want to use case '%s' for initialization of the initial ensemble when running the simulations?" % case_name
        start_simulations = QMessageBox.question(self, "Start simulations?", message, QMessageBox.Yes | QMessageBox.No )

        if start_simulations == QMessageBox.Yes:
            run_model = self.getCurrentSimulationModel()
            arguments = self.getSimulationArguments()
            dialog = RunDialog(self._config_file, run_model(), arguments)
            dialog.startSimulation()
            dialog.exec_()

            ERT.emitErtChange() # simulations may have added new cases.

    def toggleSimulationMode(self):
        current_model = self.getCurrentSimulationModel()
        if current_model is not None:
            widget = self._simulation_widgets[self.getCurrentSimulationModel()]
            self._simulation_stack.setCurrentWidget(widget)
            self.validationStatusChanged()

    def validationStatusChanged(self):
        widget = self._simulation_widgets[self.getCurrentSimulationModel()]
        self.run_button.setEnabled(widget.isConfigurationValid())
Exemplo n.º 29
0
class ArrayEditor(QDialog):
    """Array Editor Dialog"""
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)

        self.data = None
        self.arraywidget = None
        self.stack = None
        self.layout = None
        self.btn_save_and_close = None
        self.btn_close = None
        # Values for 3d array editor
        self.dim_indexes = [{}, {}, {}]
        self.last_dim = 0  # Adjust this for changing the startup dimension

    def setup_and_check(self,
                        data,
                        title='',
                        readonly=False,
                        xlabels=None,
                        ylabels=None):
        """
        Setup ArrayEditor:
        return False if data is not supported, True otherwise
        """
        self.data = data
        self.data.flags.writeable = True
        is_record_array = data.dtype.names is not None
        is_masked_array = isinstance(data, np.ma.MaskedArray)

        if data.ndim > 3:
            self.error(
                _("Arrays with more than 3 dimensions are not "
                  "supported"))
            return False
        if xlabels is not None and len(xlabels) != self.data.shape[1]:
            self.error(
                _("The 'xlabels' argument length do no match array "
                  "column number"))
            return False
        if ylabels is not None and len(ylabels) != self.data.shape[0]:
            self.error(
                _("The 'ylabels' argument length do no match array row "
                  "number"))
            return False
        if not is_record_array:
            dtn = data.dtype.name
            if dtn not in SUPPORTED_FORMATS and not dtn.startswith('str') \
               and not dtn.startswith('unicode'):
                arr = _("%s arrays") % data.dtype.name
                self.error(_("%s are currently not supported") % arr)
                return False

        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.setWindowIcon(ima.icon('arredit'))
        if title:
            title = to_text_string(title) + " - " + _("NumPy array")
        else:
            title = _("Array editor")
        if readonly:
            title += ' (' + _('read only') + ')'
        self.setWindowTitle(title)
        self.resize(600, 500)

        # Stack widget
        self.stack = QStackedWidget(self)
        if is_record_array:
            for name in data.dtype.names:
                self.stack.addWidget(
                    ArrayEditorWidget(self, data[name], readonly, xlabels,
                                      ylabels))
        elif is_masked_array:
            self.stack.addWidget(
                ArrayEditorWidget(self, data, readonly, xlabels, ylabels))
            self.stack.addWidget(
                ArrayEditorWidget(self, data.data, readonly, xlabels, ylabels))
            self.stack.addWidget(
                ArrayEditorWidget(self, data.mask, readonly, xlabels, ylabels))
        elif data.ndim == 3:
            pass
        else:
            self.stack.addWidget(
                ArrayEditorWidget(self, data, readonly, xlabels, ylabels))
        self.arraywidget = self.stack.currentWidget()
        if self.arraywidget:
            self.arraywidget.model.dataChanged.connect(
                self.save_and_close_enable)
        self.stack.currentChanged.connect(self.current_widget_changed)
        self.layout.addWidget(self.stack, 1, 0)

        # Buttons configuration
        btn_layout = QHBoxLayout()
        if is_record_array or is_masked_array or data.ndim == 3:
            if is_record_array:
                btn_layout.addWidget(QLabel(_("Record array fields:")))
                names = []
                for name in data.dtype.names:
                    field = data.dtype.fields[name]
                    text = name
                    if len(field) >= 3:
                        title = field[2]
                        if not is_text_string(title):
                            title = repr(title)
                        text += ' - ' + title
                    names.append(text)
            else:
                names = [_('Masked data'), _('Data'), _('Mask')]
            if data.ndim == 3:
                # QSpinBox
                self.index_spin = QSpinBox(self, keyboardTracking=False)
                self.index_spin.valueChanged.connect(self.change_active_widget)
                # QComboBox
                names = [str(i) for i in range(3)]
                ra_combo = QComboBox(self)
                ra_combo.addItems(names)
                ra_combo.currentIndexChanged.connect(self.current_dim_changed)
                # Adding the widgets to layout
                label = QLabel(_("Axis:"))
                btn_layout.addWidget(label)
                btn_layout.addWidget(ra_combo)
                self.shape_label = QLabel()
                btn_layout.addWidget(self.shape_label)
                label = QLabel(_("Index:"))
                btn_layout.addWidget(label)
                btn_layout.addWidget(self.index_spin)
                self.slicing_label = QLabel()
                btn_layout.addWidget(self.slicing_label)
                # set the widget to display when launched
                self.current_dim_changed(self.last_dim)
            else:
                ra_combo = QComboBox(self)
                ra_combo.currentIndexChanged.connect(
                    self.stack.setCurrentIndex)
                ra_combo.addItems(names)
                btn_layout.addWidget(ra_combo)
            if is_masked_array:
                label = QLabel(
                    _("<u>Warning</u>: changes are applied separately"))
                label.setToolTip(_("For performance reasons, changes applied "\
                                   "to masked array won't be reflected in "\
                                   "array's data (and vice-versa)."))
                btn_layout.addWidget(label)

        btn_layout.addStretch()

        if not readonly:
            self.btn_save_and_close = QPushButton(_('Save and Close'))
            self.btn_save_and_close.setDisabled(True)
            self.btn_save_and_close.clicked.connect(self.accept)
            btn_layout.addWidget(self.btn_save_and_close)

        self.btn_close = QPushButton(_('Close'))
        self.btn_close.setAutoDefault(True)
        self.btn_close.setDefault(True)
        self.btn_close.clicked.connect(self.reject)
        btn_layout.addWidget(self.btn_close)
        self.layout.addLayout(btn_layout, 2, 0)

        self.setMinimumSize(400, 300)

        # Make the dialog act as a window
        self.setWindowFlags(Qt.Window)

        return True

    @Slot(QModelIndex, QModelIndex)
    def save_and_close_enable(self, left_top, bottom_right):
        """Handle the data change event to enable the save and close button."""
        if self.btn_save_and_close:
            self.btn_save_and_close.setEnabled(True)
            self.btn_save_and_close.setAutoDefault(True)
            self.btn_save_and_close.setDefault(True)

    def current_widget_changed(self, index):
        self.arraywidget = self.stack.widget(index)
        self.arraywidget.model.dataChanged.connect(self.save_and_close_enable)

    def change_active_widget(self, index):
        """
        This is implemented for handling negative values in index for
        3d arrays, to give the same behavior as slicing
        """
        string_index = [':'] * 3
        string_index[self.last_dim] = '<font color=red>%i</font>'
        self.slicing_label.setText(
            (r"Slicing: [" + ", ".join(string_index) + "]") % index)
        if index < 0:
            data_index = self.data.shape[self.last_dim] + index
        else:
            data_index = index
        slice_index = [slice(None)] * 3
        slice_index[self.last_dim] = data_index

        stack_index = self.dim_indexes[self.last_dim].get(data_index)
        if stack_index == None:
            stack_index = self.stack.count()
            try:
                self.stack.addWidget(
                    ArrayEditorWidget(self, self.data[slice_index]))
            except IndexError:  # Handle arrays of size 0 in one axis
                self.stack.addWidget(ArrayEditorWidget(self, self.data))
            self.dim_indexes[self.last_dim][data_index] = stack_index
            self.stack.update()
        self.stack.setCurrentIndex(stack_index)

    def current_dim_changed(self, index):
        """
        This change the active axis the array editor is plotting over
        in 3D
        """
        self.last_dim = index
        string_size = ['%i'] * 3
        string_size[index] = '<font color=red>%i</font>'
        self.shape_label.setText(
            ('Shape: (' + ', '.join(string_size) + ')    ') % self.data.shape)
        if self.index_spin.value() != 0:
            self.index_spin.setValue(0)
        else:
            # this is done since if the value is currently 0 it does not emit
            # currentIndexChanged(int)
            self.change_active_widget(0)
        self.index_spin.setRange(-self.data.shape[index],
                                 self.data.shape[index] - 1)

    @Slot()
    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.stack.count()):
            self.stack.widget(index).accept_changes()
        QDialog.accept(self)

    def get_value(self):
        """Return modified array -- this is *not* a copy"""
        # It is import to avoid accessing Qt C++ object as it has probably
        # already been destroyed, due to the Qt.WA_DeleteOnClose attribute
        return self.data

    def error(self, message):
        """An error occured, closing the dialog box"""
        QMessageBox.critical(self, _("Array editor"), message)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.reject()

    @Slot()
    def reject(self):
        """Reimplement Qt method"""
        if self.arraywidget is not None:
            for index in range(self.stack.count()):
                self.stack.widget(index).reject_changes()
        QDialog.reject(self)
Exemplo n.º 30
0
class ConfigDialog(QDialog):
    """Configuration or preferences dialog box"""

    # Signals
    check_settings = Signal()
    size_change = Signal(QSize)

    def __init__(self, parent=None, objname=None):
        QDialog.__init__(self, parent)

        # If used for data object in tree, the main is the tree widget.
        self.parent = parent
        self.objname = objname

        # Widgets
        self.pages_widget = QStackedWidget()
        self.contents_widget = QListWidget()
        self.button_reset = QPushButton(_('Reset to defaults'))

        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply |
                                QDialogButtonBox.Cancel)
        self.apply_btn = bbox.button(QDialogButtonBox.Apply)

        # Widgets setup
        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Ezcad), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)
        if self.objname is None:
            self.setWindowTitle(_('Preferences'))
        else:
            self.setWindowTitle(_('Preferences of ') + self.objname)
        self.setWindowIcon(ima.icon('configure'))
        self.contents_widget.setMovement(QListView.Static)
        self.contents_widget.setSpacing(1)
        self.contents_widget.setCurrentRow(0)

        # Layout
        hsplitter = QSplitter()
        hsplitter.addWidget(self.contents_widget)
        hsplitter.addWidget(self.pages_widget)
        hsplitter.setSizes([150,500])

        btnlayout = QHBoxLayout()
        btnlayout.addWidget(self.button_reset)
        btnlayout.addStretch(1)
        btnlayout.addWidget(bbox)

        vlayout = QVBoxLayout()
        vlayout.addWidget(hsplitter)
        vlayout.addLayout(btnlayout)

        self.setLayout(vlayout)

        # Signals and slots
        self.pages_widget.currentChanged.connect(self.current_page_changed)
        self.contents_widget.currentRowChanged.connect(
            self.pages_widget.setCurrentIndex)
        bbox.accepted.connect(self.accept)
        bbox.rejected.connect(self.reject)
        bbox.clicked.connect(self.button_clicked)

        # Ensures that the config is present on ezcad first run
        CONF.set('main', 'interface_language', load_lang_conf())

    def get_current_index(self):
        """Return current page index"""
        return self.contents_widget.currentRow()

    def set_current_index(self, index):
        """Set current page index"""
        self.contents_widget.setCurrentRow(index)

    def get_page(self, index=None):
        """Return page widget"""
        if index is None:
            widget = self.pages_widget.currentWidget()
        else:
            widget = self.pages_widget.widget(index)
        return widget.widget()

    @Slot()
    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.pages_widget.count()):
            configpage = self.get_page(index)
            configpage.apply_changes()
        QDialog.accept(self)

    def button_clicked(self, button):
        if button is self.apply_btn:
            # Apply button was clicked
            configpage = self.get_page()
            configpage.apply_changes()

    def current_page_changed(self, index):
        # widget = self.get_page(index)
        self.apply_btn.setVisible(True)
        self.apply_btn.setEnabled(True)

    def add_page(self, widget):
        scrollarea = QScrollArea(self)
        scrollarea.setWidgetResizable(True)
        scrollarea.setWidget(widget)
        self.pages_widget.addWidget(scrollarea)
        item = QListWidgetItem(self.contents_widget)
        try:
            item.setIcon(widget.get_icon())
        except TypeError:
            pass
        item.setText(widget.get_name())
        item.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
        item.setSizeHint(QSize(0, 25))

    def check_all_settings(self):
        """This method is called to check all configuration page settings
        after configuration dialog has been shown"""
        self.check_settings.emit()

    def resizeEvent(self, event):
        """
        Reimplement Qt method to be able to save the widget's size from the
        main application
        """
        QDialog.resizeEvent(self, event)
        self.size_change.emit(self.size())
Exemplo n.º 31
0
class PreferencesDialog(QDialog):
    """Preferences Dialog for Napari user settings."""

    ui_schema = {
        "call_order": {
            "ui:widget": "plugins"
        },
        "highlight_thickness": {
            "ui:widget": "highlight"
        },
        "shortcuts": {
            "ui:widget": "shortcuts"
        },
        "extension2reader": {
            "ui:widget": "extension2reader"
        },
    }

    resized = Signal(QSize)

    def __init__(self, parent=None):
        from ...settings import get_settings

        super().__init__(parent)
        self.setWindowTitle(trans._("Preferences"))

        self._settings = get_settings()
        self._stack = QStackedWidget(self)
        self._list = QListWidget(self)
        self._list.setObjectName("Preferences")
        self._list.currentRowChanged.connect(self._stack.setCurrentIndex)

        # Set up buttons
        self._button_cancel = QPushButton(trans._("Cancel"))
        self._button_cancel.clicked.connect(self.reject)
        self._button_ok = QPushButton(trans._("OK"))
        self._button_ok.clicked.connect(self.accept)
        self._button_ok.setDefault(True)
        self._button_restore = QPushButton(trans._("Restore defaults"))
        self._button_restore.clicked.connect(self._restore_default_dialog)

        # Layout
        left_layout = QVBoxLayout()
        left_layout.addWidget(self._list)
        left_layout.addStretch()
        left_layout.addWidget(self._button_restore)
        left_layout.addWidget(self._button_cancel)
        left_layout.addWidget(self._button_ok)

        self.setLayout(QHBoxLayout())
        self.layout().addLayout(left_layout, 1)
        self.layout().addWidget(self._stack, 3)

        # Build dialog from settings
        self._rebuild_dialog()

    def keyPressEvent(self, e: 'QKeyEvent'):
        if e.key() == Qt.Key_Escape:
            # escape key should just close the window
            # which implies "accept"
            e.accept()
            self.accept()
            return
        super().keyPressEvent(e)

    def resizeEvent(self, event):
        """Override to emit signal."""
        self.resized.emit(event.size())
        super().resizeEvent(event)

    def _rebuild_dialog(self):
        """Removes settings not to be exposed to user and creates dialog pages."""
        # FIXME: this dialog should not need to know about the plugin manager
        from ...plugins import plugin_manager

        self._starting_pm_order = plugin_manager.call_order()
        self._starting_values = self._settings.dict(exclude={'schema_version'})

        self._list.clear()
        while self._stack.count():
            self._stack.removeWidget(self._stack.currentWidget())

        for field in self._settings.__fields__.values():
            if isinstance(field.type_, type) and issubclass(
                    field.type_, BaseModel):
                self._add_page(field)

        self._list.setCurrentRow(0)

    def _add_page(self, field: 'ModelField'):
        """Builds the preferences widget using the json schema builder.

        Parameters
        ----------
        field : ModelField
            subfield for which to create a page.
        """
        from ..._vendor.qt_json_builder.qt_jsonschema_form import WidgetBuilder

        schema, values = self._get_page_dict(field)
        name = field.field_info.title or field.name

        form = WidgetBuilder().create_form(schema, self.ui_schema)
        # set state values for widget
        form.widget.state = values
        # make settings follow state of the form widget
        form.widget.on_changed.connect(
            lambda d: getattr(self._settings, name.lower()).update(d))

        # need to disable async if octree is enabled.
        # TODO: this shouldn't live here... if there is a coupling/dependency
        # between these settings, it should be declared in the settings schema
        if (name.lower() == 'experimental'
                and values['octree'] and self._settings.env_settings().get(
                    'experimental', {}).get('async_') not in (None, '0')):
            form_layout = form.widget.layout()
            for i in range(form_layout.count()):
                wdg = form_layout.itemAt(i, form_layout.FieldRole).widget()
                if getattr(wdg, '_name') == 'async_':
                    wdg.opacity.setOpacity(0.3)
                    wdg.setDisabled(True)
                    break

        self._list.addItem(field.field_info.title or field.name)
        self._stack.addWidget(form)

    def _get_page_dict(self, field: 'ModelField') -> Tuple[dict, dict, dict]:
        """Provides the schema, set of values for each setting, and the
        properties for each setting."""
        ftype = cast('BaseModel', field.type_)
        schema = json.loads(ftype.schema_json())

        # find enums:
        for name, subfield in ftype.__fields__.items():
            if isinstance(subfield.type_, EnumMeta):
                enums = [s.value for s in subfield.type_]  # type: ignore
                schema["properties"][name]["enum"] = enums
                schema["properties"][name]["type"] = "string"

        # Need to remove certain properties that will not be displayed on the GUI
        setting = getattr(self._settings, field.name)
        with setting.enums_as_values():
            values = setting.dict()
        napari_config = getattr(setting, "NapariConfig", None)
        if hasattr(napari_config, 'preferences_exclude'):
            for val in napari_config.preferences_exclude:
                schema['properties'].pop(val, None)
                values.pop(val, None)

        return schema, values

    def _restore_default_dialog(self):
        """Launches dialog to confirm restore settings choice."""
        response = QMessageBox.question(
            self,
            trans._("Restore Settings"),
            trans._("Are you sure you want to restore default settings?"),
            QMessageBox.RestoreDefaults | QMessageBox.Cancel,
            QMessageBox.RestoreDefaults,
        )
        if response == QMessageBox.RestoreDefaults:
            self._settings.reset()
            self._rebuild_dialog()  # TODO: do we need this?

    def _restart_required_dialog(self):
        """Displays the dialog informing user a restart is required."""
        QMessageBox.information(
            self,
            trans._("Restart required"),
            trans.
            _("A restart is required for some new settings to have an effect."
              ),
        )

    def closeEvent(self, event: 'QCloseEvent') -> None:
        event.accept()
        self.accept()

    def reject(self):
        """Restores the settings in place when dialog was launched."""
        self._settings.update(self._starting_values)

        # FIXME: this dialog should not need to know about the plugin manager
        if self._starting_pm_order:
            from ...plugins import plugin_manager

            plugin_manager.set_call_order(self._starting_pm_order)
        super().reject()
Exemplo n.º 32
0
class IMCWidget(QWidget):
    NAME = 'Imaging Mass Cytometry'
    FULL_NAME = f'napari-imc: {NAME}'
    AREA = 'right'
    ALLOWED_AREAS = ['left', 'right']

    def __init__(self,
                 napari_viewer: Viewer,
                 parent: Optional[QWidget] = None):
        super(IMCWidget, self).__init__(parent)
        self._controller = IMCController(napari_viewer, self)

        self._imc_file_tree_model = IMCFileTreeModel(self._controller,
                                                     parent=self)
        self._imc_file_tree_view = IMCFileTreeView(parent=self)
        self._imc_file_tree_view.setModel(self._imc_file_tree_model)

        self._channel_table_model = ChannelTableModel(self._controller,
                                                      parent=self)
        self._channel_table_proxy_model = QSortFilterProxyModel(self)
        self._channel_table_proxy_model.setSourceModel(
            self._channel_table_model)
        self._channel_table_proxy_model.sort(
            self._channel_table_model.LABEL_COLUMN)
        self._channel_table_view = ChannelTableView(parent=self)
        self._channel_table_view.setModel(self._channel_table_proxy_model)

        self._channel_controls_widget = ChannelControlsWidget(self._controller,
                                                              parent=self)
        self._channel_controls_container = QStackedWidget(self)
        self._channel_controls_container.setSizePolicy(QSizePolicy.Maximum,
                                                       QSizePolicy.Maximum)
        self._channel_controls_container.addWidget(QWidget(self))
        self._channel_controls_container.addWidget(
            self._channel_controls_widget)

        layout = QVBoxLayout(self)
        splitter = QSplitter(Qt.Vertical, self)
        splitter.addWidget(self._imc_file_tree_view)
        channel_panel = QWidget(self)
        channel_panel_layout = QVBoxLayout()
        channel_panel_layout.addWidget(self._channel_table_view)
        channel_panel_layout.addWidget(self._channel_controls_container)
        channel_panel.setLayout(channel_panel_layout)
        splitter.addWidget(channel_panel)
        layout.addWidget(splitter)
        self.setLayout(layout)

        # noinspection PyUnusedLocal
        # noinspection PyUnresolvedReferences
        @self._imc_file_tree_model.dataChanged.connect
        def on_imc_file_tree_model_data_changed(top_left: QModelIndex,
                                                bottom_right: QModelIndex,
                                                roles: Optional[int] = None):
            item = top_left.internalPointer()
            if isinstance(item, IMCFilePanoramaModel):
                if item.is_shown:
                    self._controller.hide_imc_file_panorama(item)
                else:
                    self._controller.show_imc_file_panorama(item)
            elif isinstance(item, IMCFileAcquisitionModel):
                if item.is_loaded:
                    self._controller.unload_imc_file_acquisition(item)
                else:
                    self._controller.load_imc_file_acquisition(item)

        # noinspection PyUnresolvedReferences
        @self._imc_file_tree_view.events.imc_file_closed.connect
        def on_imc_file_tree_view_imc_file_closed(imc_file: IMCFileModel):
            self._controller.close_imc_file(imc_file)

        # noinspection PyUnusedLocal
        # noinspection PyUnresolvedReferences
        @self._channel_table_model.dataChanged.connect
        def on_channel_table_model_data_changed(top_left: QModelIndex,
                                                bottom_right: QModelIndex,
                                                roles: Optional[int] = None):
            channel = self._controller.channels[top_left.row()]
            if channel.is_shown:
                self._controller.hide_channel(channel)
            else:
                self._controller.show_channel(channel)

        channel_table_selection_model: QItemSelectionModel = self._channel_table_view.selectionModel(
        )

        # noinspection PyUnusedLocal
        # noinspection PyUnresolvedReferences
        @channel_table_selection_model.selectionChanged.connect
        def on_channel_table_view_selection_changed(
                selected: QItemSelection, deselected: QItemSelection):
            selected_channels = []
            for index in self._channel_table_view.selectedIndexes():
                index = self._channel_table_proxy_model.mapToSource(index)
                channel = self._controller.channels[index.row()]
                selected_channels.append(channel)
            self._controller.selected_channels = selected_channels

    def select_channel(self, channel_index: int):
        top_left = self._channel_table_model.index(channel_index, 0)
        top_left = self._channel_table_proxy_model.mapFromSource(top_left)
        bottom_right = self._channel_table_model.index(
            channel_index,
            self._channel_table_model.columnCount() - 1)
        bottom_right = self._channel_table_proxy_model.mapFromSource(
            bottom_right)
        selection_model: QItemSelectionModel = self._channel_table_view.selectionModel(
        )
        selection_model.clearSelection(
        )  # first clear the selection, to make sure the channel controls refresh
        selection_model.select(QItemSelection(top_left, bottom_right),
                               QItemSelectionModel.Select)

    def refresh_channel_controls_widget(self):
        self._channel_controls_widget.refresh()
        if len(self._controller.selected_channels) > 0:
            self._channel_controls_container.setCurrentIndex(1)
        else:
            self._channel_controls_container.setCurrentIndex(0)

    @property
    def controller(self):
        return self._controller

    @property
    def imc_file_tree_model(self) -> IMCFileTreeModel:
        return self._imc_file_tree_model

    @property
    def channel_table_model(self) -> ChannelTableModel:
        return self._channel_table_model
Exemplo n.º 33
0
class MixerPanel(QFrame):
    '''A color mixer to hook up to an image.
    You pass the image you the panel to operate on
    and it operates on that image in place. You also
    pass a callback to be called to trigger a refresh.
    This callback is called every time the mixer modifies
    your image.'''
    def __init__(self, img):
        QFrame.__init__(self)
        # self.setFrameStyle(QFrame.Box | QFrame.Sunken)

        self.img = img
        self.mixer = ColorMixer(self.img)
        self.callback = None

        #---------------------------------------------------------------
        # ComboBox
        #---------------------------------------------------------------

        self.combo_box_entries = [
            'RGB Color', 'HSV Color', 'Brightness/Contrast', 'Gamma',
            'Gamma (Sigmoidal)'
        ]
        self.combo_box = QComboBox()
        for entry in self.combo_box_entries:
            self.combo_box.addItem(entry)
        self.combo_box.currentIndexChanged.connect(self.combo_box_changed)

        #---------------------------------------------------------------
        # RGB color sliders
        #---------------------------------------------------------------

        # radio buttons
        self.rgb_add = QRadioButton('Additive')
        self.rgb_mul = QRadioButton('Multiplicative')
        self.rgb_mul.toggled.connect(self.rgb_radio_changed)
        self.rgb_add.toggled.connect(self.rgb_radio_changed)

        # sliders
        rs = IntelligentSlider('R', 0.51, -255, self.rgb_changed)
        gs = IntelligentSlider('G', 0.51, -255, self.rgb_changed)
        bs = IntelligentSlider('B', 0.51, -255, self.rgb_changed)
        self.rs = rs
        self.gs = gs
        self.bs = bs

        self.rgb_widget = QWidget()
        self.rgb_widget.layout = QGridLayout(self.rgb_widget)
        self.rgb_widget.layout.addWidget(self.rgb_add, 0, 0, 1, 3)
        self.rgb_widget.layout.addWidget(self.rgb_mul, 1, 0, 1, 3)
        self.rgb_widget.layout.addWidget(self.rs, 2, 0)
        self.rgb_widget.layout.addWidget(self.gs, 2, 1)
        self.rgb_widget.layout.addWidget(self.bs, 2, 2)

        #---------------------------------------------------------------
        # HSV sliders
        #---------------------------------------------------------------
        # radio buttons
        self.hsv_add = QRadioButton('Additive')
        self.hsv_mul = QRadioButton('Multiplicative')
        self.hsv_mul.toggled.connect(self.hsv_radio_changed)
        self.hsv_mul.toggled.connect(self.hsv_radio_changed)

        # sliders
        hs = IntelligentSlider('H', 0.36, -180, self.hsv_changed)
        ss = IntelligentSlider('S', 0.002, 0, self.hsv_changed)
        vs = IntelligentSlider('V', 0.002, 0, self.hsv_changed)
        self.hs = hs
        self.ss = ss
        self.vs = vs

        self.hsv_widget = QWidget()
        self.hsv_widget.layout = QGridLayout(self.hsv_widget)
        self.hsv_widget.layout.addWidget(self.hsv_add, 0, 0, 1, 3)
        self.hsv_widget.layout.addWidget(self.hsv_mul, 1, 0, 1, 3)
        self.hsv_widget.layout.addWidget(self.hs, 2, 0)
        self.hsv_widget.layout.addWidget(self.ss, 2, 1)
        self.hsv_widget.layout.addWidget(self.vs, 2, 2)

        #---------------------------------------------------------------
        # Brightness/Contrast sliders
        #---------------------------------------------------------------
        # sliders
        cont = IntelligentSlider('x', 0.002, 0, self.bright_changed)
        bright = IntelligentSlider('+', 0.51, -255, self.bright_changed)
        self.cont = cont
        self.bright = bright

        # layout
        self.bright_widget = QWidget()
        self.bright_widget.layout = QGridLayout(self.bright_widget)
        self.bright_widget.layout.addWidget(self.cont, 0, 0)
        self.bright_widget.layout.addWidget(self.bright, 0, 1)

        #----------------------------------------------------------------------
        # Gamma Slider
        #----------------------------------------------------------------------
        gamma = IntelligentSlider('gamma', 0.005, 0, self.gamma_changed)
        self.gamma = gamma

        # layout
        self.gamma_widget = QWidget()
        self.gamma_widget.layout = QGridLayout(self.gamma_widget)
        self.gamma_widget.layout.addWidget(self.gamma, 0, 0)

        #---------------------------------------------------------------
        # Sigmoid Gamma sliders
        #---------------------------------------------------------------
        # sliders
        alpha = IntelligentSlider('alpha', 0.011, 1, self.sig_gamma_changed)
        beta = IntelligentSlider('beta', 0.012, 0, self.sig_gamma_changed)
        self.a_gamma = alpha
        self.b_gamma = beta

        # layout
        self.sig_gamma_widget = QWidget()
        self.sig_gamma_widget.layout = QGridLayout(self.sig_gamma_widget)
        self.sig_gamma_widget.layout.addWidget(self.a_gamma, 0, 0)
        self.sig_gamma_widget.layout.addWidget(self.b_gamma, 0, 1)

        #---------------------------------------------------------------
        # Buttons
        #---------------------------------------------------------------
        self.commit_button = QPushButton('Commit')
        self.commit_button.clicked.connect(self.commit_changes)
        self.revert_button = QPushButton('Revert')
        self.revert_button.clicked.connect(self.revert_changes)

        #---------------------------------------------------------------
        # Mixer Layout
        #---------------------------------------------------------------
        self.sliders = QStackedWidget()
        self.sliders.addWidget(self.rgb_widget)
        self.sliders.addWidget(self.hsv_widget)
        self.sliders.addWidget(self.bright_widget)
        self.sliders.addWidget(self.gamma_widget)
        self.sliders.addWidget(self.sig_gamma_widget)

        self.layout = QGridLayout(self)
        self.layout.addWidget(self.combo_box, 0, 0)
        self.layout.addWidget(self.sliders, 1, 0)
        self.layout.addWidget(self.commit_button, 2, 0)
        self.layout.addWidget(self.revert_button, 3, 0)

        #---------------------------------------------------------------
        # State Initialization
        #---------------------------------------------------------------

        self.combo_box.setCurrentIndex(0)
        self.rgb_mul.setChecked(True)
        self.hsv_mul.setChecked(True)

    def set_callback(self, callback):
        self.callback = callback

    def combo_box_changed(self, index):
        self.sliders.setCurrentIndex(index)
        self.reset()

    def rgb_radio_changed(self):
        self.reset()

    def hsv_radio_changed(self):
        self.reset()

    def reset(self):
        self.reset_sliders()
        self.mixer.set_to_stateimg()
        if self.callback:
            self.callback()

    def reset_sliders(self):
        # handle changing the conversion factors necessary
        if self.rgb_add.isChecked():
            self.rs.set_conv_fac(0.51, -255)
            self.rs.set_value(0)
            self.gs.set_conv_fac(0.51, -255)
            self.gs.set_value(0)
            self.bs.set_conv_fac(0.51, -255)
            self.bs.set_value(0)
        else:
            self.rs.set_conv_fac(0.002, 0)
            self.rs.set_value(1.)
            self.gs.set_conv_fac(0.002, 0)
            self.gs.set_value(1.)
            self.bs.set_conv_fac(0.002, 0)
            self.bs.set_value(1.)

        self.hs.set_value(0)
        if self.hsv_add.isChecked():
            self.ss.set_conv_fac(0.002, -1)
            self.ss.set_value(0)
            self.vs.set_conv_fac(0.002, -1)
            self.vs.set_value(0)
        else:
            self.ss.set_conv_fac(0.002, 0)
            self.ss.set_value(1.)
            self.vs.set_conv_fac(0.002, 0)
            self.vs.set_value(1.)

        self.bright.set_value(0)
        self.cont.set_value(1.)

        self.gamma.set_value(1)
        self.a_gamma.set_value(1)
        self.b_gamma.set_value(0.5)

    def rgb_changed(self, name, val):
        if name == 'R':
            channel = self.mixer.RED
        elif name == 'G':
            channel = self.mixer.GREEN
        else:
            channel = self.mixer.BLUE

        if self.rgb_mul.isChecked():
            self.mixer.multiply(channel, val)
        elif self.rgb_add.isChecked():
            self.mixer.add(channel, val)
        else:
            pass

        if self.callback:
            self.callback()

    def hsv_changed(self, name, val):
        h = self.hs.val()
        s = self.ss.val()
        v = self.vs.val()

        if self.hsv_mul.isChecked():
            self.mixer.hsv_multiply(h, s, v)
        elif self.hsv_add.isChecked():
            self.mixer.hsv_add(h, s, v)
        else:
            pass

        if self.callback:
            self.callback()

    def bright_changed(self, name, val):
        b = self.bright.val()
        c = self.cont.val()
        self.mixer.brightness(c, b)

        if self.callback:
            self.callback()

    def gamma_changed(self, name, val):
        self.mixer.gamma(val)

        if self.callback:
            self.callback()

    def sig_gamma_changed(self, name, val):
        ag = self.a_gamma.val()
        bg = self.b_gamma.val()
        self.mixer.sigmoid_gamma(ag, bg)

        if self.callback:
            self.callback()

    def commit_changes(self):
        self.mixer.commit_changes()
        self.reset_sliders()

    def revert_changes(self):
        self.mixer.revert()
        self.reset_sliders()

        if self.callback:
            self.callback()
Exemplo n.º 34
0
class PreferencesDialog(QDialog):
    """Preferences Dialog for Napari user settings."""

    valueChanged = Signal()

    ui_schema = {
        "call_order": {
            "ui:widget": "plugins"
        },
        "highlight_thickness": {
            "ui:widget": "highlight"
        },
    }

    resized = Signal(QSize)
    closed = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)

        self._list = QListWidget(self)
        self._stack = QStackedWidget(self)

        self._list.setObjectName("Preferences")

        # Set up buttons
        self._button_cancel = QPushButton(trans._("Cancel"))
        self._button_ok = QPushButton(trans._("OK"))
        self._default_restore = QPushButton(trans._("Restore defaults"))

        # Setup
        self.setWindowTitle(trans._("Preferences"))

        # Layout
        left_layout = QVBoxLayout()
        left_layout.addWidget(self._list)
        left_layout.addStretch()
        left_layout.addWidget(self._default_restore)
        left_layout.addWidget(self._button_cancel)
        left_layout.addWidget(self._button_ok)

        main_layout = QHBoxLayout()
        main_layout.addLayout(left_layout, 1)
        main_layout.addWidget(self._stack, 3)

        self.setLayout(main_layout)

        # Signals

        self._list.currentRowChanged.connect(
            lambda index: self._stack.setCurrentIndex(index))
        self._button_cancel.clicked.connect(self.on_click_cancel)
        self._button_ok.clicked.connect(self.on_click_ok)
        self._default_restore.clicked.connect(self.restore_defaults)

        # Make widget

        self.make_dialog()
        self._list.setCurrentRow(0)

    def _restart_dialog(self, event=None, extra_str=""):
        """Displays the dialog informing user a restart is required.

        Paramters
        ---------
        event : Event
        extra_str : str
            Extra information to add to the message about needing a restart.
        """

        text_str = trans._(
            "napari requires a restart for image rendering changes to apply.")

        widget = ResetNapariInfoDialog(
            parent=self,
            text=text_str,
        )
        widget.exec_()

    def closeEvent(self, event):
        """Override to emit signal."""
        self.closed.emit()
        super().closeEvent(event)

    def reject(self):
        """Override to handle Escape."""
        super().reject()
        self.close()

    def resizeEvent(self, event):
        """Override to emit signal."""
        self.resized.emit(event.size())
        super().resizeEvent(event)

    def make_dialog(self):
        """Removes settings not to be exposed to user and creates dialog pages."""
        settings = get_settings()
        # Because there are multiple pages, need to keep a dictionary of values dicts.
        # One set of keywords are for each page, then in each entry for a page, there are dicts
        # of setting and its value.
        self._values_orig_dict = {}
        self._values_dict = {}
        self._setting_changed_dict = {}

        for page, setting in settings.schemas().items():
            schema, values, properties = self.get_page_dict(setting)

            self._setting_changed_dict[page] = {}
            self._values_orig_dict[page] = values
            self._values_dict[page] = values

            # Only add pages if there are any properties to add.
            if properties:
                self.add_page(schema, values)

    def get_page_dict(self, setting):
        """Provides the schema, set of values for each setting, and the properties
        for each setting.

        Parameters
        ----------
        setting : dict
            Dictionary of settings for a page within the settings manager.

        Returns
        -------
        schema : dict
            Json schema of the setting page.
        values : dict
            Dictionary of values currently set for each parameter in the settings.
        properties : dict
            Dictionary of properties within the json schema.

        """
        schema = json.loads(setting['json_schema'])

        # Resolve allOf references
        definitions = schema.get("definitions", {})
        if definitions:
            for key, data in schema["properties"].items():
                if "allOf" in data:
                    allof = data["allOf"]
                    allof = [d["$ref"].rsplit("/")[-1] for d in allof]
                    for definition in allof:
                        local_def = definitions[definition]
                        schema["properties"][key]["enum"] = local_def["enum"]
                        schema["properties"][key]["type"] = "string"

        # Need to remove certain properties that will not be displayed on the GUI
        properties = schema.pop('properties')
        model = setting['model']
        values = model.dict()
        napari_config = getattr(model, "NapariConfig", None)
        if napari_config is not None:
            for val in napari_config.preferences_exclude:
                properties.pop(val)
                values.pop(val)

        schema['properties'] = properties

        return schema, values, properties

    def restore_defaults(self):
        """Launches dialog to confirm restore settings choice."""
        self._reset_dialog = ConfirmDialog(
            parent=self,
            text=trans._("Are you sure you want to restore default settings?"),
        )
        self._reset_dialog.valueChanged.connect(self._reset_widgets)
        self._reset_dialog.exec_()

    def _reset_widgets(self):
        """Deletes the widgets and rebuilds with defaults."""
        self.close()
        self.valueChanged.emit()
        self._list.clear()

        for n in range(self._stack.count()):
            widget = self._stack.removeWidget(self._stack.currentWidget())
            del widget

        self.make_dialog()
        self._list.setCurrentRow(0)
        self.show()

    def on_click_ok(self):
        """Keeps the selected preferences saved to settings."""
        self.close()

    def on_click_cancel(self):
        """Restores the settings in place when dialog was launched."""
        # Need to check differences for each page.
        settings = get_settings()
        for n in range(self._stack.count()):
            # Must set the current row so that the proper list is updated
            # in check differences.
            self._list.setCurrentRow(n)
            page = self._list.currentItem().text().split(" ")[0].lower()
            # get new values for settings.  If they were changed from values at beginning
            # of preference dialog session, change them back.
            # Using the settings value seems to be the best way to get the checkboxes right
            # on the plugin call order widget.
            setting = settings.schemas()[page]
            schema, new_values, properties = self.get_page_dict(setting)
            self.check_differences(self._values_orig_dict[page], new_values)

        self._list.setCurrentRow(0)
        self.close()

    def add_page(self, schema, values):
        """Creates a new page for each section in dialog.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """
        widget = self.build_page_dialog(schema, values)
        self._list.addItem(schema["title"])
        self._stack.addWidget(widget)

    def build_page_dialog(self, schema, values):
        """Builds the preferences widget using the json schema builder.

        Parameters
        ----------
        schema : dict
            Json schema including all information to build each page in the
            preferences dialog.
        values : dict
            Dictionary of current values set in preferences.
        """
        settings = get_settings()
        builder = WidgetBuilder()
        form = builder.create_form(schema, self.ui_schema)

        # Disable widgets that loaded settings from environment variables
        section = schema["section"]
        form_layout = form.widget.layout()
        for row in range(form.widget.layout().rowCount()):
            widget = form_layout.itemAt(row, form_layout.FieldRole).widget()
            name = widget._name
            disable = bool(
                settings._env_settings.get(section, {}).get(name, None))
            widget.setDisabled(disable)
            try:
                widget.opacity.setOpacity(0.3 if disable else 1)
            except AttributeError:
                # some widgets may not have opacity (such as the QtPluginSorter)
                pass

        # set state values for widget
        form.widget.state = values

        if section == 'experimental':
            # need to disable async if octree is enabled.
            if values['octree'] is True:
                form = self._disable_async(form, values)

        form.widget.on_changed.connect(lambda d: self.check_differences(
            d,
            self._values_dict[schema["title"].lower()],
        ))

        return form

    def _disable_async(self, form, values, disable=True, state=True):
        """Disable async if octree is True."""
        settings = get_settings()
        # need to make sure that if async_ is an environment setting, that we don't
        # enable it here.
        if (settings._env_settings['experimental'].get('async_', None)
                is not None):
            disable = True

        idx = list(values.keys()).index('async_')
        form_layout = form.widget.layout()
        widget = form_layout.itemAt(idx, form_layout.FieldRole).widget()
        widget.opacity.setOpacity(0.3 if disable else 1)
        widget.setDisabled(disable)

        return form

    def _values_changed(self, page, new_dict, old_dict):
        """Loops through each setting in a page to determine if it changed.

        Parameters
        ----------
        new_dict : dict
            Dict that has the most recent changes by user. Each key is a setting value
            and each item is the value.
        old_dict : dict
            Dict wtih values set at the begining of preferences dialog session.

        """
        for setting_name, value in new_dict.items():
            if value != old_dict[setting_name]:
                self._setting_changed_dict[page][setting_name] = value
            elif (value == old_dict[setting_name]
                  and setting_name in self._setting_changed_dict[page]):
                self._setting_changed_dict[page].pop(setting_name)

    def set_current_index(self, index: int):
        """
        Set the current page on the preferences by index.

        Parameters
        ----------
        index : int
            Index of page to set as current one.
        """
        self._list.setCurrentRow(index)

    def check_differences(self, new_dict, old_dict):
        """Changes settings in settings manager with changes from dialog.

        Parameters
        ----------
        new_dict : dict
            Dict that has the most recent changes by user. Each key is a setting parameter
            and each item is the value.
        old_dict : dict
            Dict wtih values set at the beginning of the preferences dialog session.
        """
        settings = get_settings()
        page = self._list.currentItem().text().split(" ")[0].lower()
        self._values_changed(page, new_dict, old_dict)
        different_values = self._setting_changed_dict[page]

        if len(different_values) > 0:
            # change the values in settings
            for setting_name, value in different_values.items():
                try:
                    setattr(settings._settings[page], setting_name, value)
                    self._values_dict[page] = new_dict

                    if page == 'experimental':

                        if setting_name == 'octree':

                            # disable/enable async checkbox
                            widget = self._stack.currentWidget()
                            cstate = True if value is True else False
                            self._disable_async(widget,
                                                new_dict,
                                                disable=cstate)

                            # need to inform user that napari restart needed.
                            self._restart_dialog()

                        elif setting_name == 'async_':
                            # need to inform user that napari restart needed.
                            self._restart_dialog()

                except:  # noqa: E722
                    continue
Exemplo n.º 35
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)
Exemplo n.º 36
0
class VariableExplorer(QWidget, SpyderPluginMixin):
    """
    Variable Explorer Plugin
    """
    CONF_SECTION = 'variable_explorer'
    CONFIGWIDGET_CLASS = VariableExplorerConfigPage
    sig_option_changed = Signal(str, object)

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        SpyderPluginMixin.__init__(self, parent)

        # Widgets
        self.stack = QStackedWidget(self)
        self.shellwidgets = {}

        # Layout
        layout = QVBoxLayout()
        layout.addWidget(self.stack)
        self.setLayout(layout)

        # Initialize plugin
        self.initialize_plugin()

    @staticmethod
    def get_settings():
        """
        Return Variable Explorer settings dictionary
        (i.e. namespace browser settings according to Spyder's configuration file)
        """
        settings = {}
        #        CONF.load_from_ini() # necessary only when called from another process
        for name in REMOTE_SETTINGS:
            settings[name] = CONF.get(VariableExplorer.CONF_SECTION, name)
        return settings

    # ----- Stack accesors ----------------------------------------------------
    def set_current_widget(self, nsb):
        self.stack.setCurrentWidget(nsb)

    def current_widget(self):
        return self.stack.currentWidget()

    def count(self):
        return self.stack.count()

    def remove_widget(self, nsb):
        self.stack.removeWidget(nsb)

    def add_widget(self, nsb):
        self.stack.addWidget(nsb)

    # ----- Public API --------------------------------------------------------
    def add_shellwidget(self, shellwidget):
        shellwidget_id = id(shellwidget)
        # Add shell only once: this method may be called two times in a row
        # by the External console plugin (dev. convenience)
        from spyderlib.widgets.externalshell import systemshell
        if isinstance(shellwidget, systemshell.ExternalSystemShell):
            return
        if shellwidget_id not in self.shellwidgets:
            nsb = NamespaceBrowser(self)
            nsb.set_shellwidget(shellwidget)
            nsb.setup(**VariableExplorer.get_settings())
            nsb.sig_option_changed.connect(self.sig_option_changed.emit)
            self.add_widget(nsb)
            self.shellwidgets[shellwidget_id] = nsb
            self.set_shellwidget_from_id(shellwidget_id)
            return nsb

    def remove_shellwidget(self, shellwidget_id):
        # If shellwidget_id is not in self.shellwidgets, it simply means
        # that shell was not a Python-based console (it was a terminal)
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets.pop(shellwidget_id)
            self.remove_widget(nsb)
            nsb.close()

    def set_shellwidget_from_id(self, shellwidget_id):
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets[shellwidget_id]
            self.set_current_widget(nsb)
            if self.isvisible:
                nsb.visibility_changed(True)

    def import_data(self, fname):
        """Import data in current namespace"""
        if self.count():
            nsb = self.current_widget()
            nsb.refresh_table()
            nsb.import_data(filename=fname)
            if self.dockwidget and not self.ismaximized:
                self.dockwidget.setVisible(True)
                self.dockwidget.raise_()

    #------ SpyderPluginMixin API ---------------------------------------------
    def visibility_changed(self, enable):
        """DockWidget visibility has changed"""
        SpyderPluginMixin.visibility_changed(self, enable)
        for nsb in list(self.shellwidgets.values()):
            nsb.visibility_changed(enable and nsb is self.current_widget())

    #------ SpyderPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _('Variable explorer')

    def get_plugin_icon(self):
        """Return plugin icon"""
        return ima.icon('dictedit')

    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.current_widget()

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True

    def refresh_plugin(self):
        """Refresh widget"""
        pass

    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return []

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.main.extconsole.set_variableexplorer(self)
        self.main.add_dockwidget(self)

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        for nsb in list(self.shellwidgets.values()):
            nsb.setup(**VariableExplorer.get_settings())
        ar_timeout = self.get_option('autorefresh/timeout')
        for shellwidget in self.main.extconsole.shellwidgets:
            shellwidget.set_autorefresh_timeout(ar_timeout)
Exemplo n.º 37
0
class VariableExplorer(QWidget, SpyderPluginMixin):
    """
    Variable Explorer Plugin
    """
    CONF_SECTION = 'variable_explorer'
    CONFIGWIDGET_CLASS = VariableExplorerConfigPage
    sig_option_changed = Signal(str, object)

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        SpyderPluginMixin.__init__(self, parent)

        # Widgets
        self.stack = QStackedWidget(self)
        self.shellwidgets = {}

        # Layout
        layout = QVBoxLayout()
        layout.addWidget(self.stack)
        self.setLayout(layout)

        # Initialize plugin
        self.initialize_plugin()

    @staticmethod
    def get_settings():
        """
        Return Variable Explorer settings dictionary
        (i.e. namespace browser settings according to Spyder's configuration file)
        """
        settings = {}
#        CONF.load_from_ini() # necessary only when called from another process
        for name in REMOTE_SETTINGS:
            settings[name] = CONF.get(VariableExplorer.CONF_SECTION, name)
        return settings

    # ----- Stack accesors ----------------------------------------------------
    def set_current_widget(self, nsb):
        self.stack.setCurrentWidget(nsb)

    def current_widget(self):
        return self.stack.currentWidget()

    def count(self):
        return self.stack.count()

    def remove_widget(self, nsb):
        self.stack.removeWidget(nsb)

    def add_widget(self, nsb):
        self.stack.addWidget(nsb)

    # ----- Public API --------------------------------------------------------
    def add_shellwidget(self, shellwidget):
        shellwidget_id = id(shellwidget)
        # Add shell only once: this method may be called two times in a row
        # by the External console plugin (dev. convenience)
        from spyder.widgets.externalshell import systemshell
        if isinstance(shellwidget, systemshell.ExternalSystemShell):
            return
        if shellwidget_id not in self.shellwidgets:
            nsb = NamespaceBrowser(self)
            nsb.set_shellwidget(shellwidget)
            nsb.setup(**VariableExplorer.get_settings())
            nsb.sig_option_changed.connect(self.sig_option_changed.emit)
            self.add_widget(nsb)
            self.shellwidgets[shellwidget_id] = nsb
            self.set_shellwidget_from_id(shellwidget_id)
            return nsb
        
    def remove_shellwidget(self, shellwidget_id):
        # If shellwidget_id is not in self.shellwidgets, it simply means
        # that shell was not a Python-based console (it was a terminal)
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets.pop(shellwidget_id)
            self.remove_widget(nsb)
            nsb.close()
    
    def set_shellwidget_from_id(self, shellwidget_id):
        if shellwidget_id in self.shellwidgets:
            nsb = self.shellwidgets[shellwidget_id]
            self.set_current_widget(nsb)
            if self.isvisible:
                nsb.visibility_changed(True)

    def import_data(self, fname):
        """Import data in current namespace"""
        if self.count():
            nsb = self.current_widget()
            nsb.refresh_table()
            nsb.import_data(filenames=fname)
            if self.dockwidget and not self.ismaximized:
                self.dockwidget.setVisible(True)
                self.dockwidget.raise_()

    #------ SpyderPluginMixin API ---------------------------------------------
    def visibility_changed(self, enable):
        """DockWidget visibility has changed"""
        SpyderPluginMixin.visibility_changed(self, enable)
        for nsb in list(self.shellwidgets.values()):
            nsb.visibility_changed(enable and nsb is self.current_widget())
    
    #------ SpyderPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _('Variable explorer')

    def get_plugin_icon(self):
        """Return plugin icon"""
        return ima.icon('dictedit')
    
    def get_focus_widget(self):
        """
        Return the widget to give focus to when
        this plugin's dockwidget is raised on top-level
        """
        return self.current_widget()
        
    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True
        
    def refresh_plugin(self):
        """Refresh widget"""
        pass
    
    def get_plugin_actions(self):
        """Return a list of actions related to plugin"""
        return []
    
    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.main.extconsole.set_variableexplorer(self)
        self.main.add_dockwidget(self)
        
    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        for nsb in list(self.shellwidgets.values()):
            nsb.setup(**VariableExplorer.get_settings())
        ar_timeout = self.get_option('autorefresh/timeout')
        for shellwidget in self.main.extconsole.shellwidgets:
            shellwidget.set_autorefresh_timeout(ar_timeout)
Exemplo n.º 38
0
class ConfigDialog(QDialog):
    """Spyder configuration ('Preferences') dialog box"""

    # Signals
    check_settings = Signal()
    size_change = Signal(QSize)

    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        self.main = parent

        # Widgets
        self.pages_widget = QStackedWidget()
        self.pages_widget.setMinimumWidth(600)
        self.contents_widget = QListWidget()
        self.button_reset = QPushButton(_('Reset to defaults'))

        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply
                                | QDialogButtonBox.Cancel)
        self.apply_btn = bbox.button(QDialogButtonBox.Apply)
        self.ok_btn = bbox.button(QDialogButtonBox.Ok)

        # Widgets setup
        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setWindowTitle(_('Preferences'))
        self.setWindowIcon(ima.icon('configure'))
        self.contents_widget.setMovement(QListView.Static)
        self.contents_widget.setSpacing(1)
        self.contents_widget.setCurrentRow(0)
        self.contents_widget.setMinimumWidth(220)
        self.contents_widget.setMinimumHeight(400)

        # Layout
        hsplitter = QSplitter()
        hsplitter.addWidget(self.contents_widget)
        hsplitter.addWidget(self.pages_widget)
        hsplitter.setStretchFactor(0, 1)
        hsplitter.setStretchFactor(1, 2)

        btnlayout = QHBoxLayout()
        btnlayout.addWidget(self.button_reset)
        btnlayout.addStretch(1)
        btnlayout.addWidget(bbox)

        vlayout = QVBoxLayout()
        vlayout.addWidget(hsplitter)
        vlayout.addLayout(btnlayout)

        self.setLayout(vlayout)

        # Signals and slots
        if self.main:
            self.button_reset.clicked.connect(self.main.reset_spyder)
        self.pages_widget.currentChanged.connect(self.current_page_changed)
        self.contents_widget.currentRowChanged.connect(
            self.pages_widget.setCurrentIndex)
        bbox.accepted.connect(self.accept)
        bbox.rejected.connect(self.reject)
        bbox.clicked.connect(self.button_clicked)

        # Ensures that the config is present on spyder first run
        CONF.set('main', 'interface_language', load_lang_conf())

    def get_current_index(self):
        """Return current page index"""
        return self.contents_widget.currentRow()

    def set_current_index(self, index):
        """Set current page index"""
        self.contents_widget.setCurrentRow(index)

    def get_page(self, index=None):
        """Return page widget"""
        if index is None:
            widget = self.pages_widget.currentWidget()
        else:
            widget = self.pages_widget.widget(index)

        if widget:
            return widget.widget()

    def get_index_by_name(self, name):
        """Return page index by CONF_SECTION name."""
        for idx in range(self.pages_widget.count()):
            widget = self.pages_widget.widget(idx)
            widget = widget.widget()
            if widget.CONF_SECTION == name:
                return idx
        else:
            return None

    @Slot()
    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.pages_widget.count()):
            configpage = self.get_page(index)
            if not configpage.is_valid():
                return
            configpage.apply_changes()
        QDialog.accept(self)

    def button_clicked(self, button):
        if button is self.apply_btn:
            # Apply button was clicked
            configpage = self.get_page()
            if not configpage.is_valid():
                return
            configpage.apply_changes()

    def current_page_changed(self, index):
        widget = self.get_page(index)
        self.apply_btn.setVisible(widget.apply_callback is not None)
        self.apply_btn.setEnabled(widget.is_modified)

    def add_page(self, widget):
        self.check_settings.connect(widget.check_settings)
        widget.show_this_page.connect(lambda row=self.contents_widget.count():
                                      self.contents_widget.setCurrentRow(row))
        widget.apply_button_enabled.connect(self.apply_btn.setEnabled)
        scrollarea = QScrollArea(self)
        scrollarea.setWidgetResizable(True)
        scrollarea.setWidget(widget)
        self.pages_widget.addWidget(scrollarea)
        item = QListWidgetItem(self.contents_widget)
        try:
            item.setIcon(widget.get_icon())
        except TypeError:
            pass
        item.setText(widget.get_name())
        item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        item.setSizeHint(QSize(0, 25))

    def check_all_settings(self):
        """This method is called to check all configuration page settings
        after configuration dialog has been shown"""
        self.check_settings.emit()

    def resizeEvent(self, event):
        """
        Reimplement Qt method to be able to save the widget's size from the
        main application
        """
        QDialog.resizeEvent(self, event)
        self.size_change.emit(self.size())
Exemplo n.º 39
0
class ShellConnectMainWidget(PluginMainWidget):
    """
    Main widget to use in a plugin that shows console-specific content.

    Notes
    -----
    * This is composed of a QStackedWidget to stack widgets associated to each
      shell widget in the console and only show one of them at a time.
    * The current widget in the stack will display the content associated to
      the console with focus.
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Widgets
        self._stack = QStackedWidget(self)
        self._shellwidgets = {}

        # Layout
        layout = QVBoxLayout()
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self._stack)
        self.setLayout(layout)

    def update_style(self):
        self._stack.setStyleSheet("QStackedWidget {padding: 0px; border: 0px}")

    # ---- Stack accesors
    # ------------------------------------------------------------------------
    def count(self):
        """
        Return the number of widgets in the stack.

        Returns
        -------
        int
            The number of widgets in the stack.
        """
        return self._stack.count()

    def current_widget(self):
        """
        Return the current figure browser widget in the stack.

        Returns
        -------
        QWidget
            The current widget.
        """
        return self._stack.currentWidget()

    def get_focus_widget(self):
        return self.current_widget()

    # ---- Public API
    # ------------------------------------------------------------------------
    def add_shellwidget(self, shellwidget):
        """
        Create a new widget in the stack and associate it to
        shellwidget.
        """
        shellwidget_id = id(shellwidget)
        if shellwidget_id not in self._shellwidgets:
            widget = self.create_new_widget(shellwidget)
            self._stack.addWidget(widget)
            self._shellwidgets[shellwidget_id] = widget

            # Add all actions to new widget for shortcuts to work.
            for __, action in self.get_actions().items():
                if action:
                    widget_actions = widget.actions()
                    if action not in widget_actions:
                        widget.addAction(action)

            self.set_shellwidget(shellwidget)

    def remove_shellwidget(self, shellwidget):
        """Remove widget associated to shellwidget."""
        shellwidget_id = id(shellwidget)
        if shellwidget_id in self._shellwidgets:
            widget = self._shellwidgets.pop(shellwidget_id)
            self._stack.removeWidget(widget)
            self.close_widget(widget)
            self.update_actions()

    def set_shellwidget(self, shellwidget):
        """
        Set widget associated with shellwidget as the current widget.
        """
        shellwidget_id = id(shellwidget)
        old_widget = self.current_widget()
        if shellwidget_id in self._shellwidgets:
            widget = self._shellwidgets[shellwidget_id]
            self._stack.setCurrentWidget(widget)
            self.switch_widget(widget, old_widget)
            self.update_actions()

    def create_new_widget(self, shellwidget):
        """Create a widget to communicate with shellwidget."""
        raise NotImplementedError

    def close_widget(self, widget):
        """Close the widget."""
        raise NotImplementedError

    def switch_widget(self, widget, old_widget):
        """Switch the current widget."""
        raise NotImplementedError

    def refresh(self):
        """Refresh widgets."""
        if self.count():
            widget = self.current_widget()
            widget.refresh()