Gần đây tôi đã đánh một bức tường trong một dự án mà tôi đang sử dụng PyQt. Tôi có một QTreeView nối với một QAbstractItemModel mà thường có hàng ngàn nút trong đó. Cho đến nay, nó hoạt động ổn, nhưng hôm nay tôi nhận ra rằng việc chọn rất nhiều nút rất chậm. Sau khi một số đào, nó chỉ ra rằng QAbstractItemModel.parent() được gọi là cách quá thường xuyên. Tôi tạo ra mã số tối thiểu để tạo lại vấn đề:Lựa chọn chậm trong QTreeView, tại sao?
#!/usr/bin/env python
import sys
import cProfile
import pstats
from PyQt4.QtCore import Qt, QAbstractItemModel, QVariant, QModelIndex
from PyQt4.QtGui import QApplication, QTreeView
# 200 root nodes with 10 subnodes each
class TreeNode(object):
def __init__(self, parent, row, text):
self.parent = parent
self.row = row
self.text = text
if parent is None: # root node, create subnodes
self.children = [TreeNode(self, i, unicode(i)) for i in range(10)]
else:
self.children = []
class TreeModel(QAbstractItemModel):
def __init__(self):
QAbstractItemModel.__init__(self)
self.nodes = [TreeNode(None, i, unicode(i)) for i in range(200)]
def index(self, row, column, parent):
if not self.nodes:
return QModelIndex()
if not parent.isValid():
return self.createIndex(row, column, self.nodes[row])
node = parent.internalPointer()
return self.createIndex(row, column, node.children[row])
def parent(self, index):
if not index.isValid():
return QModelIndex()
node = index.internalPointer()
if node.parent is None:
return QModelIndex()
else:
return self.createIndex(node.parent.row, 0, node.parent)
def columnCount(self, parent):
return 1
def rowCount(self, parent):
if not parent.isValid():
return len(self.nodes)
node = parent.internalPointer()
return len(node.children)
def data(self, index, role):
if not index.isValid():
return QVariant()
node = index.internalPointer()
if role == Qt.DisplayRole:
return QVariant(node.text)
return QVariant()
app = QApplication(sys.argv)
treemodel = TreeModel()
treeview = QTreeView()
treeview.setSelectionMode(QTreeView.ExtendedSelection)
treeview.setSelectionBehavior(QTreeView.SelectRows)
treeview.setModel(treemodel)
treeview.expandAll()
treeview.show()
cProfile.run('app.exec_()', 'profdata')
p = pstats.Stats('profdata')
p.sort_stats('time').print_stats()
Để tạo lại vấn đề, chỉ cần chạy mã (mà không profiling) và chọn tất cả các nút trong các phụ tùng cây (hoặc thông qua lựa chọn thay đổi hoặc Cmd-A). Khi bạn thoát khỏi ứng dụng, số liệu thống kê tiểu sử sẽ hiển thị một cái gì đó như:
Fri May 8 20:04:26 2009 profdata
628377 function calls in 6.210 CPU seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 4.788 4.788 6.210 6.210 {built-in method exec_}
136585 0.861 0.000 1.182 0.000 /Users/hsoft/Desktop/slow_selection.py:34(parent)
142123 0.217 0.000 0.217 0.000 {built-in method createIndex}
17519 0.148 0.000 0.164 0.000 /Users/hsoft/Desktop/slow_selection.py:52(data)
162198 0.094 0.000 0.094 0.000 {built-in method isValid}
8000 0.055 0.000 0.076 0.000 /Users/hsoft/Desktop/slow_selection.py:26(index)
161357 0.047 0.000 0.047 0.000 {built-in method internalPointer}
94 0.000 0.000 0.000 0.000 /Users/hsoft/Desktop/slow_selection.py:46(rowCount)
404 0.000 0.000 0.000 0.000 /Users/hsoft/Desktop/slow_selection.py:43(columnCount)
94 0.000 0.000 0.000 0.000 {len}
1 0.000 0.000 6.210 6.210 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Phần lạ thường được gọi là: 136k lần cho các nút 2k! Bất cứ ai cũng có một đầu mối tại sao?
Cảm ơn gợi ý, nhưng thật không may, nó không giúp ích gì. Nó đã làm giảm số lượng các cuộc gọi cha mẹ, nhưng chỉ để gọi 134k. Đối với Modeltest, nó có vẻ thú vị, nhưng tôi không biết làm thế nào để nhập khẩu các thành phần bên thứ 3 C + + trong PyQt (tôi sẽ phải google nó lên). Nhưng trong mọi trường hợp, có vẻ như với tôi rằng mô hình này là chính xác, phải không? –