2009-05-08 30 views
12

Geçenlerde hangi üzerinde çalıştığım bir projede bir duvara isabet ettik PyQT kullanır. Bir QTreeView tipik içinde düğümlerin binlerce olan bir QAbstractItemModel bağladım var. Şimdiye kadar, iyi çalışıyor, ama bugün çok fazla düğüm seçmenin çok yavaş olduğunu fark ettim. Bazı kazılardan sonra, QAbstractItemModel.parent() yönteminin çok sık çağrıldığı ortaya çıkıyor.QTreeView'da yavaş seçim, neden?

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

sadece kod çalıştırmasına Sorunu yeniden oluşturmak için (profilleme etmez) ve (ya vardiya seçimi veya Cmd-A üzerinden) ağaç widget'ı tüm düğümleri seçmek: Ben sorunu yeniden minimal kod yarattı. Uygulamayı çıktığınızda, profilleme istatistikler gibi bir şey gösterecektir:

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} 

bu verilerdeki garip kısmı denir ne sıklıkta ebeveyn() 'dir: 136K kez 2k düğümleri için! Herkesin neden bir ipucu var? senin ağaç görünümü için setUniformRowHeights(true) çağıran

cevap

3

Dene:

Ayrıca, C++ aracı qt laboratuarlarından modeltest denilen var. Ben PyQt5 için çok güzel bir örnek kod dönüştürülmüş ve Qt5.2 altında koştu ve sayılar yine açıklanamaz büyük yani benzer olduğunu teyit edebilir

https://wiki.qt.io/Model_Test

+0

ipucu için teşekkürler, ama ne yazık ki, yardım etmedi. Ebeveyn çağrılarının sayısını azalttı, ancak sadece 134k çağrılarına. Modeltest gelince , bu ilginç görünüyor, ama ben (ben onu google gerekecek) PyQt'de 3. parti C++ bileşenleri almak için nasıl bilmiyorum. Ama her durumda, bu modelin doğru olduğu anlaşılıyor, değil mi? –

0

: gerçi piton için bir şey varsa emin değilim çağrıların sayısı.

 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)
Ve sayımları gerçekten aşırı (ve Hiçbir açıklamam yok) iken, cumtime değerleri değildir dikkat edin: Burada örneğin başlangıcında, tüm seçmek bir sayfa kaydırmak için cmd-A için raporun üst kısmıdır, çıkın çok büyük. Ayrıca bu işlevler daha hızlı çalışacak şekilde yeniden yazılabilir; örneğin, index(), "self.nodes değilse" hiç doğru mu? Benzer şekilde, (son düğümler çok daha sayısız üst düğümlerine daha olduğu gibi, makul) dolayısıyla index.isValid() çok daha sık değil doğrudur, neredeyse aynıdır) veli() ve CREATEINDEX (için sayıları dikkat edin. Bu vakayı işlemek için önce kodlamak ebeveyn() cumtime daha da keser. Düzenleme: ikinci düşüncede, bu tür optimizasyonlar "titanik üzerindeki şezlongların yeniden düzenlenmesi" dir.