如何从python线程更新QML ListView?
我正在编写一个小程序,从Reddit获取提交。 到目前为止,我的目标只是获取提交并在QML的ListView中显示它们。我创建了一个基本的QML文件,并创建了一个名为“SubmissionModel”的类,该类扩展了“QAbstractListModel”。我使用PRAW获得reddit提交,效果非常好 我使用一个名为“fetch”的函数,用它从reddit获得的新提交填充如何从python线程更新QML ListView?,python,multithreading,pyqt,qml,pyqt5,Python,Multithreading,Pyqt,Qml,Pyqt5,我正在编写一个小程序,从Reddit获取提交。 到目前为止,我的目标只是获取提交并在QML的ListView中显示它们。我创建了一个基本的QML文件,并创建了一个名为“SubmissionModel”的类,该类扩展了“QAbstractListModel”。我使用PRAW获得reddit提交,效果非常好 我使用一个名为“fetch”的函数,用它从reddit获得的新提交填充SubmissionModel类。但是,这会阻止QML视图并使其挂起,直到“fetch”函数退出 我试图在另一个Python
SubmissionModel
类。但是,这会阻止QML视图并使其挂起,直到“fetch”函数退出
我试图在另一个Python线程中运行“fetch”函数,这释放了QML视图,但遗憾的是ListView不再更新。我正在寻找一种方法,可以在QML端更新ListView,同时从另一个线程运行fetch函数
获取函数:
def fetch():
reddit = init_reddit()
subreddit = reddit.subreddit('LandscapePhotography')
counter = 0
for submission in subreddit.submissions(None, time.time()):
counter += 1
print(
"Counter: {} Submission title: {} , Submission URL: {} ,Created at: {}".format(
counter, submission.title,
submission.url,
datetime.datetime.fromtimestamp(int(submission.created)).strftime('%Y-%m-%d %H:%M:%S')))
model.addSubmission(Submission(submission.title, submission.url,
datetime.datetime.fromtimestamp(int(submission.created)).strftime(
'%Y-%m-%d %H:%M:%S')))
if counter == 400:
break
class SubmissionModel(QAbstractListModel):
NameRole = Qt.UserRole + 1
LinkRole = Qt.UserRole + 2
TimeRole = Qt.UserRole + 3
_roles = {NameRole: b"name", LinkRole: b"link", TimeRole: b"time"}
def __init__(self, parent=None):
super(SubmissionModel, self).__init__(parent)
self._submissions = []
def addSubmission(self, submission):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self._submissions.append(submission)
self.endInsertRows()
def rowCount(self, parent=QModelIndex()):
return len(self._submissions)
def data(self, index, role=Qt.DisplayRole):
try:
submission = self._submissions[index.row()]
except IndexError:
return QVariant()
if role == self.NameRole:
return submission.name()
if role == self.LinkRole:
return submission.link()
if role == self.CreateTimeRole:
return submission.time()
return QVariant()
def roleNames(self):
return self._roles
提交模型类:
def fetch():
reddit = init_reddit()
subreddit = reddit.subreddit('LandscapePhotography')
counter = 0
for submission in subreddit.submissions(None, time.time()):
counter += 1
print(
"Counter: {} Submission title: {} , Submission URL: {} ,Created at: {}".format(
counter, submission.title,
submission.url,
datetime.datetime.fromtimestamp(int(submission.created)).strftime('%Y-%m-%d %H:%M:%S')))
model.addSubmission(Submission(submission.title, submission.url,
datetime.datetime.fromtimestamp(int(submission.created)).strftime(
'%Y-%m-%d %H:%M:%S')))
if counter == 400:
break
class SubmissionModel(QAbstractListModel):
NameRole = Qt.UserRole + 1
LinkRole = Qt.UserRole + 2
TimeRole = Qt.UserRole + 3
_roles = {NameRole: b"name", LinkRole: b"link", TimeRole: b"time"}
def __init__(self, parent=None):
super(SubmissionModel, self).__init__(parent)
self._submissions = []
def addSubmission(self, submission):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self._submissions.append(submission)
self.endInsertRows()
def rowCount(self, parent=QModelIndex()):
return len(self._submissions)
def data(self, index, role=Qt.DisplayRole):
try:
submission = self._submissions[index.row()]
except IndexError:
return QVariant()
if role == self.NameRole:
return submission.name()
if role == self.LinkRole:
return submission.link()
if role == self.CreateTimeRole:
return submission.time()
return QVariant()
def roleNames(self):
return self._roles
不带线程的主功能:
if __name__ == '__main__':
import sys
app = QGuiApplication(sys.argv)
model = SubmissionModel()
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
ctxt = view.rootContext()
ctxt.setContextProperty('myModel', model)
view.setSource(QUrl('main.qml'))
view.show()
fetch()
sys.exit(app.exec_())
if __name__ == '__main__':
import sys
app = QGuiApplication(sys.argv)
model = SubmissionModel()
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
ctxt = view.rootContext()
ctxt.setContextProperty('myModel', model)
view.setSource(QUrl('main.qml'))
view.show()
**thread = threading.Thread(target=fetch)**
**thread.start()**
sys.exit(app.exec_())
当应用程序以这种方式运行时^^^它可以工作。UI将挂起,直到fetch()
完成,完成后,UI将使用SubmissionModel
带线程的Main:
if __name__ == '__main__':
import sys
app = QGuiApplication(sys.argv)
model = SubmissionModel()
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
ctxt = view.rootContext()
ctxt.setContextProperty('myModel', model)
view.setSource(QUrl('main.qml'))
view.show()
fetch()
sys.exit(app.exec_())
if __name__ == '__main__':
import sys
app = QGuiApplication(sys.argv)
model = SubmissionModel()
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
ctxt = view.rootContext()
ctxt.setContextProperty('myModel', model)
view.setSource(QUrl('main.qml'))
view.show()
**thread = threading.Thread(target=fetch)**
**thread.start()**
sys.exit(app.exec_())
这使得“fetch”函数在另一个线程上运行。虽然它工作正常,但每次添加提交时都无法更新UI。事实上,用户界面永远不会随着新提交的内容而更新
我的理解是,
addSubmission
函数中的self.beginInsertRows(QModelIndex()、self.rowCount()、self.rowCount())
和self.endInsertRows()
方法旨在让UI中的ListView知道模型已更新。但是,当运行这些方法的函数从另一个线程运行时,这就不起作用了。有人能告诉我如何从不同线程更新ListView的正确方向吗?建议使用PyQt提供的工具来处理线程:
带有QThreadPool
:QRunnable
def fetch():
reddit=init_reddit()
subreddit=reddit.subreddit(“景观摄影”)
计数器=0
对于subreddit.submissions中的提交(无,time.time()):
计数器+=1
印刷品(
“计数器:{}提交标题:{},提交URL:{},创建于:{}”。格式(
柜台
提交.标题,
submission.url,
datetime.datetime.fromtimestamp(
int(submission.created)
).strftime(“%Y-%m-%d%H:%m:%S”),
)
)
提交=提交(
提交.标题,
submission.url,
datetime.datetime.fromtimestamp(int(submission.created)).strftime(
%Y-%m-%d%H:%m:%S
),
)
QMetaObject.invokeMethod(
模型
“添加提交”,
Qt.QueuedConnection,
Q_ARG(提交,提交),
)
msleep(10)
如果计数器==400:
打破
类RedditRunnable(QRunnable):
def运行(自):
fetch()
然后它主要被称为:
如果uuuu name_uuuu=='\uuuuuuu main\uuuuuu':
导入系统
app=qgui应用程序(sys.argv)
模型=提交模型()
视图=QQuickView()
view.setResizeMode(QQuickView.SizerootObject视图)
ctxt=view.rootContext()
setContextProperty('myModel',model)
view.setSource(QUrl('main.qml'))
view.show()
runnable=RedditRunnable()
QThreadPool.globalInstance().start(可运行)
sys.exit(app.exec_())
- 或使用信号:
类消息(QObject):
submissionSignal=pyqtSignal(提交)
如果uuuu name_uuuu=='\uuuuuuu main\uuuuuu':
导入系统
app=qgui应用程序(sys.argv)
模型=提交模型()
视图=QQuickView()
view.setResizeMode(QQuickView.SizerootObject视图)
ctxt=view.rootContext()
setContextProperty('myModel',model)
view.setSource(QUrl('main.qml'))
view.show()
线程=线程。线程(目标=获取)
thread.start()
sys.exit(app.exec_())
这两种方法都可以通过以下方式获得。谢谢!它的工作原理完全符合预期!请问您为什么在发送信号后添加
QThread.msleep(10)
?它是为了防止一次触发过多的信号吗?它是为了给您一段时间让GUI更新数据,如果您不这样做,CPU使用率会不必要地增加太多。