예제 #1
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
예제 #2
0
class RedisTree(QMainWindow):

    addKey = Signal(object)
    currentChanged = Signal(object)

    def __init__(self, redis, parent=None):
        super(RedisTree, self).__init__(parent)
        self.load_ui()
        ui = self.ui
        self.redis = redis
        self.source_model = RedisKeyModel(redis)
        self.sort_filter_model = QSortFilterProxyModel()
        self.sort_filter_model.setFilterRole(KeyNameRole)
        self.sort_filter_model.setSourceModel(self.source_model)
        ui.tree.setModel(self.sort_filter_model)
        selection = ui.tree.selectionModel()
        selection.currentChanged.connect(self._on_current_changed)
        selection.selectionChanged.connect(self._on_selection_changed)
        # TODO: fix bug search of type "bl04:" still gives no result
        ui.filter_container.setVisible(False)
        add_menu = QMenu("Add")
        ui.add_string_action = add_menu.addAction("string")
        ui.add_list_action = add_menu.addAction("list")
        ui.add_set_action = add_menu.addAction("set")
        ui.add_hash_action = add_menu.addAction("hash")
        add_button = QToolButton()
        add_button.setMenu(add_menu)
        add_button.setPopupMode(QToolButton.InstantPopup)
        add_button.setIcon(QIcon.fromTheme("list-add"))
        ui.add_key_action = ui.db_toolbar.insertWidget(ui.remove_key_action,
                                                       add_button)
        ui.add_string_action.triggered.connect(
            functools.partial(self._on_add_key, "string"))
        ui.add_list_action.triggered.connect(
            functools.partial(self._on_add_key, "list"))
        ui.add_set_action.triggered.connect(
            functools.partial(self._on_add_key, "set"))
        ui.add_hash_action.triggered.connect(
            functools.partial(self._on_add_key, "hash"))

        ui.update_db_action.triggered.connect(self._on_update_db)
        ui.flush_db_action.triggered.connect(self._on_flush_db)
        ui.remove_key_action.triggered.connect(self._on_remove_key)
        ui.touch_key_action.triggered.connect(self._on_touch_key)
        ui.persist_key_action.triggered.connect(self._on_persist_key)
        ui.copy_key_action.triggered.connect(self._on_copy_key)
        ui.filter_edit.textChanged.connect(self._on_filter_changed)

    def contextMenuEvent(self, event):
        pass

    def _get_selected_keys(self):
        selection = self.ui.tree.selectionModel()
        indexes = (self.sort_filter_model.mapToSource(i)
                   for i in selection.selectedIndexes())
        nodes = (self.source_model.data(i, NodeRole) for i in indexes)
        keys = tuple(node.key for node in nodes
                     if node is not None and node.is_key())
        return keys

    def _on_filter_changed(self, text):
        if not text.endswith("*"):
            text += "*"
        self.sort_filter_model.setFilterWildcard(text)

    def _on_current_changed(self, current, previous):
        current = self.sort_filter_model.mapToSource(current)
        node = self.source_model.data(current, Qt.UserRole)
        self.currentChanged.emit(node)

    def _on_selection_changed(self, selected, deselected):
        indexes = (self.sort_filter_model.mapToSource(i)
                   for i in selected.indexes())
        nodes = (self.source_model.data(i, NodeRole) for i in indexes)
        nodes = [node for node in nodes if node is not None]
        nodes_selected = bool(nodes)
        ui = self.ui
        ui.remove_key_action.setEnabled(nodes_selected)
        ui.touch_key_action.setEnabled(nodes_selected)
        ui.persist_key_action.setEnabled(nodes_selected)
        ui.copy_key_action.setEnabled(len(nodes) == 1)

    def _on_flush_db(self):
        result = QMessageBox.question(
            self, "Danger!",
            "This action will delete all data from the current database.\n" \
            "Are you absolutely sure?")
        if result == QMessageBox.Yes:
            self.redis.flushdb()
            self.source_model.refresh()

    def _on_update_db(self):
        self.source_model.refresh()

    def _on_touch_key(self):
        keys = self._get_selected_keys()
        if keys:
            self.redis.touch(*keys)

    def _on_persist_key(self):
        keys = self._get_selected_keys()
        for key in keys:
            self.redis.persist(key)

    def _on_add_key(self, dtype):
        value = None
        if dtype == "string":
            value = ""
        elif dtype == "list":
            value = []
        elif dtype == "set":
            value = set()
        elif dtype == "hash":
            value = {}
        item = Item(self.redis, "", dtype, -1, value)
        self.addKey.emit(item)

    def _on_remove_key(self):
        keys = self._get_selected_keys()
        if keys:
            self.redis.delete(*keys)
        self.ui.tree.clearSelection()

    def _on_copy_key(self):
        keys = self._get_selected_keys()
        assert len(keys) == 1
        src = keys[0]
        dst, ok = QInputDialog.getText(self, f"Copy {src!r} to...", "New key")
        if ok:
            # redis.copy() only >= 6.2
            #self.redis.copy(src, dst)
            self.redis[dst] = self.redis[src].value
            self.source_model.refresh()
예제 #3
0
class LicenseManagerDialog(DialogBase):
    """License Manager main dialog."""

    CONTACT_LINK = 'https://support.continuum.io/'  # TODO: Centralize this?

    # Url, Sender
    sig_url_clicked = Signal(object, object)

    def __init__(self, parent=None):
        """License Manager main dialog."""
        super(LicenseManagerDialog, self).__init__(parent=parent)

        self.api = AnacondaAPI()

        # Widgets
        self.message_box = None  # For testing
        self.button_add = ButtonPrimary('Add license')
        self.button_ok = ButtonNormal('Close')
        self.button_remove = ButtonNormal('Remove license')
        self.button_contact = ButtonLink('Please contact us.')
        self.label_info = LabelBase('Manage your Continuum Analytics '
                                    'license keys.')
        self.label_contact = LabelBase('Got a problem with your license? ')
        self.proxy_model = QSortFilterProxyModel(parent=self)
        self.model = LicenseModel(parent=self)
        self.table = LicenseTableView(parent=self)
        self.delegate = BackgroundDelegate(self.table)

        # Widget setup
        self.proxy_model.setSourceModel(self.model)
        self.table.setItemDelegate(self.delegate)
        self.table.setModel(self.proxy_model)
        self.setWindowTitle('License Manager')

        # Layouts
        layout_buttons = QHBoxLayout()
        layout_buttons.addWidget(self.label_info)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_add)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_remove)

        layout_buttons_bottom = QHBoxLayout()
        layout_buttons_bottom.addWidget(self.label_contact)
        layout_buttons_bottom.addWidget(self.button_contact)
        layout_buttons_bottom.addStretch()
        layout_buttons_bottom.addWidget(self.button_ok)

        layout = QVBoxLayout()
        layout.addLayout(layout_buttons)
        layout.addWidget(SpacerVertical())
        layout.addWidget(self.table)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons_bottom)
        self.setLayout(layout)

        # Signals
        self.button_add.clicked.connect(lambda: self.add_license())
        self.button_remove.clicked.connect(self.remove_license)
        self.button_ok.clicked.connect(self.accept)
        self.button_contact.clicked.connect(
            lambda v=None: self.sig_url_clicked.emit(self.CONTACT_LINK,
                                                     'License Manager'))
        self.table.sig_dropped.connect(self.handle_drop)

        # Setup
        self.button_add.setFocus()
        self.load_licenses()

    def handle_drop(self, links):
        """Handle a drag and drop event."""
        self.api.add_license(links)
        self.load_licenses()

    def _hide_columns(self):
        """Hide columns."""
        for key, val in COL_MAP.items():
            if val in HIDDEN_COLUMNS:
                self.table.setColumnHidden(key, True)

    def add_license(self, v=None, path=None):
        """Add license file."""
        if path is None:
            filename, selected_filter = getopenfilename(
                self,
                'Select license file',
                filters='License files (*.txt)',
                basedir=get_home_dir(),
            )

            if filename:
                paths = [filename]
            else:
                paths = []
        else:
            paths = [path]

        valid_licenses, invalid_licenses = self.api.add_license(paths)

        for path in invalid_licenses:
            text = ('File: <b>"{0}"</b>'
                    '<br>is not a valid license file.').format(path)
            self.message_box = MessageBoxInformation(
                text=text, title="Invalid license file")
            self.message_box.exec_()

        if valid_licenses:
            self.load_licenses()

    def remove_license(self, row=None):
        """Remove license from file."""
        if row is None:
            index = self.table.currentIndex()
        else:
            index = self.proxy_model.index(row, 0)

        model_index = self.proxy_model.mapToSource(index)
        row_data = self.model.row(model_index.row())

        if row_data:
            text = ('Do you want to remove license for product:<br><br>'
                    '<b>{product}</b> ({issued} - {end_date})')
            text = text.format(product=row_data.get('product'),
                               end_date=row_data.get('end_date'),
                               issued=row_data.get('issued'))
            self.message_box = MessageBoxRemove(title='Remove license',
                                                text=text)
            if self.message_box.exec_():
                self.api.remove_license(row_data)
                self.load_licenses()

    def load_licenses(self):
        """Load license files."""
        res = self.api.load_licenses()
        self.model.load_licenses(res)
        self.proxy_model.setSourceModel(self.model)
        self.table.resizeColumnsToContents()
        self._hide_columns()
        self.update_status()

    def count(self):
        """Return the number of items in the table."""
        return self.table.model().rowCount()

    def update_status(self):
        """Update visible and enabled status for widgets based on actions."""
        self.button_remove.setEnabled(bool(self.count()))