2009-05-08 31 views
12

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?

Trả lời

3

thử gọi setUniformRowHeights(true) để xem cây của bạn:

Ngoài ra, có một công cụ ++ C gọi modeltest từ phòng thí nghiệm qt. Tôi không chắc chắn nếu có cái gì đó cho python mặc dù:

https://wiki.qt.io/Model_Test

+0

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? –

0

tôi chuyển mã ví dụ rất tốt đẹp của bạn để PyQt5 và chạy dưới Qt5.2 và có thể khẳng định rằng con số này vẫn tương tự, tức là hiểu sao khổng lồ số lượng cuộc gọi. Ví dụ ở đây là phần trên cùng của báo cáo để bắt đầu, cmd-A để chọn tất cả, di chuyển một trang, thoát:

 ncalls tottime percall cumtime percall filename:lineno(function) 
     1 14.880 14.880 15.669 15.669 {built-in method exec_} 
    196712 0.542 0.000 0.703 0.000 /Users/dcortes1/Desktop/scratch/treeview.py:36(parent) 
    185296 0.104 0.000 0.104 0.000 {built-in method createIndex} 
    20910 0.050 0.000 0.056 0.000 /Users/dcortes1/Desktop/scratch/treeview.py:54(data) 
    225252 0.036 0.000 0.036 0.000 {built-in method isValid} 
    224110 0.034 0.000 0.034 0.000 {built-in method internalPointer} 
    7110 0.020 0.000 0.027 0.000 /Users/dcortes1/Desktop/scratch/treeview.py:28(index)
Và trong khi số đếm thực sự quá mức (và tôi không giải thích), hãy chú ý các giá trị cumtime không to quá. Ngoài ra những chức năng có thể được recoded để chạy nhanh hơn; ví dụ trong index(), là "if not self.nodes" có đúng không? Tương tự, lưu ý số đếm cho parent() và createIndex() gần như giống nhau, do đó index.isValid() là đúng hơn thường xuyên hơn không (hợp lý, vì các nút cuối nhiều hơn nhiều nút cha). Việc giải mã để xử lý trường hợp đó trước tiên sẽ cắt giảm thêm thời gian cha mẹ(). Chỉnh sửa: trên suy nghĩ thứ hai, tối ưu hóa như vậy là "sắp xếp lại các ghế xếp trên titanic".

Các vấn đề liên quan