2009-05-08 4 views
12

저는 최근 PyQt를 사용하는 프로젝트에서 벽을 때렸습니다. QTreeView QAbstractItemModel 일반적으로 수천 개의 노드가 있습니다. 지금까지는 문제가 없었지만 많은 노드를 선택하는 것이 매우 느리다는 것을 깨달았습니다. 파고가 끝나면 QAbstractItemModel.parent()가 너무 자주 호출되는 것으로 나타났습니다.QTreeView에서 선택이 느린 이유는 무엇입니까?

#!/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() 

그냥 코드를 실행, 문제를 재현하려면 (프로파일 않음) 및 (중 시프트 선택 또는 Cmd를-A를 통해) 트리 위젯의 모든 노드를 선택 : 나는 문제를 재현하기 위해 최소한의 코드를 만들었습니다. 당신이 응용 프로그램을 종료하면, 프로파일 링 통계 같은 표시됩니다 :

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} 

이 데이터에 이상한 부분이라고 얼마나 자주 부모()이다 : 136k 번 2K 노드! 누구나 단서가 있습니까? 당신의 트리 뷰에 대한 setUniformRowHeights(true)를 호출

답변

3

봅니다 :

https://doc.qt.io/qt-4.8/qtreeview.html#uniformRowHeights-prop

또한, C++ 도구는 Qt는 실험실에서 modeltest라는있다. 내가 PyQt5에 아주 좋은 예제 코드를 변환 Qt5.2에서 실행하고 숫자가 여전히 알수없는 거대한 즉, 유사하다는 것을 확인할 수 있습니다

https://wiki.qt.io/Model_Test

+0

힌트를 보내 주셔서 감사합니다.하지만 불행히도 도움이되지 않았습니다. 그것은 부모 호출의 수를 줄 였지만 단지 134k 호출까지 줄였습니다. Modeltest에 관해서는 재미 있지만, PyQt에서 타사 C++ 구성 요소를 가져 오는 방법을 알지 못합니다. 그러나 어쨌든이 모델이 맞았다는 생각이 듭니다. –

0

:하지만 파이썬에 대한 뭔가가 있는지 확실하지 않습니다 전화 번호. 여기에 예를 들어, 시작에 대한 보고서의 상단 부분, 모두 선택, 한 페이지를 스크롤, 종료 :

 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)
그리고 카운트가 실제로 과도한 (그리고 나는 설명이 없음) 동안, cumtime 값이 아닙니다 통지 그래서 큰. 또한 이러한 기능은 더 빠르게 실행되도록 레코딩 될 수 있습니다. 예를 들어 index()에서 "self.nodes가 아닌 경우"는 사실입니까? 비슷하게, parent()와 createIndex()에 대한 카운트가 거의 같기 때문에 index.isValid()는 훨씬 더 자주 발생합니다 (엔드 노드가 부모 노드보다 훨씬 많기 때문에 합리적입니다). 이 경우를 먼저 처리하기 위해 다시 코딩하면 parenttime을 더 줄일 수 있습니다. 편집 : 두 번째 생각에, 그러한 최적화는 "타이타닉에 갑판 의자를 재배치"합니다.

관련 문제