示例#1
0
    def __init__(self, parent=None):
        super(OffsetTable, self).__init__(parent)

        self.setEnabled(False)

        self.offset_model = OffsetModel(self)

        # Properties
        self._columns = self.offset_model._columns
        self._confirm_actions = False
        self._current_row_color = QColor('sage')

        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(0)
        self.proxy_model.setSourceModel(self.offset_model)

        self.item_delegate = ItemDelegate(columns=self._columns)
        self.setItemDelegate(self.item_delegate)

        self.setModel(self.proxy_model)

        # Appearance/Behaviour settings
        self.setSortingEnabled(False)
        self.setAlternatingRowColors(True)
        self.setSelectionBehavior(QTableView.SelectRows)
        self.setSelectionMode(QTableView.SingleSelection)
        self.horizontalHeader().setStretchLastSection(False)
        self.horizontalHeader().setSortIndicator(0, Qt.AscendingOrder)

        STATUS.all_axes_homed.notify(self.handle_home_signal)
示例#2
0
class _PeaksWorkspaceTableView(TableWorkspaceDisplayView):
    """Specialization of a table view to display peaks
    Designed specifically to be used by PeaksViewerView
    """
    def __init__(self, *args, **kwargs):
        self._key_handler = kwargs.pop('key_handler')
        TableWorkspaceDisplayView.__init__(self, *args, **kwargs)
        self.source_model = self.model()
        self.proxy_model = None

    def keyPressEvent(self, event):
        """
        Override base to call handler as part of event
        """
        # bypass immediate base class to get standard table arrow key behaviour
        QTableView.keyPressEvent(self, event)
        self._key_handler._row_selected()

    def enable_sorting(self):
        """
        Turn on column sorting
        """
        self.setSortingEnabled(True)
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setSourceModel(self.source_model)
        self.setModel(self.proxy_model)
示例#3
0
    def __init__(self, parent=None):
        super(ToolTable, self).__init__(parent)

        self.clicked.connect(self.onClick)

        self.tool_model = ToolModel(self)

        self.item_delegate = ItemDelegate(columns=self.tool_model._columns)
        self.setItemDelegate(self.item_delegate)

        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(0)
        self.proxy_model.setSourceModel(self.tool_model)

        self.setModel(self.proxy_model)

        # Properties
        self._columns = self.tool_model._columns
        self._confirm_actions = False
        self._current_tool_color = QColor('sage')
        self._current_tool_bg = None

        # Appearance/Behaviour settings
        self.setSortingEnabled(True)
        self.verticalHeader().hide()
        self.setAlternatingRowColors(True)
        self.setSelectionBehavior(QTableView.SelectRows)
        self.setSelectionMode(QTableView.SingleSelection)
        self.horizontalHeader().setStretchLastSection(True)
        self.horizontalHeader().setSortIndicator(0, Qt.AscendingOrder)
示例#4
0
    def __init__(self, parent=None):
        super(AbstractComboListInputWidget, self).__init__(parent)
        self.main_widget = self.parent()
        self.previous_text = ''
        self.setExistsFlag(True)

        # setup line edit
        #self.line_edit = QLineEdit("Select & Focus", self)
        self.line_edit = QLineEdit(self)
        self.line_edit.editingFinished.connect(self.userFinishedEditing)
        self.setLineEdit(self.line_edit)

        self.setEditable(True)

        # setup completer
        self.completer = QCompleter(self)
        self.completer.setCompletionMode(QCompleter.PopupCompletion)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setPopup(self.view())
        self.setCompleter(self.completer)
        self.pFilterModel = QSortFilterProxyModel(self)

        # set size policy ( this will ignore the weird resizing effects)
        size_policy = QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Preferred)
        self.setSizePolicy(size_policy)

        # initialize model...
        model = QStandardItemModel()
        self.setModel(model)
        self.setModelColumn(0)
示例#5
0
 def enable_sorting(self):
     """
     Turn on column sorting
     """
     self.setSortingEnabled(True)
     self.proxy_model = QSortFilterProxyModel()
     self.proxy_model.setSourceModel(self.source_model)
     self.setModel(self.proxy_model)
示例#6
0
    def __init__(self, parent, model):
        QSortFilterProxyModel.__init__(self, parent)

        self.__show_summary_keys = True
        self.__show_block_keys = True
        self.__show_gen_kw_keys = True
        self.__show_gen_data_keys = True
        self.__show_custom_pca_keys = True
        self._metadata_filters = {}
        self.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.setSourceModel(model)
示例#7
0
    def __init__(self, device, parent=None):
        super().__init__(parent=parent)
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(-1)
        self.proxy_model.setDynamicSortFilter(True)
        self.setModel(self.proxy_model)

        self.models = {}
        self._device = None

        # Set the property last
        self.device = device
示例#8
0
    def setupCustomModelCompleter(self, item_list):
        """
        Creates a new completely custom completer

        Args:
            item_list (list): of strings to be the list of things
                that is displayed to the user
        """
        # create completer/models
        completer = CustomModelCompleter()
        self.setCompleter(completer)
        self.proxy_model = QSortFilterProxyModel()
        self._updateModel(item_list)
示例#9
0
文件: main.py 项目: pcdshub/adviewer
    def __init__(self, prefix, parent=None):
        super().__init__(parent=parent)
        self._prefix = None
        self._pvlist = None
        self._pvlist_key = None
        self.models = {}
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setDynamicSortFilter(True)
        self.proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.setModel(self.proxy_model)
        self.setSortingEnabled(True)

        # Set the property last
        self.prefix = prefix
示例#10
0
    def filterAcceptsRow(self, index, q_model_index):
        show = QSortFilterProxyModel.filterAcceptsRow(self, index, q_model_index)

        if show:
            source_model = self.sourceModel()
            source_index = source_model.index(index, 0, q_model_index)
            key = source_model.itemAt(source_index)

            if not self.__show_summary_keys and source_model.isSummaryKey(key):
                show = False

            elif not self.__show_block_keys and source_model.isBlockKey(key):
                show = False

            elif not self.__show_gen_kw_keys and source_model.isGenKWKey(key):
                show = False

            elif not self.__show_gen_data_keys and source_model.isGenDataKey(key):
                show = False

            elif not self.__show_custom_kw_keys and source_model.isCustomKwKey(key):
                show = False

            elif not self.__show_custom_pca_keys and source_model.isCustomPcaKey(key):
                show = False


        return show
示例#11
0
 def data(self, index, role):
     """Show tooltip with full path only for the root directory"""
     if role == Qt.ToolTipRole:
         root_dir = self.path_list[0].split(osp.sep)[-1]
         if index.data() == root_dir:
             return osp.join(self.root_path, root_dir)
     return QSortFilterProxyModel.data(self, index, role)
示例#12
0
 def data(self, index, role):
     """Show tooltip with full path only for the root directory."""
     if role == Qt.ToolTipRole:
         root_dir = self.path_list[0].split(osp.sep)[-1]
         if index.data() == root_dir:
             return osp.join(self.root_path, root_dir)
     return QSortFilterProxyModel.data(self, index, role)
示例#13
0
    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)
示例#14
0
    def _updateModel(self, item_list=None):
        # get item list
        if not item_list:
            item_list = self.getCleanItems()

        # completer = CustomModelCompleter()
        # self.setCompleter(completer)
        # update model items
        self._model = CustomModel(item_list=item_list)
        self._model.display_item_colors = self.display_item_colors
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setSourceModel(self._model)

        # set models
        self.completer().setModel(self.proxy_model)

        # set item for popup
        # this makes it so that the stylesheet can be attached...
        # https://forum.qt.io/topic/26703/solved-stylize-using-css-and-editable-qcombobox-s-completions-list-view/7
        delegate = QStyledItemDelegate()
        self.completer().popup().setItemDelegate(delegate)
示例#15
0
    def filterAcceptsRow(self, index, q_model_index):
        show = QSortFilterProxyModel.filterAcceptsRow(self, index,
                                                      q_model_index)

        if show:
            source_model = self.sourceModel()
            source_index = source_model.index(index, 0, q_model_index)
            key = source_model.itemAt(source_index)

            for meta_key, values in self._metadata_filters.items():
                for value, visible in values.items():
                    if not visible and meta_key in key["metadata"] and key[
                            "metadata"][meta_key] == value:
                        show = False

        return show
示例#16
0
class DeviceView(QtWidgets.QTableView):
    def __init__(self, device, parent=None):
        super().__init__(parent=parent)
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(-1)
        self.proxy_model.setDynamicSortFilter(True)
        self.setModel(self.proxy_model)

        self.models = {}
        self._device = None

        # Set the property last
        self.device = device

    def clear(self):
        for model in self.models.values():
            model.stop()
        self.models.clear()
        self._device = None

    @property
    def device(self):
        return self._device

    @device.setter
    def device(self, device):
        if device is self._device:
            return

        if self._device is not None:
            self.models[device].stop()

        self._device = device
        if device:
            try:
                model = self.models[device]
            except KeyError:
                model = PolledDeviceModel(device=device)
                self.models[device] = model

            model.start()

            self.proxy_model.setSourceModel(model)
示例#17
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()
示例#18
0
class ToolTable(QTableView):
    toolSelected = Signal(int)

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

        self.clicked.connect(self.onClick)

        self.tool_model = ToolModel(self)

        self.item_delegate = ItemDelegate(columns=self.tool_model._columns)
        self.setItemDelegate(self.item_delegate)

        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(0)
        self.proxy_model.setSourceModel(self.tool_model)

        self.setModel(self.proxy_model)

        # Properties
        self._columns = self.tool_model._columns
        self._confirm_actions = False
        self._current_tool_color = QColor('sage')
        self._current_tool_bg = None

        # Appearance/Behaviour settings
        self.setSortingEnabled(True)
        self.verticalHeader().hide()
        self.setAlternatingRowColors(True)
        self.setSelectionBehavior(QTableView.SelectRows)
        self.setSelectionMode(QTableView.SingleSelection)
        self.horizontalHeader().setStretchLastSection(True)
        self.horizontalHeader().setSortIndicator(0, Qt.AscendingOrder)

    @Slot()
    def saveToolTable(self):
        if not self.confirmAction("Do you want to save changes and\n"
                                  "load tool table into LinuxCNC?"):
            return
        self.tool_model.saveToolTable()

    @Slot()
    def loadToolTable(self):
        if not self.confirmAction("Do you want to re-load the tool table?\n"
                                  "All unsaved changes will be lost."):
            return
        self.tool_model.loadToolTable()

    @Slot()
    def deleteSelectedTool(self):
        """Delete the currently selected item"""
        current_row = self.selectedRow()
        if current_row == -1:
            # no row selected
            return

        tdata = self.tool_model.toolDataFromRow(current_row)
        tnum = tdata['T']

        # should not delete tool if currently loaded in spindle. Warn user
        if tnum == self.tool_model.stat.tool_in_spindle:

            box = QMessageBox(
                QMessageBox.Warning,
                "Can't delete current tool!",
                "Tool #{} is currently loaded in the spindle.\n"
                "Please remove tool from spindle and try again.".format(tnum),
                QMessageBox.Ok,
                parent=self)
            box.show()
            return False

        if not self.confirmAction(
                'Are you sure you want to delete T{tdata[T]}?\n'
                '"{tdata[R]}"'.format(tdata=tdata)):
            return

        self.tool_model.removeTool(current_row)

    @Slot()
    def selectPrevious(self):
        """Select the previous item in the view."""
        self.selectRow(self.selectedRow() - 1)
        return True

    @Slot()
    def selectNext(self):
        """Select the next item in the view."""
        self.selectRow(self.selectedRow() + 1)
        return True

    @Slot()
    def clearToolTable(self, confirm=True):
        """Remove all items from the model"""
        if confirm:
            if not self.confirmAction(
                    "Do you want to delete the whole tool table?"):
                return

        self.tool_model.clearToolTable()

    @Slot()
    def addTool(self):
        """Appends a new item to the model"""
        self.tool_model.addTool()
        self.selectRow(self.tool_model.rowCount() - 1)

    @Slot()
    def loadSelectedTool(self):
        """Loads the currently selected tool"""
        # see: https://forum.linuxcnc.org/41-guis/36042?start=50#151820
        current_row = self.selectedRow()
        if current_row == -1:
            # no row selected
            return

        tnum = self.tool_model.toolDataFromRow(current_row)['T']
        issue_mdi("T%s M6" % tnum)

    def selectedRow(self):
        """Returns the row number of the currently selected row, or 0"""
        tool_no = self.selectionModel().currentIndex().row()
        return tool_no

    def onClick(self, index):
        row = index.row()
        tnum = self.tool_model.toolDataFromRow(row)['T']

        self.toolSelected.emit(tnum)

    def confirmAction(self, message):
        if not self._confirm_actions:
            return True

        box = QMessageBox.question(self, 'Confirm Action', message,
                                   QMessageBox.Yes, QMessageBox.No)

        if box == QMessageBox.Yes:
            return True
        else:
            return False

    @Property(bool)
    def confirmActions(self):
        return self._confirm_actions

    @confirmActions.setter
    def confirmActions(self, confirm):
        self._confirm_actions = confirm

    @Property(QColor)
    def currentToolColor(self):
        return self.tool_model.current_tool_color

    @currentToolColor.setter
    def currentToolColor(self, color):
        self.tool_model.current_tool_color = color

    @Property(QColor)
    def currentToolBackground(self):
        return self.tool_model.current_tool_bg or QColor()

    @currentToolBackground.setter
    def currentToolBackground(self, color):
        self.tool_model.current_tool_bg = color

    def insertToolAbove(self):
        # it does not make sense to insert tools, since the numbering
        # of all the other tools would have to change.
        self.addTool()
        raise DeprecationWarning("insertToolAbove() will be removed in "
                                 "the future, use addTool() instead")

    def insertToolBelow(self):
        self.addTool()
        raise DeprecationWarning("insertToolBelow() will be removed in "
                                 "the future, use addTool() instead")
示例#19
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
示例#20
0
    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
示例#21
0
 def sourceModel(self):
     """ @rtype: DataTypeKeysListModel """
     return QSortFilterProxyModel.sourceModel(self)
示例#22
0
class NotificationWidget(QWidget, VCPWidget):
    def __init__(self, parent=None):
        super(NotificationWidget, self).__init__(parent)
        self.notification_channel = getPlugin("notifications")

        self.main_layout = QVBoxLayout()
        self.button_layout = QHBoxLayout()

        self.all_button = QPushButton()
        self.info_button = QPushButton()
        self.warn_button = QPushButton()
        self.error_button = QPushButton()
        self.debug_button = QPushButton()
        self.clear_button = QPushButton()

        self.all_button.setText("all")
        self.info_button.setText("info")
        self.warn_button.setText("warn")
        self.error_button.setText("error")
        self.debug_button.setText("debug")
        self.clear_button.setText("clear")

        self.all_button.setCheckable(True)
        self.info_button.setCheckable(True)
        self.warn_button.setCheckable(True)
        self.error_button.setCheckable(True)
        self.debug_button.setCheckable(True)

        self.all_button.setChecked(True)
        self.info_button.setChecked(False)
        self.warn_button.setChecked(False)
        self.error_button.setChecked(False)
        self.debug_button.setChecked(False)

        self.clear_button.clicked.connect(self.clear_all_notifications)

        self.button_layout.addWidget(self.all_button)
        self.button_layout.addWidget(self.info_button)
        self.button_layout.addWidget(self.warn_button)
        self.button_layout.addWidget(self.error_button)
        self.button_layout.addWidget(self.debug_button)
        self.button_layout.addWidget(self.clear_button)

        self.notification_name = QLabel()
        self.notification_name.setAlignment(Qt.AlignCenter)
        self.notification_name.setText("All Notifications")

        self.all_notification_view = QListView()

        self.all_notification_model = QStandardItemModel(
            self.all_notification_view)
        self.all_notification_model_proxy = QSortFilterProxyModel(
            self.all_notification_view)

        self.all_notification_model_proxy.setSourceModel(
            self.all_notification_model)

        # self.all_notification_view.setModel(self.all_notification_model)
        self.all_notification_view.setModel(self.all_notification_model_proxy)

        self.all_notifications = list()

        self.main_layout.addWidget(self.notification_name)
        self.main_layout.addWidget(self.all_notification_view)
        self.main_layout.addLayout(self.button_layout)

        self.setLayout(self.main_layout)

        self.notification_channel.info_message.notify(self.on_info_message)
        self.notification_channel.warn_message.notify(self.on_warn_message)
        self.notification_channel.error_message.notify(self.on_error_message)
        self.notification_channel.debug_message.notify(self.on_debug_message)

        self.all_button.clicked.connect(self.show_all_notifications)
        self.info_button.clicked.connect(self.show_info_notifications)
        self.warn_button.clicked.connect(self.show_warn_notifications)
        self.error_button.clicked.connect(self.show_error_notifications)
        self.debug_button.clicked.connect(self.show_debug_notifications)

    def on_info_message(self, message):
        timestamp = time()
        dt_object = datetime.fromtimestamp(timestamp)

        current_time = str(dt_object)

        msg = 'INFO:\nTIME {}\n  {}'.format(current_time, message)
        notification_item = QStandardItem()
        notification_item.setText(msg)
        notification_item.setIcon(QIcon.fromTheme('dialog-information'))
        notification_item.setEditable(False)
        self.all_notification_model.appendRow(notification_item)

    def on_warn_message(self, message):
        timestamp = time()
        dt_object = datetime.fromtimestamp(timestamp)

        current_time = str(dt_object)

        msg = 'WARNING:\nTIME {}\n  {}'.format(current_time, message)
        notification_item = QStandardItem()
        notification_item.setText(msg)
        notification_item.setIcon(QIcon.fromTheme('dialog-warning'))
        notification_item.setEditable(False)
        self.all_notification_model.appendRow(notification_item)

    def on_error_message(self, message):
        timestamp = time()
        dt_object = datetime.fromtimestamp(timestamp)

        current_time = str(dt_object)

        msg = 'ERROR:\nTIME {}\n  {}'.format(current_time, message)
        notification_item = QStandardItem()
        notification_item.setText(msg)
        notification_item.setIcon(QIcon.fromTheme('dialog-error'))
        notification_item.setEditable(False)
        self.all_notification_model.appendRow(notification_item)

    def on_debug_message(self, message):
        timestamp = time()
        dt_object = datetime.fromtimestamp(timestamp)

        current_time = str(dt_object)

        msg = 'DEBUG\nTIME {}\n  {}'.format(current_time, message)
        notification_item = QStandardItem()
        notification_item.setText(msg)
        notification_item.setIcon(QIcon.fromTheme('dialog-question'))
        notification_item.setEditable(False)
        self.all_notification_model.appendRow(notification_item)

    def show_all_notifications(self):
        self.all_button.setChecked(True)
        self.info_button.setChecked(False)
        self.warn_button.setChecked(False)
        self.error_button.setChecked(False)
        self.debug_button.setChecked(False)

        self.notification_name.setText("All Notifications")
        self.all_notification_model_proxy.setFilterRegExp(None)

    def show_info_notifications(self):
        self.all_button.setChecked(False)
        self.info_button.setChecked(True)
        self.warn_button.setChecked(False)
        self.error_button.setChecked(False)
        self.debug_button.setChecked(False)

        self.notification_name.setText("Information Notifications")
        self.all_notification_model_proxy.setFilterRegExp(
            QRegExp("INFO", Qt.CaseSensitive, QRegExp.FixedString))

    def show_warn_notifications(self):
        self.all_button.setChecked(False)
        self.info_button.setChecked(False)
        self.warn_button.setChecked(True)
        self.error_button.setChecked(False)
        self.debug_button.setChecked(False)

        self.notification_name.setText("Warning Notifications")
        self.all_notification_model_proxy.setFilterRegExp(
            QRegExp("WANRNING", Qt.CaseSensitive, QRegExp.FixedString))

    def show_error_notifications(self):
        self.all_button.setChecked(False)
        self.info_button.setChecked(False)
        self.warn_button.setChecked(False)
        self.error_button.setChecked(True)
        self.debug_button.setChecked(False)

        self.notification_name.setText("Error Notifications")
        self.all_notification_model_proxy.setFilterRegExp(
            QRegExp("ERROR", Qt.CaseInsensitive, QRegExp.FixedString))

    def show_debug_notifications(self):
        self.all_button.setChecked(False)
        self.info_button.setChecked(False)
        self.warn_button.setChecked(False)
        self.error_button.setChecked(False)
        self.debug_button.setChecked(True)

        self.notification_name.setText("Debug Notifications")
        self.all_notification_model_proxy.setFilterRegExp(
            QRegExp("DEBUG", Qt.CaseSensitive, QRegExp.FixedString))

    def clear_all_notifications(self):
        self.all_notification_model.clear()
示例#23
0
    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()
示例#24
0
    def __init__(self, parent=None, *args):
        QTableView.__init__(self, parent, *args)

        self.setMinimumSize(800, 600)

        # set the table model
        tm = MyTableModel(self)

        # set the proxy model
        pm = QSortFilterProxyModel(self)
        pm.setSourceModel(tm)

        self.setModel(pm)

        self.frozenTableView = QTableView(self)
        self.frozenTableView.setModel(pm)
        self.frozenTableView.verticalHeader().hide()
        self.frozenTableView.setFocusPolicy(Qt.NoFocus)
        # self.frozenTableView.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed)
        self.frozenTableView.setStyleSheet(
            '''border: none; background-color: #CCC''')
        self.frozenTableView.setSelectionModel(
            QAbstractItemView.selectionModel(self))
        self.frozenTableView.setHorizontalScrollBarPolicy(
            Qt.ScrollBarAlwaysOff)
        self.frozenTableView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        self.viewport().stackUnder(self.frozenTableView)

        self.setEditTriggers(QAbstractItemView.SelectedClicked)

        # hide grid
        self.setShowGrid(False)

        self.setStyleSheet('font: 10pt "Courier New"')

        hh = self.horizontalHeader()
        hh.setDefaultAlignment(Qt.AlignCenter)
        hh.setStretchLastSection(True)

        # self.resizeColumnsToContents()

        ncol = tm.columnCount(self)
        for col in range(ncol):
            if col == 0:
                self.horizontalHeader().resizeSection(col, 60)
                # self.horizontalHeader().setSectionResizeMode(col, QHeaderView.Fixed)
                self.frozenTableView.setColumnWidth(col, self.columnWidth(col))
            elif col == 1:
                self.horizontalHeader().resizeSection(col, 150)
                # self.horizontalHeader().setSectionResizeMode(col, QHeaderView.Fixed)
                self.frozenTableView.setColumnWidth(col, self.columnWidth(col))
            else:
                self.horizontalHeader().resizeSection(col, 100)
                self.frozenTableView.setColumnHidden(col, True)

        self.frozenTableView.setSortingEnabled(True)
        self.frozenTableView.sortByColumn(0, Qt.AscendingOrder)

        self.setAlternatingRowColors(True)

        vh = self.verticalHeader()
        vh.setDefaultSectionSize(25)
        vh.setDefaultAlignment(Qt.AlignCenter)
        vh.setVisible(True)
        self.frozenTableView.verticalHeader().setDefaultSectionSize(
            vh.defaultSectionSize())

        # nrows = tm.rowCount(self)
        # for row in range(nrows):
        #     self.setRowHeight(row, 25)

        self.frozenTableView.show()
        self.updateFrozenTableGeometry()

        self.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        self.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
        self.frozenTableView.setVerticalScrollMode(
            QAbstractItemView.ScrollPerPixel)

        # connect the headers and scrollbars of both tableviews together
        self.horizontalHeader().sectionResized.connect(self.updateSectionWidth)
        self.verticalHeader().sectionResized.connect(self.updateSectionHeight)
        self.frozenTableView.verticalScrollBar().valueChanged.connect(
            self.verticalScrollBar().setValue)
        self.verticalScrollBar().valueChanged.connect(
            self.frozenTableView.verticalScrollBar().setValue)
示例#25
0
class ToolTable(QTableView):
    def __init__(self, parent=None):
        super(ToolTable, self).__init__(parent)

        self.tool_model = ToolModel(self)

        self.item_delegate = ItemDelegate(columns=self.tool_model._columns)
        self.setItemDelegate(self.item_delegate)

        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(0)
        self.proxy_model.setSourceModel(self.tool_model)

        self.setModel(self.proxy_model)

        # Properties
        self._columns = self.tool_model._columns
        self._confirm_actions = False
        self._current_tool_color = QColor('sage')

        # Appearance/Behaviour settings
        self.setSortingEnabled(True)
        self.verticalHeader().hide()
        self.setAlternatingRowColors(True)
        self.setSelectionBehavior(QTableView.SelectRows)
        self.setSelectionMode(QTableView.SingleSelection)
        self.horizontalHeader().setStretchLastSection(True)
        self.horizontalHeader().setSortIndicator(0, Qt.AscendingOrder)

    @Slot()
    def saveToolTable(self):
        if not self.confirmAction("Do you want to save changes and\n"
                                  "load tool table into LinuxCNC?"):
            return
        self.tool_model.saveToolTable()

    @Slot()
    def loadToolTable(self):
        if not self.confirmAction("Do you want to re-load the tool table?\n"
                                  "All unsaved changes will be lost."):
            return
        self.tool_model.loadToolTable()

    @Slot()
    def deleteSelectedTool(self):
        """Delete the currently selected item"""
        current_row = self.selectedRow()
        if current_row == -1:
            # no row selected
            return

        tdata = self.tool_model.toolDataFromRow(current_row)
        if not self.confirmAction(
                "Are you sure you want to delete T{tdata[T]}?\n"
                "{tdata[R]}".format(tdata=tdata)):
            return

        self.tool_model.removeTool(current_row)

    @Slot()
    def selectPrevious(self):
        """Select the previous item in the view."""
        self.selectRow(self.selectedRow() - 1)
        return True

    @Slot()
    def selectNext(self):
        """Select the next item in the view."""
        self.selectRow(self.selectedRow() + 1)
        return True

    @Slot()
    def clearToolTable(self, confirm=True):
        """Remove all items from the model"""
        if confirm:
            if not self.confirmAction(
                    "Do you want to delete the whole tool table?"):
                return

        self.tool_model.clearToolTable()

    @Slot()
    def addTool(self):
        """Appends a new item to the model"""
        self.tool_model.addTool()
        self.selectRow(self.tool_model.rowCount() - 1)

    def selectedRow(self):
        """Returns the row number of the currently selected row, or 0"""
        return self.selectionModel().currentIndex().row()

    def confirmAction(self, message):
        if not self._confirm_actions:
            return True

        box = QMessageBox.question(self, 'Confirm Action', message,
                                   QMessageBox.Yes, QMessageBox.No)
        if box == QMessageBox.Yes:
            return True
        else:
            return False

    @Property(str)
    def displayColumns(self):
        return "".join(self._columns)

    @displayColumns.setter
    def displayColumns(self, columns):
        self._columns = [
            col for col in columns.upper() if col in 'TPXYZABCUVWDIJQR'
        ]
        self.tool_model.setColumns(self._columns)
        self.itemDelegate().setColumns(self._columns)

    @Property(bool)
    def confirmActions(self):
        return self._confirm_actions

    @confirmActions.setter
    def confirmActions(self, confirm):
        self._confirm_actions = confirm

    @Property(QColor)
    def currentToolColor(self):
        return self.tool_model.current_tool_color

    @currentToolColor.setter
    def currentToolColor(self, color):
        self.tool_model.current_tool_color = color

    def insertToolAbove(self):
        # it does not make sense to insert tools, since the numbering
        # of all the other tools would have to change.
        self.addTool()
        raise DeprecationWarning("insertToolAbove() will be removed in "
                                 "the future, use addTool() instead")

    def insertToolBelow(self):
        self.addTool()
        raise DeprecationWarning("insertToolBelow() will be removed in "
                                 "the future, use addTool() instead")
示例#26
0
class OffsetTable(QTableView):
    def __init__(self, parent=None):
        super(OffsetTable, self).__init__(parent)

        self.setEnabled(False)

        self.offset_model = OffsetModel(self)

        # Properties
        self._columns = self.offset_model._columns
        self._confirm_actions = False
        self._current_row_color = QColor('sage')

        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setFilterKeyColumn(0)
        self.proxy_model.setSourceModel(self.offset_model)

        self.item_delegate = ItemDelegate(columns=self._columns)
        self.setItemDelegate(self.item_delegate)

        self.setModel(self.proxy_model)

        # Appearance/Behaviour settings
        self.setSortingEnabled(False)
        self.setAlternatingRowColors(True)
        self.setSelectionBehavior(QTableView.SelectRows)
        self.setSelectionMode(QTableView.SingleSelection)
        self.horizontalHeader().setStretchLastSection(False)
        self.horizontalHeader().setSortIndicator(0, Qt.AscendingOrder)

        STATUS.all_axes_homed.notify(self.handle_home_signal)

    def handle_home_signal(self, all_axes):
        if all_axes:
            self.setEnabled(True)
        else:
            self.setEnabled(False)

    @Slot()
    def saveOffsetTable(self):

        if self.isEnabled():
            if not self.confirmAction("Do you want to save changes and\n"
                                      "load offset table into LinuxCNC?"):
                return
            self.offset_model.saveOffsetTable()

    @Slot()
    def loadOffsetTable(self):
        if not self.confirmAction("Do you want to re-load the offset table?\n"
                                  "All unsaved changes will be lost."):
            return
        self.offset_model.loadOffsetTable()

    @Slot()
    def deleteSelectedOffset(self):
        """Delete the currently selected item"""
        current_row = self.selectedRow()
        if current_row == -1:
            # no row selected
            return

        if not self.confirmAction("Are you sure you want to delete offset {}?".format(current_row)):
            return

        self.offset_model.clearRow(current_row)

    # @Slot()
    # def selectPrevious(self):
    #     """Select the previous item in the view."""
    #     self.selectRow(self.selectedRow() - 1)
    #     return True

    # @Slot()
    # def selectNext(self):
    #     """Select the next item in the view."""
    #     self.selectRow(self.selectedRow() + 1)
    #     return True

    @Slot()
    def clearOffsetTable(self, confirm=True):
        """Remove all items from the model"""
        if confirm:
            if not self.confirmAction("Do you want to delete the whole offsets table?"):
                return

        self.offset_model.clearRows()

    def selectedRow(self):
        """Returns the row number of the currently selected row, or 0"""
        return self.selectionModel().currentIndex().row()

    def confirmAction(self, message):
        if not self._confirm_actions:
            return True

        box = QMessageBox.question(self,
                                   'Confirm Action',
                                   message,
                                   QMessageBox.Yes,
                                   QMessageBox.No)
        if box == QMessageBox.Yes:
            return True
        else:
            return False

    @Property(str)
    def displayColumns(self):
        return "".join(self._columns)

    @displayColumns.setter
    def displayColumns(self, columns):
        self._columns = [col for col in columns.upper() if col in 'XYZABCUVWR']
        self.offset_model.setColumns(self._columns)
        self.itemDelegate().setColumns(self._columns)

    @Property(bool)
    def confirmActions(self):
        return self._confirm_actions

    @confirmActions.setter
    def confirmActions(self, confirm):
        self._confirm_actions = confirm

    @Property(QColor)
    def currentRowColor(self):
        return self.offset_model.current_row_color

    @currentRowColor.setter
    def currentRowColor(self, color):
        self.offset_model.current_row_color = color
示例#27
0
文件: main.py 项目: pcdshub/adviewer
class DetectorView(QtWidgets.QTableView):
    def __init__(self, prefix, parent=None):
        super().__init__(parent=parent)
        self._prefix = None
        self._pvlist = None
        self._pvlist_key = None
        self.models = {}
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setDynamicSortFilter(True)
        self.proxy_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.setModel(self.proxy_model)
        self.setSortingEnabled(True)

        # Set the property last
        self.prefix = prefix

    @property
    def model(self):
        if self._pvlist_key:
            return self.models.get(self._pvlist_key, None)
        return self.models.get(self._prefix, None)

    @property
    def prefix(self):
        return self._prefix

    @prefix.setter
    def prefix(self, prefix):
        if prefix == self._prefix:
            return

        logger.info('New prefix: %s', prefix)
        self._prefix = prefix
        self._pvlist = None
        self._pvlist_key = None

        if prefix:
            try:
                model = self.models[prefix]
            except KeyError:
                model = DetectorFromPrefixModel(prefix=prefix)
                self.models[prefix] = model

            self.proxy_model.setSourceModel(model)

            header = self.horizontalHeader()
            for col in range(3):
                header.setSectionResizeMode(
                    col, QtWidgets.QHeaderView.ResizeToContents)

    @property
    def pvlist(self):
        return self.model.pvlist

    @pvlist.setter
    def pvlist(self, pvlist):
        pvlist = list(sorted(set(pvlist)))
        unique_id = '/'.join(pvlist)
        pvlist_key = f'pvlist_{hash(unique_id)}'

        if pvlist_key == self._pvlist_key:
            return

        self._pvlist = pvlist
        self._pvlist_key = pvlist_key

        try:
            model = self.models[self._pvlist_key]
        except KeyError:
            model = DetectorFromPVListModel(pvlist=self._pvlist)
            self.models[self._pvlist_key] = model

        logger.info(
            'New PV list (%d PVs) - searching for %d cam PVs, %d plugin PVs',
            len(pvlist), len(model.cams.pvs), len(model.plugins.pvs))

        self._prefix = model.prefix
        self.proxy_model.setSourceModel(model)

        header = self.horizontalHeader()
        for col in range(3):
            header.setSectionResizeMode(col,
                                        QtWidgets.QHeaderView.ResizeToContents)
    def __init__(self, parent=None):
        super(NotificationsWidget, self).__init__(parent)
        self.notification_channel = getPlugin("notifications")

        self.main_layout = QVBoxLayout()
        self.button_layout = QHBoxLayout()

        self.all_button = QPushButton()
        self.info_button = QPushButton()
        self.warn_button = QPushButton()
        self.error_button = QPushButton()
        self.debug_button = QPushButton()
        self.clear_button = QPushButton()

        self.all_button.setText("all")
        self.info_button.setText("info")
        self.warn_button.setText("warn")
        self.error_button.setText("error")
        self.debug_button.setText("debug")
        self.clear_button.setText("clear")

        self.all_button.setCheckable(True)
        self.info_button.setCheckable(True)
        self.warn_button.setCheckable(True)
        self.error_button.setCheckable(True)
        self.debug_button.setCheckable(True)

        self.all_button.setChecked(True)
        self.info_button.setChecked(False)
        self.warn_button.setChecked(False)
        self.error_button.setChecked(False)
        self.debug_button.setChecked(False)

        self.clear_button.clicked.connect(self.clear_all_notifications)

        self.button_layout.addWidget(self.all_button)
        self.button_layout.addWidget(self.info_button)
        self.button_layout.addWidget(self.warn_button)
        self.button_layout.addWidget(self.error_button)
        self.button_layout.addWidget(self.debug_button)
        self.button_layout.addWidget(self.clear_button)

        self.notification_name = QLabel()
        self.notification_name.setAlignment(Qt.AlignCenter)
        self.notification_name.setText("All Notifications")

        self.all_notification_view = QListView()

        self.all_notification_model = QStandardItemModel(
            self.all_notification_view)
        self.all_notification_model_proxy = QSortFilterProxyModel(
            self.all_notification_view)

        self.all_notification_model_proxy.setSourceModel(
            self.all_notification_model)

        # self.all_notification_view.setModel(self.all_notification_model)
        self.all_notification_view.setModel(self.all_notification_model_proxy)

        self.all_notifications = list()

        self.main_layout.addWidget(self.notification_name)
        self.main_layout.addWidget(self.all_notification_view)
        self.main_layout.addLayout(self.button_layout)

        self.setLayout(self.main_layout)

        self.notification_channel.info_message.notify(self.on_info_message)
        self.notification_channel.warn_message.notify(self.on_warn_message)
        self.notification_channel.error_message.notify(self.on_error_message)
        self.notification_channel.debug_message.notify(self.on_debug_message)

        self.all_button.clicked.connect(self.show_all_notifications)
        self.info_button.clicked.connect(self.show_info_notifications)
        self.warn_button.clicked.connect(self.show_warn_notifications)
        self.error_button.clicked.connect(self.show_error_notifications)
        self.debug_button.clicked.connect(self.show_debug_notifications)
class NotificationsWidget(QWidget, VCPWidget):
    notificationsCleared = Signal(object)

    def __init__(self, parent=None):
        super(NotificationsWidget, self).__init__(parent)
        self.notification_channel = getPlugin("notifications")

        self.main_layout = QVBoxLayout()
        self.button_layout = QHBoxLayout()

        self.all_button = QPushButton()
        self.info_button = QPushButton()
        self.warn_button = QPushButton()
        self.error_button = QPushButton()
        self.debug_button = QPushButton()
        self.clear_button = QPushButton()

        self.all_button.setText("all")
        self.info_button.setText("info")
        self.warn_button.setText("warn")
        self.error_button.setText("error")
        self.debug_button.setText("debug")
        self.clear_button.setText("clear")

        self.all_button.setCheckable(True)
        self.info_button.setCheckable(True)
        self.warn_button.setCheckable(True)
        self.error_button.setCheckable(True)
        self.debug_button.setCheckable(True)

        self.all_button.setChecked(True)
        self.info_button.setChecked(False)
        self.warn_button.setChecked(False)
        self.error_button.setChecked(False)
        self.debug_button.setChecked(False)

        self.clear_button.clicked.connect(self.clear_all_notifications)

        self.button_layout.addWidget(self.all_button)
        self.button_layout.addWidget(self.info_button)
        self.button_layout.addWidget(self.warn_button)
        self.button_layout.addWidget(self.error_button)
        self.button_layout.addWidget(self.debug_button)
        self.button_layout.addWidget(self.clear_button)

        self.notification_name = QLabel()
        self.notification_name.setAlignment(Qt.AlignCenter)
        self.notification_name.setText("All Notifications")

        self.all_notification_view = QListView()

        self.all_notification_model = QStandardItemModel(
            self.all_notification_view)
        self.all_notification_model_proxy = QSortFilterProxyModel(
            self.all_notification_view)

        self.all_notification_model_proxy.setSourceModel(
            self.all_notification_model)

        # self.all_notification_view.setModel(self.all_notification_model)
        self.all_notification_view.setModel(self.all_notification_model_proxy)

        self.all_notifications = list()

        self.main_layout.addWidget(self.notification_name)
        self.main_layout.addWidget(self.all_notification_view)
        self.main_layout.addLayout(self.button_layout)

        self.setLayout(self.main_layout)

        self.notification_channel.info_message.notify(self.on_info_message)
        self.notification_channel.warn_message.notify(self.on_warn_message)
        self.notification_channel.error_message.notify(self.on_error_message)
        self.notification_channel.debug_message.notify(self.on_debug_message)

        self.all_button.clicked.connect(self.show_all_notifications)
        self.info_button.clicked.connect(self.show_info_notifications)
        self.warn_button.clicked.connect(self.show_warn_notifications)
        self.error_button.clicked.connect(self.show_error_notifications)
        self.debug_button.clicked.connect(self.show_debug_notifications)

    @staticmethod
    def _get_time():
        timestamp = time()
        dt_object = datetime.fromtimestamp(timestamp)
        return dt_object.strftime("%d / %b / %Y  -  %H:%M:%S")

    @staticmethod
    def _strip_new_line(message):
        return message.replace('\n', ' ').replace('\r', '').replace(
            '    ', ' ').replace('   ', ' ').replace('  ', ' ')

    def on_info_message(self, message):
        msg = 'INFO - occurred at: [ {} ]\n{}'.format(
            NotificationsWidget._get_time(),
            NotificationsWidget._strip_new_line(message))
        notification_item = QStandardItem()
        notification_item.setText(msg)
        notification_item.setIcon(QIcon.fromTheme('dialog-information'))
        notification_item.setEditable(False)
        self.all_notification_model.appendRow(notification_item)

    def on_warn_message(self, message):
        msg = 'WARNING - occurred at: [ {} ]\n{}'.format(
            NotificationsWidget._get_time(),
            NotificationsWidget._strip_new_line(message))
        notification_item = QStandardItem()
        notification_item.setText(msg)
        notification_item.setIcon(QIcon.fromTheme('dialog-warning'))
        notification_item.setEditable(False)
        self.all_notification_model.appendRow(notification_item)

    def on_error_message(self, message):
        msg = 'ERROR - occurred at: [ {} ]\n{}'.format(
            NotificationsWidget._get_time(),
            NotificationsWidget._strip_new_line(message))
        LOG.debug('-----on_error_message called: {}'.format(message))
        notification_item = QStandardItem()
        notification_item.setText(msg)
        notification_item.setIcon(QIcon.fromTheme('dialog-error'))
        notification_item.setEditable(False)
        self.all_notification_model.appendRow(notification_item)

    def on_debug_message(self, message):
        msg = 'DEBUG - occurred at: [ {} ]\n{}'.format(
            NotificationsWidget._get_time(),
            NotificationsWidget._strip_new_line(message))
        notification_item = QStandardItem()
        notification_item.setText(msg)
        notification_item.setIcon(QIcon.fromTheme('dialog-question'))
        notification_item.setEditable(False)
        self.all_notification_model.appendRow(notification_item)

    def show_all_notifications(self):
        self.all_button.setChecked(True)
        self.info_button.setChecked(False)
        self.warn_button.setChecked(False)
        self.error_button.setChecked(False)
        self.debug_button.setChecked(False)

        self.notification_name.setText("All Notifications")
        self.all_notification_model_proxy.setFilterRegExp(None)

    def show_info_notifications(self):
        self.all_button.setChecked(False)
        self.info_button.setChecked(True)
        self.warn_button.setChecked(False)
        self.error_button.setChecked(False)
        self.debug_button.setChecked(False)

        self.notification_name.setText("Information Notifications")
        self.all_notification_model_proxy.setFilterRegExp(
            QRegExp("INFO", Qt.CaseSensitive, QRegExp.FixedString))

    def show_warn_notifications(self):
        self.all_button.setChecked(False)
        self.info_button.setChecked(False)
        self.warn_button.setChecked(True)
        self.error_button.setChecked(False)
        self.debug_button.setChecked(False)

        self.notification_name.setText("Warning Notifications")
        self.all_notification_model_proxy.setFilterRegExp(
            QRegExp("WANRNING", Qt.CaseSensitive, QRegExp.FixedString))

    def show_error_notifications(self):
        self.all_button.setChecked(False)
        self.info_button.setChecked(False)
        self.warn_button.setChecked(False)
        self.error_button.setChecked(True)
        self.debug_button.setChecked(False)

        self.notification_name.setText("Error Notifications")
        self.all_notification_model_proxy.setFilterRegExp(
            QRegExp("ERROR", Qt.CaseInsensitive, QRegExp.FixedString))

    def show_debug_notifications(self):
        self.all_button.setChecked(False)
        self.info_button.setChecked(False)
        self.warn_button.setChecked(False)
        self.error_button.setChecked(False)
        self.debug_button.setChecked(True)

        self.notification_name.setText("Debug Notifications")
        self.all_notification_model_proxy.setFilterRegExp(
            QRegExp("DEBUG", Qt.CaseSensitive, QRegExp.FixedString))

    def clear_all_notifications(self):
        self.all_notification_model.clear()
        self.notificationsCleared.emit(self)
示例#30
0
class AbstractListInputWidget(AbstractStringInputWidget):
    """
    TODO:
        *   as soon as you type it breaks?
    Signals:
    QLineEdit
        QCompleter
            | -- QSortFilterProxyModel --> QAbstractListModel (Custom Model)
            | -- Popup
                    | -- QListView (CompleterPopup)
    Attributes:
        dynamic_update (bool): Determines if the model should be repopulated
            every time the user requests the popup to be displayed
        filter_results (bool): Determines if the returned results in the popup
            should be filtered based off of the current input.
        display_item_colors (bool): Determines if the item colors should be displayed
            or not.  By default this is set to off.  Item colors will need to be put as the
            second index for each item in the model provided to the list.
        item_list (list): string list of all of the items in the list.  Updating this
            will auto update the default settings for blank setups
        previous_text (str): the previous items text.  This is stored for cancel events
            and allowing the user to return to the previous item after cancelling.
        cleanItems (virtual function): returns a list of items to populate the
            model with.  Not sure if I still need this...
    """
    TYPE = 'list'

    def __init__(self, parent=None, item_list=[]):
        super(AbstractListInputWidget, self).__init__(parent)
        # setup default attrs
        self._item_list = []
        self._previous_text = ''
        self._dynamic_update = False
        self._filter_results = True
        self._display_item_colors = False

        # setup custom completer
        self.setupCustomModelCompleter(item_list)

        # setup style
        self.updateStyleSheet()

        # setup signals
        self.textChanged.connect(self.filterCompletionResults)

    def populate(self, item_list):
        """
        Creates all of the items from the item list and
        appends them to the model.

        item_list (list): list of strings to be displayed to the user
            this should be set with the setCleanItemsFunction.
        """
        self.setItemList(item_list)
        self.setupCustomModelCompleter(item_list)

    def __getCleanItems(self):
        return []

    def setCleanItemsFunction(self, function):
        """
        Sets the function to get the list of strings to populate the model

        function (function): function to return a list of strings to be shown
            to the user
        """
        self.__getCleanItems = function

    def getCleanItems(self):
        """
        Returns a list of strings based off of the function set with
        setCleanItemsFunction
        """
        return self.__getCleanItems()

    """ Style Sheet"""

    def updateStyleSheet(self):
        style_sheet_args = iColor.style_sheet_args
        style_sheet_args.update({'type': type(self).__name__})

        style_sheet = """
        {type}{{
            border:None;
            background-color: rgba{rgba_gray_0};
            selection-background-color: rgba{rgba_selected};
            color: rgba{rgba_text}
        }}

        """.format(**style_sheet_args)

        self.setStyleSheet(style_sheet)

    """ COMPLETER """

    def _updateModel(self, item_list=None):
        # get item list
        if not item_list:
            item_list = self.getCleanItems()

        # completer = CustomModelCompleter()
        # self.setCompleter(completer)
        # update model items
        self._model = CustomModel(item_list=item_list)
        self._model.display_item_colors = self.display_item_colors
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setSourceModel(self._model)

        # set models
        self.completer().setModel(self.proxy_model)

        # set item for popup
        # this makes it so that the stylesheet can be attached...
        # https://forum.qt.io/topic/26703/solved-stylize-using-css-and-editable-qcombobox-s-completions-list-view/7
        delegate = QStyledItemDelegate()
        self.completer().popup().setItemDelegate(delegate)

    def setupCustomModelCompleter(self, item_list):
        """
        Creates a new completely custom completer

        Args:
            item_list (list): of strings to be the list of things
                that is displayed to the user
        """
        # create completer/models
        completer = CustomModelCompleter()
        self.setCompleter(completer)
        self.proxy_model = QSortFilterProxyModel()
        self._updateModel(item_list)

    def filterCompletionResults(self):
        """
        Filter the current proxy model based off of the text in the input string
        """
        # preflight
        if not self.filter_results: return

        # filter results
        if self.text() != '':
            self.completer().setCaseSensitivity(False)
            self.completer().setCompletionMode(QCompleter.PopupCompletion)
        else:
            self.completer().setCompletionMode(
                QCompleter.UnfilteredPopupCompletion)

    """ EVENTS """

    def mouseReleaseEvent(self, event, *args, **kwargs):
        # update model (if enabled)
        if self.dynamic_update:
            self._updateModel()

        # update the current proxy model based off the current text
        self.filterCompletionResults()

        # show completer
        self.completer().complete()

        return QLineEdit.mouseReleaseEvent(self, event, *args, **kwargs)

    """ UTILS """

    def next_completion(self):
        row = self.completer().currentRow()

        # if does not exist reset
        if not self.completer().setCurrentRow(row + 1):
            self.completer().setCurrentRow(0)

        # if initializing
        if self.completer().popup().currentIndex().row() == -1:
            self.completer().setCurrentRow(0)

        index = self.completer().currentIndex()
        self.completer().popup().setCurrentIndex(index)

    def previous_completion(self):
        row = self.completer().currentRow()
        numRows = self.completer().completionCount()

        # if wrapping
        if not self.completer().setCurrentRow(row - 1):
            self.completer().setCurrentRow(numRows - 1)
        # if initializing
        if self.completer().popup().currentIndex().row() == -1:
            self.completer().setCurrentRow(numRows - 1)

        index = self.completer().currentIndex()
        self.completer().popup().setCurrentIndex(index)

    def event(self, event, *args, **kwargs):
        if (event.type() == QEvent.KeyPress) and (event.key() == Qt.Key_Tab):
            if self.text() == '':
                self.completer().complete()
            else:
                self.next_completion()
            return True

        if (event.type() == QEvent.KeyPress) and (event.key() == 16777218):
            self.previous_completion()
            return True

        return AbstractStringInputWidget.event(self, event, *args, **kwargs)

    """ PROPERTIES """

    @property
    def dynamic_update(self):
        return self._dynamic_update

    @dynamic_update.setter
    def dynamic_update(self, dynamic_update):
        self._dynamic_update = dynamic_update

    @property
    def filter_results(self):
        return self._filter_results

    @filter_results.setter
    def filter_results(self, filter_results):
        self._filter_results = filter_results

    @property
    def display_item_colors(self):
        return self._display_item_colors

    @display_item_colors.setter
    def display_item_colors(self, display_item_colors):
        self._display_item_colors = display_item_colors
        self.model().display_item_colors = display_item_colors

    def getItemList(self):
        return self._item_list

    def setItemList(self, item_list):
        # if self.previous_text == '':
        #     item_list.insert(0, '')
        self._item_list = item_list

    @property
    def previous_text(self):
        return self._previous_text

    @previous_text.setter
    def previous_text(self, previous_text):
        self._previous_text = previous_text

    def model(self):
        return self._model

    def setModel(self, _model):
        self._model = _model
示例#31
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()))