Установить выбор из списка индексов в 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_())
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)