1

私は PyQt4 を学び始めており、長い間何かに固執していて、自分で理解できません:

概念は次のとおりです。数秒ごとに再構築されるカスタム QStandartItemModel を備えた TreeView があり、多数の (少なくとも数百の) エントリを持つことができ、さまざまな列などに追加のデリゲートも存在します。かなり複雑で、デリゲートのない単純なモデルの構築時間は最大 .3 秒になり、TreeView がフリーズします。

これを解決する最善の方法についてアドバイスをください。どういうわけか別のスレッドでモデルを構築し、最終的にそれを TreeView に送信して、新しいモデルで setModel() を実行することを考えていましたが、それを機能させることができませんでした。

問題を少し説明するコードを次に示します。

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, os, re, time

app = QApplication(sys.argv)
REFRESH = 1

class Reloader_Thread(QThread):
    def __init__(self, parent = None):
        QThread.__init__(self, parent)
        self.loaders = ['\\', '--', '|', '/', '--']
        self.emit(SIGNAL('refresh'))
    def run(self):
        format = '|%d/%b/%Y %H:%M:%S| '
        while True:
            self.emit(SIGNAL('refresh'))
            self.sleep(REFRESH)

class Model(QStandardItemModel):
    def __init__(self, viewer=None):
        QStandardItemModel.__init__(self,None)
        self.build()

    def build(self):
        stTime = time.clock()
        newRows = []
        for r in range(1000):
            row = []
            for c in range(12):
                item = QStandardItem('%s %02d%02d' % (time.strftime('%H"%M\'%S'), r,c))
                row.append(item)
            newRows.append(row)
        eTime = time.clock() - stTime
        outStr = 'Build %03f' % eTime
        format = '|%d/%b/%Y %H:%M:%S| '
        stTime = time.clock()
        self.beginRemoveRows(QModelIndex(),  0, self.rowCount())
        self.removeRows(0, self.rowCount())
        self.endRemoveRows()
        eTime = time.clock() - stTime
        outStr += ', Remove %03f' % eTime
        stTime = time.clock()
        numNew = len(newRows)
        for r in range(numNew):
            self.appendRow(newRows[r])
        eTime = time.clock() - stTime
        outStr += ', Set %03f' % eTime
        self.emit(SIGNAL('status'), outStr)
        self.reset()

w = QWidget()
w.setGeometry(200,200,800,600)
hb = QVBoxLayout(w)
tv = QTreeView()
tvm = Model(tv)
tv.setModel(tvm)

sb = QStatusBar()
reloader = Reloader_Thread()
tvm.connect(tvm, SIGNAL('status'), sb.showMessage)
reloader.connect(reloader, SIGNAL('refresh'), tvm.build)
reloader.start()

hb.addWidget(tv)
hb.addWidget(sb)
w.show()
app.setStyle('plastique')
app.processEvents(QEventLoop.AllEvents)
app.aboutToQuit.connect(reloader.quit)
app.exec_()

ヒントをありがとう。これまでに得た状況は次のとおりです。更新するたびに、新しいモデルを作成して TreeView に送信します...高速ですが、TreeView の現在のモデルで何が起こっているのか、どのように対処すればよいのかわかりません。また、「アプリ」で使用されるメモリが常に増加しているようです。

もう1つのことは、選択を保持したいのですが、視覚的な四角形や行の順序ではなく、アイテムデータに基づいているため、それも行いましたが、適切な方法であるにはあまりにも汚い/ハッキーに見えます. これに関するヘルプもいただければ幸いです。コードは次のとおりです。

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys, os, re, time

app = QApplication(sys.argv)
REFRESH = 1

class Reloader_Thread(QThread):
    def __init__(self, parent = None):
        QThread.__init__(self, parent)
        self.moveToThread(self)

    def run(self):
        while True:
            model = Model()
            #model.connect(model, SIGNAL('status'), self.emitStat)
            if model.build():
                self.emit(SIGNAL('refresh'), model)
            self.sleep(REFRESH)

    def emitStat(self, stat):
        self.emit(SIGNAL('status'), stat)

class Tree(QTreeView):
    def __init__(self, parent=None):
        QTreeView.__init__(self, parent)
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)

    def resetModel(self, model):
        stTime = time.clock()
        # gather old selection
        oldSel = set()
        currModel = self.model()
        for index in self.selectedIndexes():
            id = currModel.itemFromIndex(index).data().toString()
            oldSel.add('%s'%id)
        # setup new
        self.setModel(model)
        selModel = self.selectionModel()
        for r in range(model.rowCount()):
            item = model.item(r,0)
            rowId = '%s' % item.data().toString()
            if rowId in oldSel:
                sel = QItemSelection(model.index(r,0), model.index(r,model.columnCount()-1))
                selModel.select(sel, QItemSelectionModel.Select)
        self.setSelectionModel(selModel)
        self.emit(SIGNAL('status'), 'TV setModel: %03fs' % (time.clock() - stTime))

class Model(QStandardItemModel):
    def __init__(self, viewer=None):
        QStandardItemModel.__init__(self,None)

    def build(self):
        stTime = time.clock()
        newRows = []
        for r in range(1000):
            row = []
            var = QVariant('%d'%r)
            for c in range(12):
                item = QStandardItem('%s r%02dc%02d' % (time.strftime('%H"%M\'%S'), r,c))
                item.setData(var)
                row.append(item)
            newRows.append(row)
        eTime = time.clock() - stTime
        outStr = 'Build %03f' % eTime
        format = '|%d/%b/%Y %H:%M:%S| '
        stTime = time.clock()
        self.beginRemoveRows(QModelIndex(),  0, self.rowCount())
        self.removeRows(0, self.rowCount())
        self.endRemoveRows()
        eTime = time.clock() - stTime
        outStr += ', Remove %03f' % eTime
        stTime = time.clock()
        numNew = len(newRows)
        for r in range(numNew):
            self.appendRow(newRows[r])
        eTime = time.clock() - stTime
        outStr += ', Set %03f' % eTime
        self.emit(SIGNAL('status'), outStr)
        #self.reset()
        return True

w = QWidget()
w.setGeometry(200,200,800,600)
hb = QVBoxLayout(w)
tv = Tree()

sb = QStatusBar()
reloader = Reloader_Thread()
tv.connect(tv, SIGNAL('status'), sb.showMessage)
reloader.connect(reloader, SIGNAL('refresh'), tv.resetModel)
reloader.connect(reloader, SIGNAL('status'), sb.showMessage)
reloader.start()

hb.addWidget(tv)
hb.addWidget(sb)
w.show()
app.setStyle('plastique')
app.processEvents(QEventLoop.AllEvents)
app.aboutToQuit.connect(reloader.quit)
app.exec_()
4

1 に答える 1

1

あなたは正しい考えを持っています。

モデルを再計算するには、別のワーカー スレッドが必要です (必要に応じて、時間ベースまたはシグナル ベースで)。次に、メイン スレッドに通知するために、計算が完了したときにシグナルを接続する必要があります。

注意しなければならない落とし穴がいくつかあります。

  1. QObject はスレッドに存在します。シグナル/スロット パラダイム (少なくとも C++ QT では) は、デフォルトでオーナー スレッドに対してローカルに機能します。シグナル クロス スレッドを送信する場合は、明示的に指定する必要があります (シグナル/接続のドキュメントを参照)。

  2. ワーカー スレッドでモデルを操作するには、モデルをワーカー スレッドに「移動」する必要があります (movetothread などと呼ばれるメソッドが必要です)。

  3. メイン スレッドとワーカー スレッドが適切に同期されていることを確認します。

QT には QFuture (PyQT にあるかどうかは不明) もあり、これを使用して新しいモデルをメイン スレッドに保持し、ワーカー スレッドがモデルを再生成するときに自動的にリロードできます。

幸運を。

于 2011-11-14T12:31:26.707 に答える