python - 从 QTreeView 中的索引列表中设置选择

标签 python selection pyside qtreeview

在 PySide 中,有没有一种方法可以选择数百个 TreeView 项,而无需手动逐行选择它们?此方法的问题是每次选择新行时 UI 都会更新,导致应用程序在完成该方法时卡住。有没有办法可以将我想要选择的所有行的列表传递给选择模型?

我的 TreeView 有数百行和四列,但 TreeView 设置为选择整行,而不是单元格。

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)

这是我的示例项目,在我更新模式中的数据后,选择没有更新。您将在下面的示例中看到,当您右键单击并更改年龄或 Jersey 号码时,必须重新填充列表才能更新显示的数据。不过,我尝试在更新列表之前存储选择。然后我尝试在重新填充列表后恢复它,但它似乎不起作用。

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_())

最佳答案

您的示例不起作用,因为它使用了错误的 selection-flags 。如果您单独使用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)

关于python - 从 QTreeView 中的索引列表中设置选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47271494/

相关文章:

python - Pyside PyQt,如何将 Key_Delete 连接到 4 个不同的 QListWidget

python - gTTS 可以保存哪些类型的文件?

Python:数组值以某种方式被改变

python - 在线程类中与 __init__ 相反?

java - 在Java中如何比较两个链表节点?

c - 打印菜单功能选择后继续打印

python - OpenCV无法从视频读取图像帧

python - 为什么 super().__init__ 没有自引用?

c# - 在 WinForms ListView C# 中删除突出显示并添加选择边框

python - 从小部件调用主窗口中的函数 (PySide)