- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在编写一个小程序,用于从 Reddit 获取提交内容。到目前为止,我的目标只是获取提交内容并将其显示在 QML 中的 ListView 中。我创建了一个基本的 QML 文件,并创建了一个名为“SubmissionModel”的类,该类扩展了“QAbstractListModel”。我使用 PRAW 来获取 reddit 提交内容,效果非常好。
我使用一个名为“fetch”的函数,该函数用从 reddit 获取的新提交内容填充 SubmissionModel
类。然而,这会阻塞 QML View 并使其挂起,直到“fetch”函数退出。
我尝试在另一个 Python 线程中运行“fetch”函数,这会释放 QML View ,但遗憾的是 ListView 不再更新。我正在寻找一种方法,可以在从另一个线程运行 fetch 函数的同时更新 QML 端的 ListView。
获取函数:
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
“SubmissionModel”类:
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_())
当应用程序像这样运行时^^它就可以工作了。 UI 会挂起,直到 fetch()
完成,完成后,UI 将使用 SubmissionModel
主线程:
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。事实上,用户界面永远不会随着新提交的内容而更新。
我的理解是 self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
和 self.endInsertRows()
方法addSubmission
函数的目的是让 UI 中的 ListView 知道模型已更新。但是,当运行这些方法的函数从另一个线程运行时,这不起作用。有人可以指出我如何从不同的线程更新 ListView 的正确方向吗?
最佳答案
建议使用 PyQt 提供的工具来处理线程:
QThreadPool
和 QRunnable
: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"),
)
)
submission = Submission(
submission.title,
submission.url,
datetime.datetime.fromtimestamp(int(submission.created)).strftime(
"%Y-%m-%d %H:%M:%S"
),
)
QMetaObject.invokeMethod(
model,
"addSubmission",
Qt.QueuedConnection,
Q_ARG(Submission, submission),
)
QThread.msleep(10)
if counter == 400:
break
class RedditRunnable(QRunnable):
def run(self):
fetch()
然后在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()
runnable = RedditRunnable()
QThreadPool.globalInstance().start(runnable)
sys.exit(app.exec_())
def fetch():
reddit = init_reddit()
subreddit = reddit.subreddit("LandscapePhotography")
counter = 0
message = Message()
message.submissionSignal.connect(model.addSubmission, Qt.QueuedConnection)
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"),
)
)
submission = Submission(
submission.title,
submission.url,
datetime.datetime.fromtimestamp(int(submission.created)).strftime(
"%Y-%m-%d %H:%M:%S"
),
)
message.submissionSignal.emit(submission)
QThread.msleep(10)
if counter == 400:
break
class Message(QObject):
submissionSignal = pyqtSignal(Submission)
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_())
两种方法都可以在下面link中获取.
关于python - 如何从 python 线程更新 QML ListView?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48793100/
我是一名优秀的程序员,十分优秀!