如何从python线程更新QML ListView?

如何从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

我正在编写一个小程序,从Reddit获取提交。 到目前为止,我的目标只是获取提交并在QML的ListView中显示它们。我创建了一个基本的QML文件,并创建了一个名为“SubmissionModel”的类,该类扩展了“QAbstractListModel”。我使用PRAW获得reddit提交,效果非常好

我使用一个名为“fetch”的函数,用它从reddit获得的新提交填充
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使用率会不必要地增加太多。