首页 > 解决方案 > 如何使用 pyQt 和 appScheduler 更改视图?

问题描述

我是python新手。我正在编写桌面应用程序,它应该每 x 分钟提醒用户一次练习。当计数器到达时间应用程序应更改视图以查看视频。

在应用程序中,我使用 Python 3.7、appSchedulerevent-notifierpyQt 5Designer

整个应用程序使用一个窗口。我QStackedWidget用来改变视图之间。

为了实现我的目标,我appScheduler在我的主控制器动作中运行。在 x 次之后,它会触发应该更改视图的操作。

  EventDispatcher().getDispatcher().raise_event("onSettingsUpdate", view=view)

听众:

  # ...
  scheduler.getScheduler().add_job(self.changeView, 'interval', [view], minutes=time, id="exercise_block_scn_3", replace_existing=True, )
  # ...

def changeView(self, view):
  view.page_main.setCurrentWidget(view.view_video)

当我尝试运行应用程序时出现错误:

'job_state': pickle.dumps(job.__getstate__(), self.pickle_protocol)

TypeError:无法腌制“QWidget”对象

我搜索了 StackOverflow 以了解这个问题。我发现,当我尝试将视图设置为函数参数时,它使用指针对象。AppScheduler 使用主应用程序以外的其他线程,它会产生问题。AppScheduler 无法从第一个威胁访问内存。

我正在寻找解决这个问题的好方法。一种解决方案是覆盖视图对象并将其更改为停止使用指针。当我使用 desinger 生成文件时,我认为这不是一个好的解决方法。

你有想法如何以很好的方式解决它吗?

为了获得更好的图片,这里是 git repo 的链接: https ://github.com/TheGeniesis/PAK

PR 与我试图解决描述的问题的最后更改 https://github.com/TheGeniesis/PAK/pull/23/files

相关更改在这里:

https://github.com/TheGeniesis/PAK/pull/23/files#diff-3208ab0a9716ff23ea9f343d3cab88db9831089e8316bd44568a9c6557667c9fR42

https://github.com/TheGeniesis/PAK/pull/23/files#diff-f98587bb5b4b6eca87e710b9fb8487a58212feba106eb30dc04456f26da27504R45

编辑:

这是最简单的例子:

主视图.py

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'untitled.ui'
#
# Created by: PyQt5 UI code generator 5.14.1
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets



class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget)
        self.stackedWidget.setGeometry(QtCore.QRect(210, 30, 441, 471))
        self.stackedWidget.setObjectName("stackedWidget")
        self.view_main = QtWidgets.QWidget()
        self.view_main.setObjectName("view_main")
        self.label = QtWidgets.QLabel(self.view_main)
        self.label.setGeometry(QtCore.QRect(90, 80, 67, 17))
        self.label.setObjectName("label")
        self.stackedWidget.addWidget(self.view_main)
        self.view_wideo = QtWidgets.QWidget()
        self.view_wideo.setObjectName("view_wideo")
        self.label_2 = QtWidgets.QLabel(self.view_wideo)
        self.label_2.setGeometry(QtCore.QRect(220, 110, 67, 17))
        self.label_2.setObjectName("label_2")
        self.stackedWidget.addWidget(self.view_wideo)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.stackedWidget.setCurrentIndex(1)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "Main"))
        self.label_2.setText(_translate("MainWindow", "Video"))


if __name__ == "__main__":
    import sys
    from scheduler import Foo

    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()

    foo = Foo()
    foo.bar(ui)

    sys.exit(app.exec_())

调度程序.py:

from MainView import Ui_MainWindow
from apscheduler.schedulers.qt import QtScheduler

scheduler = QtScheduler({
    'apscheduler.jobstores.default': {
        'type': 'sqlalchemy',
        'url': 'sqlite:///jobs.sqlite'
    },
    'apscheduler.executors.default': {
        'class': 'apscheduler.executors.pool:ThreadPoolExecutor',
        'max_workers': '20'
    },
    'apscheduler.executors.processpool': {
        'type': 'processpool',
        'max_workers': '5'
    },
    'apscheduler.job_defaults.coalesce': 'false',
    'apscheduler.job_defaults.max_instances': '3',
    'apscheduler.timezone': 'UTC',
})
scheduler.start()

def changeView(view: Ui_MainWindow):
    print("run")
    view.stackedWidget.setCurrentWidget(view.view_wideo)

class Foo:
    def bar(self, view):
        time = 0.1
        print("asdasd")
        scheduler.add_job(changeView, 'interval', [view], seconds=time, id="exercise_block_scn_1", replace_existing=True, )

标签: pythonpyqt5

解决方案


由于您的类没有任何内部状态,并且如果这是导致问题的类,您可以试试这个:

class ExerciseTimeListener:
    __view: Ui_MainWindow

    def changeView(self, view):
        view.page_main.setCurrentWidget(view.view_video)

    def __getstate__(self):
        return dict()

    def __getstate__(self, state):
        pass

编辑:问题是QObject作为函数的参数给出的。所以想法是改变那个论点,但仍然能够检索视图。

您可以将视图名称作为字符串给出,并在主体函数中调用 agetView(view_name)来检索此视图。Foo实例化类时,名称和视图之间的关系可以使用全局字典来完成。

考虑从调用者脚本注册视图MainView.py

if __name__ == "__main__":
    import sys
    from scheduler import Foo
    from scheduler import viewDict

    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()

    # Register the view
    viewDict[ui.objectName()] = ui

    foo = Foo()
    foo.bar(ui)

    sys.exit(app.exec_())

这将是scheduler.py

viewDict = {}

def getView(view_name):
    return viewDict.get(view_name)

def changeView(view_name: str):
    print("run")
    view = getView(view_name)
    # Check if the requested view is registered
    if view:
        view.stackedWidget.setCurrentWidget(view.view_wideo)
    else:
        print("No view called {}".format(view_name))

class Foo:
    def bar(self, view):
        time = 0.1
        print("asdasd")
        scheduler.add_job(changeView, 'interval', [view.objectName()], seconds=time, id="exercise_block_scn_1", replace_existing=True, )

推荐阅读