将导入的函数连接到没有依赖关系的 Qt5 进度条 [英] Connect an imported function to Qt5 progress bar without dependencies

查看:50
本文介绍了将导入的函数连接到没有依赖关系的 Qt5 进度条的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一组小型 Python 应用程序,旨在通过 CLI 运行.一些功能应该捆绑在 PyQT5 GUI 中以便更容易使用.现在,我的包中有一个函数,它往往会运行很长时间,所以我想显示一个进度条.但是,函数本身需要能够在没有 QT5 的情况下运行.我正在寻找一种方法,可以将我长期运行的导入函数的进度显示在 QT GUI 中,而不会使 QT 成为我的包的依赖项.

I'm writing a set of small python applications, that are aimed to be run via CLI. Some of the functions should be bundled together in a PyQT5 GUI to be easier usable. Now, I have one function inside my package, that tends to run quite long, so I would like to display a progress bar. However, the function itself needs to be able to be run without QT5 present. I'm looking for a way to have the progress from my long running imported function to be shown in the QT GUI without making QT a dependency of my package.

简单例子:

我包裹内的某处:

import time
percent = 0
def long_running_function(percent):
  while percent < 100:
    percent+=1
    #do something here to update percentage in QT
    time.sleep(1) #just to indicate, that the function might be a blocking call

我的简单图形用户界面:

My simple GUI:

from my_package import long_running_function

from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import (QApplication, QDialog,
                             QProgressBar, QPushButton)

class Actions(QDialog):
    """
    Simple dialog that consists of a Progress Bar and a Button.
    Clicking on the button results in running my external function and
    updates the progress bar.
    """
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Progress Bar')
        self.progress = QProgressBar(self)
        self.progress.setGeometry(0, 0, 300, 25)
        self.progress.setMaximum(100)
        self.button = QPushButton('Start', self)
        self.button.move(0, 30)
        self.show()

        self.button.clicked.connect(self.onButtonClick)

    def onButtonClick(self):
        long_running_function(0)
        self.progress.setValue(value) #probably somewhere

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Actions()
    sys.exit(app.exec_())

我知道,我可以通过在 long_running_function 内循环的每次迭代中发出一个 pyqtsignal 来解决这个问题,但这会使 QT 成为我的包的依赖项,我想规避.

I know, that I could solve this, by emitting a pyqtsignal in each iteration of the loop inside long_running_function, but that would make QT a dependency of my package, which I would like to circumvent.

推荐答案

一种可能的解决方案是通过将 __add__ 和 __lt__ 运算符实现为函数的百分比来创建 QObject:

One possible solution is to create a QObject by implementing the __add__ and __lt__ operators to be the percent of the function:

from functools import partial

from PyQt5.QtCore import QObject, QThread, QTimer, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QDialog, QProgressBar, QPushButton

from my_package import long_running_function


class PercentageWorker(QObject):
    started = pyqtSignal()
    finished = pyqtSignal()
    percentageChanged = pyqtSignal(int)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._percentage = 0

    def __add__(self, other):
        if isinstance(other, int):
            self._percentage += other
            self.percentageChanged.emit(self._percentage)
            return self
        return super().__add__(other)

    def __lt__(self, other):
        if isinstance(other, int):
            return self._percentage < other
        return super().__lt__(other)

    def start_task(self, callback, initial_percentage):
        self._percentage = initial_percentage
        wrapper = partial(callback, self)
        QTimer.singleShot(0, wrapper)

    @pyqtSlot(object)
    def launch_task(self, wrapper):
        self.started()
        wrapper()
        self.finished()


class Actions(QDialog):
    """
    Simple dialog that consists of a Progress Bar and a Button.
    Clicking on the button results in running my external function and
    updates the progress bar.
    """

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("Progress Bar")
        self.progress = QProgressBar(self)
        self.progress.setGeometry(0, 0, 300, 25)
        self.progress.setMaximum(100)
        self.button = QPushButton("Start", self)
        self.button.move(0, 30)
        self.show()

        self.button.clicked.connect(self.onButtonClick)

        thread = QThread(self)
        thread.start()
        self.percentage_worker = PercentageWorker()
        self.percentage_worker.moveToThread(thread)
        self.percentage_worker.percentageChanged.connect(self.progress.setValue)
        self.percentage_worker.started.connect(self.onStarted)
        self.percentage_worker.finished.connect(self.onFinished)

    @pyqtSlot()
    def onStarted(self):
        self.button.setDisabled(True)

    @pyqtSlot()
    def onFinished(self):
        self.button.setDisabled(False)

    @pyqtSlot()
    def onButtonClick(self):
        self.percentage_worker.start_task(long_running_function, 0)


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    window = Actions()
    sys.exit(app.exec_())

这篇关于将导入的函数连接到没有依赖关系的 Qt5 进度条的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆