Я начинаю изучать PyQt4 и уже давно на чем-то застрял и сам не могу понять:
Вот концепция: существует TreeView с пользовательской моделью QStandartItemModel, которая перестраивается каждые пару секунд и может иметь много (не менее сотен) записей, также будут дополнительные делегаты для разных столбцов и т. д. Это довольно сложно и время построения даже простой модели без делегатов увеличивается до 0,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 и как с этим бороться, также кажется, что память, используемая моим «приложением», постоянно увеличивается.
Другое дело, что я хочу сохранить свой выбор, но на основе данных элемента, а не визуального прямоугольника или порядка строк, поэтому я тоже это сделал, но это выглядит слишком грязным/хакерским, чтобы быть правильным способом. Любая помощь в этом также будет оценена. Код следует:
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_()
1 ответ
Вы правильно поняли.
Вам нужен отдельный рабочий поток для пересчета модели для вас (так часто, как вы хотите, на основе времени или сигнала). Затем вам нужно подключить сигнал, когда вычисления будут выполнены, чтобы уведомить основной поток.
Есть несколько ошибок, о которых вы должны знать.
QObject живут в потоках. Парадигма сигнал/слот (по крайней мере, в C++ QT) по умолчанию работает локально в потоке-владельце. Если вы хотите отправить сигнал через поток, вам нужно указать это явно (см. документацию по сигналу/соединению).
Чтобы работать с моделью в рабочем потоке, вам нужно "переместить" модель в рабочий поток (должен быть метод с именем movetothread или что-то в этом роде).
Убедитесь, что основной поток и рабочий поток правильно синхронизированы.
QT также имеет QFuture (не уверен, есть ли он у PyQT), который можно использовать для хранения новой модели в основном потоке и автоматической перезагрузки ее, когда рабочий поток регенерирует ее.
Удачи.
Похожие вопросы
Новые вопросы
python
Python — это мультипарадигмальный многоцелевой язык программирования с динамической типизацией. Он предназначен для быстрого изучения, понимания и использования, а также обеспечивает чистый и унифицированный синтаксис. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Если у вас есть вопросы о версии Python, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas, NumPy) укажите это в тегах.