Пример #1
0
class CheckList(QWidget):
    def __init__(self, model, label="", help_link="", custom_filter_button=None):
        """
        :param custom_filter_button:  if needed, add a button that opens a
        custom filter menu. Useful when search alone isn't enough to filter the
        list.
        :type custom_filter_button: QToolButton
        """
        QWidget.__init__(self)

        self._model = model

        if help_link != "":
            addHelpToWidget(self, help_link)

        layout = QVBoxLayout()

        self._createCheckButtons()

        self._list = QListWidget()
        self._list.setContextMenuPolicy(Qt.CustomContextMenu)
        self._list.setSelectionMode(QAbstractItemView.ExtendedSelection)

        self._search_box = SearchBox()

        check_button_layout = QHBoxLayout()

        check_button_layout.setContentsMargins(0, 0, 0, 0)
        check_button_layout.setSpacing(0)
        check_button_layout.addWidget(QLabel(label))
        check_button_layout.addStretch(1)
        check_button_layout.addWidget(self._checkAllButton)
        check_button_layout.addWidget(self._uncheckAllButton)

        layout.addLayout(check_button_layout)
        layout.addWidget(self._list)

        """
        Inserts the custom filter button, if provided. The caller is responsible
        for all related actions.
        """
        if custom_filter_button is not None:
            search_bar_layout = QHBoxLayout()
            search_bar_layout.addWidget(self._search_box)
            search_bar_layout.addWidget(custom_filter_button)
            layout.addLayout(search_bar_layout)
        else:
            layout.addWidget(self._search_box)

        self.setLayout(layout)

        self._checkAllButton.clicked.connect(self.checkAll)
        self._uncheckAllButton.clicked.connect(self.uncheckAll)
        self._list.itemChanged.connect(self.itemChanged)
        self._search_box.filterChanged.connect(self.filterList)
        self._list.customContextMenuRequested.connect(self.showContextMenu)

        self._model.selectionChanged.connect(self.modelChanged)
        self._model.modelChanged.connect(self.modelChanged)

        self.modelChanged()

    def _createCheckButtons(self):
        self._checkAllButton = QToolButton()
        self._checkAllButton.setIcon(resourceIcon("check.svg"))
        self._checkAllButton.setIconSize(QSize(16, 16))
        self._checkAllButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self._checkAllButton.setAutoRaise(True)
        self._checkAllButton.setToolTip("Select all")
        self._uncheckAllButton = QToolButton()
        self._uncheckAllButton.setIcon(resourceIcon("checkbox_outline.svg"))
        self._uncheckAllButton.setIconSize(QSize(16, 16))
        self._uncheckAllButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self._uncheckAllButton.setAutoRaise(True)
        self._uncheckAllButton.setToolTip("Unselect all")

    def itemChanged(self, item):
        """@type item: QListWidgetItem"""
        if item.checkState() == Qt.Checked:
            self._model.selectValue(str(item.text()))
        elif item.checkState() == Qt.Unchecked:
            self._model.unselectValue(str(item.text()))
        else:
            raise AssertionError("Unhandled checkstate!")

    def modelChanged(self):
        self._list.clear()

        items = self._model.getList()

        for item in items:
            list_item = QListWidgetItem(item)
            list_item.setFlags(list_item.flags() | Qt.ItemIsUserCheckable)

            if self._model.isValueSelected(item):
                list_item.setCheckState(Qt.Checked)
            else:
                list_item.setCheckState(Qt.Unchecked)

            self._list.addItem(list_item)

        self.filterList(self._search_box.filter())

    def setSelectionEnabled(self, enabled):
        self.setEnabled(enabled)
        self._checkAllButton.setEnabled(enabled)
        self._uncheckAllButton.setEnabled(enabled)

    def filterList(self, filter):
        filter = filter.lower()

        for index in range(0, self._list.count()):
            item = self._list.item(index)
            text = str(item.text()).lower()

            if filter == "":
                item.setHidden(False)
            elif filter in text:
                item.setHidden(False)
            else:
                item.setHidden(True)

    def checkAll(self):
        """
        Checks all visible items in the list.
        """
        for index in range(0, self._list.count()):
            item = self._list.item(index)
            if not item.isHidden():
                self._model.selectValue(str(item.text()))

    def uncheckAll(self):
        """
        Unchecks all items in the list, visible or not
        """
        self._model.unselectAll()

    def checkSelected(self):
        items = []
        for item in self._list.selectedItems():
            items.append(str(item.text()))

        for item in items:
            self._model.selectValue(item)

    def uncheckSelected(self):
        items = []
        for item in self._list.selectedItems():
            items.append(str(item.text()))

        for item in items:
            self._model.unselectValue(item)

    def showContextMenu(self, point):
        p = self._list.mapToGlobal(point)
        menu = QMenu()
        check_selected = menu.addAction("Check selected")
        uncheck_selected = menu.addAction("Uncheck selected")
        menu.addSeparator()
        clear_selection = menu.addAction("Clear selection")

        selected_item = menu.exec_(p)

        if selected_item == check_selected:
            self.checkSelected()
        elif selected_item == uncheck_selected:
            self.uncheckSelected()
        elif selected_item == clear_selection:
            self._list.clearSelection()
Пример #2
0
class HeaderEditDialog(QDialog):

    # name of the current preset; whether to set this preset as default; list of Columns
    header_changed = Signal(str, bool, list)

    def __init__(self, parent, table_header):
        super().__init__(parent)

        self.table_header = table_header
        self.default_preset_name = None
        self.preset_name = table_header.preset_name
        self.columns = deepcopy(table_header.columns)
        self.setupUi()
        self.update_output()

    def setupUi(self):
        self.resize(240, 400)
        self.vbox = QVBoxLayout(self)
        self.presetLabel = QLabel(self)
        self.columnList = QListWidget(self)
        self.setAsDefaultCheckbox = QCheckBox("Set as default preset", self)
        self.vbox.addWidget(self.presetLabel)
        self.vbox.addWidget(self.columnList)
        self.vbox.addWidget(self.setAsDefaultCheckbox)

        self.columnList.setDragDropMode(QListWidget.InternalMove)
        self.columnList.setDefaultDropAction(Qt.MoveAction)
        self.columnList.setSelectionMode(QListWidget.ExtendedSelection)
        self.columnList.setAlternatingRowColors(True)
        self.columnList.installEventFilter(self)
        self.columnList.setContextMenuPolicy(Qt.CustomContextMenu)
        self.columnList.customContextMenuRequested.connect(self.open_menu)
        self.columnList.model().rowsMoved.connect(self.read_columns_from_list)

        # for a dumb qss hack to make selected checkboxes not white on a light theme
        self.columnList.setObjectName("ColumnList")

        buttons = QDialogButtonBox.Reset | QDialogButtonBox.Save | QDialogButtonBox.Cancel
        self.buttonBox = QDialogButtonBox(buttons, self)
        self.vbox.addWidget(self.buttonBox)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)
        self.resetButton = self.buttonBox.button(QDialogButtonBox.Reset)
        self.resetButton.clicked.connect(self.reset_to_stock)

    def eventFilter(self, object, event):
        if event.type() == QEvent.KeyPress:
            if event.key() == Qt.Key_Space or event.key() == Qt.Key_Return:
                self.toggle_selected_columns()
                return True
            if event.key() == Qt.Key_Delete:
                self.delete_selected()
                return True
        return False

    def update_output(self):
        self.presetLabel.setText("Preset: {}".format(self.preset_name))
        self.setAsDefaultCheckbox.setChecked(
            CONFIG['default_header_preset'] == self.preset_name)
        self.columnList.clear()
        for column in self.columns:
            ColumnListItem(self.columnList, column)

    def accept(self):
        self.read_columns_from_list()
        self.header_changed.emit(self.preset_name,
                                 self.setAsDefaultCheckbox.isChecked(),
                                 self.columns)
        self.done(0)

    def reject(self):
        self.done(0)

    def reset_to_stock(self):
        self.columns = deepcopy(DEFAULT_COLUMNS)
        self.update_output()

    def read_columns_from_list(self):
        new_columns = []
        for i in range(self.columnList.count()):
            item = self.columnList.item(i)
            new_columns.append(item.column)
        self.columns = new_columns

    def toggle_selected_columns(self):
        selected = self.columnList.selectedItems()
        for item in selected:
            value_now = item.data(Qt.CheckStateRole)
            item.setData(Qt.CheckStateRole, not value_now)
        self.columnList.reset(
        )  # @Improvement: is there a better way to update QListWidget?

    def open_menu(self, position):
        menu = QMenu(self)

        preset_menu = menu.addMenu('Presets')
        preset_menu.addAction('New preset', self.new_preset_dialog)
        preset_menu.addSeparator()

        preset_names = CONFIG.get_header_presets()

        if len(preset_names) == 0:
            action = preset_menu.addAction('No presets')
            action.setEnabled(False)
        else:
            delete_menu = menu.addMenu('Delete preset')
            for name in preset_names:
                preset_menu.addAction(name, partial(self.load_preset, name))
                delete_menu.addAction(name, partial(self.delete_preset, name))

        menu.addSeparator()
        menu.addAction('New column...', self.create_new_column_dialog)

        if len(self.columnList.selectedIndexes()) > 0:
            menu.addAction('Delete selected', self.delete_selected)

        menu.popup(self.columnList.viewport().mapToGlobal(position))

    def load_preset(self, name):
        new_columns = CONFIG.load_header_preset(name)
        if not new_columns:
            return

        self.columns = new_columns
        self.preset_name = name
        self.update_output()

    def new_preset_dialog(self):
        d = QInputDialog(self)
        d.setLabelText('Enter the new name for the new preset:')
        d.setWindowTitle('Create new preset')
        d.textValueSelected.connect(self.create_new_preset)
        d.open()

    def create_new_preset(self, name):
        if name in CONFIG.get_header_presets():
            show_warning_dialog(
                self, "Preset creation error",
                'Preset named "{}" already exists.'.format(name))
            return
        if len(name.strip()) == 0:
            show_warning_dialog(
                self, "Preset creation error",
                'This preset name is not allowed.'.format(name))
            return

        self.preset_name = name
        self.update_output()
        CONFIG.save_header_preset(name, self.columns)

    def delete_preset(self, name):
        CONFIG.delete_header_preset(name)
        if name == self.preset_name:
            self.columns = deepcopy(DEFAULT_COLUMNS)
            self.update_output()

    def create_new_column_dialog(self):
        d = CreateNewColumnDialog(self)
        d.add_new_column.connect(self.add_new_column)
        d.setWindowTitle('Create new column')
        d.open()

    def add_new_column(self, name, title):
        new_column = Column(name, title)
        # if the last column is message, insert this column before it (I think that makes sense?)
        if len(self.columns) == 0:
            self.columns.append(new_column)
        elif self.columns[-1].name in ('message', 'msg'):
            self.columns.insert(-1, new_column)
        else:
            self.columns.append(new_column)
        self.update_output()

    def delete_selected(self):
        selected = self.columnList.selectedItems()
        for item in selected:
            self.columnList.takeItem(self.columnList.row(item))
        self.read_columns_from_list()
        self.update_output()