Установить выбор из списка индексов в QTreeView

Есть ли в PySide способ выбрать несколько сотен элементов дерева без необходимости вручную переходить по строке и выбирать их? Проблема с этим методом заключается в том, что пользовательский интерфейс обновляется каждый раз, когда выбирается новая строка, что приводит к зависанию приложения, пока оно завершает метод. Есть ли способ, которым я могу просто передать модели выбора список всех строк, которые я хочу выбрать?

В моем древовидном представлении сотни строк и четыре столбца, но древовидное представление настроено на выбор целых строк, а не ячеек.

model = self.uiFilesList.model()
rows = self.selectedFileItems.selectedRows()
self.uiFilesList.selectionModel().clear()

Я ожидал бы, что это сработает, но это не так.

selection = self.uiFilesList.selectionModel().selection()
self.uiFilesList.selectionModel().clear()
mode = QtGui.QItemSelectionModel.Select | QtGui.QItemSelectionModel.Rows
self.uiFilesList.selectionModel().select(selection, mode)

Вот мой пример проекта, в котором выделение не обновляется после обновления данных в режиме. В приведенном ниже примере вы увидите, что когда вы щелкаете правой кнопкой мыши и меняете возраст или номер футболки, список необходимо заполнить заново, чтобы обновить отображаемые данные. Однако я пытаюсь сохранить выбор перед обновлением списка. Затем я пытаюсь восстановить его после повторного заполнения списка, но он не работает.

import sys, os, random
from PySide import QtGui, QtCore

class Person(object):

    def __init__(self, name, age, hair, jersey):
        self.name = name
        self.age = age
        self.hair = hair
        self.jersey = jersey


class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.resize(500, 320)

        self.people = [
            Person('Kevin', 10, 'Brown', 20),
            Person('Marsha', 32, 'Blonde', 00),
            Person('Leslie', 27, 'Black', 15),
            Person('Tim', 53, 'Red', 37),
            Person('Marie', 65, 'Brown', 101),
            Person('Bella', 8, 'Blonde', 1)
        ]

        self.treeview = QtGui.QTreeView()
        self.treeview.setAlternatingRowColors(True)
        self.treeview.setModel(QtGui.QStandardItemModel())
        self.treeview.setSortingEnabled(True)
        self.treeview.setRootIsDecorated(False)
        self.treeview.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.treeview.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
        self.treeview.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        self.treeview.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.treeview.customContextMenuRequested.connect(self.open_menu)
        self.selectedItems = self.treeview.selectionModel()
        self.setCentralWidget(self.treeview)
        self.populate_list()

        # actions
        self.actRandomizeAges = QtGui.QAction('Randomize Ages', self)
        self.actRandomizeJerseys = QtGui.QAction('Randomize Jersey Numbers', self)

        # menu
        self.cmenu = QtGui.QMenu()
        self.cmenu.addAction(self.actRandomizeAges)
        self.cmenu.addAction(self.actRandomizeJerseys)

        # connections
        self.actRandomizeAges.triggered.connect(self.randomize_ages)
        self.actRandomizeJerseys.triggered.connect(self.randomize_jerseys)

    def open_menu(self, position):
        self.cmenu.exec_(self.treeview.viewport().mapToGlobal(position))


    def randomize_ages(self):
        rows = self.selectedItems.selectedRows()
        for i, item in enumerate(rows):
            obj = item.data(role=QtCore.Qt.UserRole)
            obj.age = random.randint(0, 70)
        self.populate_list()


    def randomize_jerseys(self):
        rows = self.selectedItems.selectedRows()
        for i, item in enumerate(rows):
            obj = item.data(role=QtCore.Qt.UserRole)
            obj.jersey = random.randint(1, 100)
        self.populate_list()


    def populate_list(self):
        selection = self.treeview.selectionModel().selection()
        flags = QtGui.QItemSelectionModel.Select
        self.treeview.selectionModel().clear()

        model = self.treeview.model()
        model.clear()
        model.setHorizontalHeaderLabels(['Name','Age','Hair', 'Jersey'])

        for p in self.people:
            # column 1
            col1 = QtGui.QStandardItem()
            col1.setData(p.name, role=QtCore.Qt.DisplayRole)
            col1.setData(p, role=QtCore.Qt.UserRole)
            # column 2
            col2 = QtGui.QStandardItem()
            col2.setData(p.age, role=QtCore.Qt.DisplayRole)
            if p.age > 30:
                col2.setData(QtGui.QBrush(QtGui.QColor(255,0,0,255)), role=QtCore.Qt.ForegroundRole)
            # column 3
            col3 = QtGui.QStandardItem()
            col3.setData(p.hair, role=QtCore.Qt.DisplayRole)
            # column 4
            col4 = QtGui.QStandardItem()
            col4.setData(p.jersey, role=QtCore.Qt.DisplayRole)
            if p.jersey > 30:
                col4.setData(QtGui.QBrush(QtGui.QColor(0,0,255,255)), role=QtCore.Qt.ForegroundRole)

            model.appendRow([col1, col2, col3, col4])

        self.treeview.selectionModel().select(selection, flags)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    ex = MainWindow()
    ex.show()
    sys.exit(app.exec_())

person JokerMartini    schedule 13.11.2017    source источник


Ответы (1)


Ваш пример не работает, потому что он использует неправильные флаги выбора. Если вы используете только QItemSelectionModel.Select, он будет работать правильно (и будет выбирать целые строки).

Чтобы установить выбор из списка индексов, вы можете создать серию QItemSelection объектов, которые охватывают смежные диапазоны, и объединить их все в один выбор.

Вот демонстрационный скрипт, который показывает, как это сделать:

import sys
from PySide import QtCore, QtGui

class Window(QtGui.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.button = QtGui.QPushButton('Select')
        self.button.clicked.connect(self.handleButton)
        self.tree = QtGui.QTreeView()
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.tree)
        layout.addWidget(self.button)
        columns = 'One Two Three Four'.split()
        mod = QtGui.QStandardItemModel(self)
        mod.setHorizontalHeaderLabels(columns)
        for row in range(1000):
            mod.appendRow((
                QtGui.QStandardItem('A%s' % row),
                QtGui.QStandardItem('B%s' % row),
                QtGui.QStandardItem('C%s' % row),
                QtGui.QStandardItem('D%s' % row),
                ))
        self.tree.setModel(mod)
        self.tree.setSelectionMode(
            QtGui.QAbstractItemView.ExtendedSelection)

    def handleButton(self):
        mod = self.tree.model()
        columns = mod.columnCount() - 1
        flags = QtGui.QItemSelectionModel.Select
        selection = QtGui.QItemSelection()
        for start, end in ((2, 15), (25, 260), (500, 996)):
            start, end = mod.index(start, 0), mod.index(end, columns)
            if selection.indexes():
                selection.merge(QtGui.QItemSelection(start, end), flags)
            else:
                selection.select(start, end)
        self.tree.selectionModel().clear()
        self.tree.selectionModel().select(selection, flags)

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.setGeometry(700, 50, 500, 600)
    window.show()
    sys.exit(app.exec_())

ОБНОВЛЕНИЕ:

Пример в вашем вопросе не работает, потому что ваш метод populate_list очищает модель, что делает недействительными все индексы в выборе. Поэтому вам нужен метод для сохранения текущего выбора в виде списка номеров строк (а не индексов модели). Затем это можно передать методу, который я дал выше, чтобы воссоздать выделение.

Если вы обновите свой пример следующим образом, он должен работать так, как ожидалось:

def save_selection(self):
    selection = self.treeview.selectionModel().selectedRows()
    blocks = []
    for count, index in enumerate(sorted(selection)):
        row = index.row()
        if count > 0 and row == block[1] + 1:
            block[1] = row
        else:
            block = [row, row]
            blocks.append(block)
    return blocks

def create_selection(self, blocks):
    mod = self.treeview.model()
    columns = mod.columnCount() - 1
    flags = QtGui.QItemSelectionModel.Select
    selection = QtGui.QItemSelection()
    for start, end in blocks:
        start, end = mod.index(start, 0), mod.index(end, columns)
        if selection.indexes():
            selection.merge(QtGui.QItemSelection(start, end), flags)
        else:
            selection.select(start, end)
    self.treeview.selectionModel().clear()
    self.treeview.selectionModel().select(selection, flags)

def populate_list(self):
    selection = self.save_selection()

    ... # re-populate model

    self.create_selection(selection)
person ekhumoro    schedule 13.11.2017
comment
Я попробовал то, что вы сказали, с моим примером кода, и это не сработало. - person JokerMartini; 14.11.2017
comment
Я обновил свой исходный вопрос своим примером, в котором выбор не обновляется. - person JokerMartini; 14.11.2017
comment
@ДжокерМартини. Я добавил дополнительный код, который решает проблему. - person ekhumoro; 14.11.2017
comment
Большое спасибо за вашу помощь, я ценю это. теперь работает :) - person JokerMartini; 14.11.2017