Я пытаюсь установить setData () моего QAbstractTableModel (который подключен к QTableView) из другого потока. Данные в модели изменяются, как и ожидалось, но представление не обновляется само по себе (только после нажатия на представление таблицы, которое вызывает представление для обновления). Каков наилучший способ реализации такого обновления?
Я работаю над Python 3.6 с pyqt 5.11.1. Я пытался испустить dataChanged (а также layoutAboutToBeChanged, layoutChanged, editCompleted) сигнал от метода setData моей модели - ничего из этого не работает.
Затем я предложил два возможных решения:
1) испускающий modelReset из setData
или же
2) создание QTimer в модели и подключение его к методу, который генерирует dataChanged для всех индексов модели
И то и другое работает, как и ожидалось, но я думаю, что это не очень хорошие решения, так как сначала нужно обновить всю таблицу (я так полагаю), и это не очень полезный вариант? И второе решение просто даст постоянную нагрузку на приложение, за исключением некоторой задержки отображения данных.
Это минимальный (надеюсь, так), воспроизводимый пример моей проблемы
import sys
import threading
import time
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt as Qt
class CopterDataModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None):
super(CopterDataModel, self).__init__(parent)
self.data_contents = [[1, 2]]
def rowCount(self, n=None):
return len(self.data_contents)
def columnCount(self, n=None):
return 2
def data(self, index, role):
row = index.row()
col = index.column()
#print('row {}, col {}, role {}'.format(row, col, role)) #for debug
if role == Qt.DisplayRole:
return self.data_contents[row][col] or ""
@QtCore.pyqtSlot()
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid():
return False
if role == Qt.EditRole:
self.data_contents[index.row()][index.column()] = value
print("edit", value)
self.modelReset.emit() # working fine
#self.dataChanged.emit(index, index, [Qt.EditRole]) # NOT WORKING
else:
return False
return True
def flags(self, index):
roles = Qt.ItemIsSelectable | Qt.ItemIsEnabled
return roles
if __name__ == '__main__':
def timer():
idc = 1001
while True:
myModel.setData(myModel.index(0, 0), idc)
idc += 1
time.sleep(1)
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
tableView = QtWidgets.QTableView()
myModel = CopterDataModel(None)
tableView.setModel(myModel)
tableView.show()
t = threading.Thread(target=timer, daemon=True)
t.start()
app.exec_()
Индекс (0, 0) табличного представления должен обновляться каждую секунду с увеличением счетчика (что не происходит, когда я пытаюсь передать сигнал dataChanged, работает только с modelReset). (обратите внимание, это лишь минимальный пример потока, который имеет более сложную логику в реальном коде и данные не поступают «по таймеру»)
Таймер от https://github.com/Taar2/pyqt5-modelview-tutorial/blob/master/modelview_3.py также заставляет его работать (минусы этого решения описаны выше).
Я ожидаю, что сигналы будут работать так же, но по какой-то причине это не происходит, и представление не обновляется с помощью сигнала dataChanged, вызванного из потока.