gpt4 book ai didi

python - 在后台运行函数并更新 UI

转载 作者:行者123 更新时间:2023-11-28 22:22:39 24 4
gpt4 key购买 nike

我正在使用 PyQt 为项目制作 GUI。

界面截图

输入数字并提交后,我需要执行将在后台运行的功能,否则应用程序会卡住,直到该过程完成。

我还需要在暗箱中输出函数产生的日志。

这是 GUI 代码:

import sys
from PyQt5.QtWidgets import (
QWidget,
QDesktopWidget,
QLineEdit,
QGridLayout,
QLabel,
QFrame,
QPushButton,
QApplication,
QTextEdit
)
from PyQt5.QtGui import (QTextCursor)
from bot.bot import (run, slack_notification)
from multiprocessing import Process, Pipe

class LogginOutput(QTextEdit):
def __init__(self, parent=None):
super(LogginOutput, self).__init__(parent)

self.setReadOnly(True)
self.setLineWrapMode(self.NoWrap)

self.insertPlainText("")

def append(self, text):
self.moveCursor(QTextCursor.End)
current = self.toPlainText()

if current == "":
self.insertPlainText(text)
else:
self.insertPlainText("\n" + text)

sb = self.verticalScrollBar()
sb.setValue(sb.maximum())

class App(QWidget):
def __init__(self):
super().__init__()

self.init_ui()

def init_ui(self):
label = QLabel('Amount')
amount_input = QLineEdit()
submit = QPushButton('Submit', self)
box = LogginOutput(self)

submit.clicked.connect(lambda: self.changeLabel(box, amount_input))

grid = QGridLayout()
grid.addWidget(label, 0, 0)
grid.addWidget(amount_input, 1, 0)
grid.addWidget(submit, 1, 1)
grid.addWidget(box, 2, 0, 5, 2)

self.setLayout(grid)
self.resize(350, 250)
self.setWindowTitle('GetMeStuff Bot v0.1')
self.show()

def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())

def changeLabel(self, box, user_input):
p = Process(target=run, args=(user_input.displayText(), box))
p.start()
user_input.clear()


if __name__ == '__main__':
app = QApplication(sys.argv)
widget = App()
sys.exit(app.exec_())

run 函数:

def run(user_input, log):
if user_input == "":
log.append("Please enter a value\n")
else:
log.append("Test")

为了在后台运行函数,我尝试使用 Process,但是当我执行 append 函数时,GUI 没有更新。

最佳答案

GUI 不应从另一个线程更新,因为 Qt 在应用程序所在的位置创建了一个循环,尽管 python 提供了许多使用线程的替代方法,但通常这些工具不处理 Qt 的逻辑,因此它们会产生问题。 Qt 提供了使用 QThread(低级)执行此类任务的类,但这次我将使用 QRunnable 和 QThreadPool,我创建了一个与 Process 行为相同的类:

class ProcessRunnable(QRunnable):
def __init__(self, target, args):
QRunnable.__init__(self)
self.t = target
self.args = args

def run(self):
self.t(*self.args)

def start(self):
QThreadPool.globalInstance().start(self)

使用:

self.p = ProcessRunnable(target=run, args=(user_input.displayText(), box))
self.p.start()

另外正如我之前所说,你不应该直接从另一个线程更新 GUI,一个解决方案是使用信号,或者在这种情况下,为了简单起见,使用 QMetaObject.invokeMethod:

def run(user_input, log):
text = ""
if user_input == "":
text = "Please enter a value\n"
else:
text = "Test"

QMetaObject.invokeMethod(log,
"append", Qt.QueuedConnection,
Q_ARG(str, text))

要正确调用,这必须是一个插槽,为此我们使用装饰器:

class LogginOutput(QTextEdit):
# ...
@pyqtSlot(str)
def append(self, text):
self.moveCursor(QTextCursor.End)
# ...

完整可行的例子在下面的代码中

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class ProcessRunnable(QRunnable):
def __init__(self, target, args):
QRunnable.__init__(self)
self.t = target
self.args = args

def run(self):
self.t(*self.args)

def start(self):
QThreadPool.globalInstance().start(self)

def run(user_input, log):
text = ""
if user_input == "":
text = "Please enter a value\n"
else:
text = "Test"

QMetaObject.invokeMethod(log,
"append", Qt.QueuedConnection,
Q_ARG(str, text))

class LogginOutput(QTextEdit):
def __init__(self, parent=None):
super(LogginOutput, self).__init__(parent)

self.setReadOnly(True)
self.setLineWrapMode(self.NoWrap)
self.insertPlainText("")

@pyqtSlot(str)
def append(self, text):
self.moveCursor(QTextCursor.End)
current = self.toPlainText()

if current == "":
self.insertPlainText(text)
else:
self.insertPlainText("\n" + text)

sb = self.verticalScrollBar()
sb.setValue(sb.maximum())

class App(QWidget):
def __init__(self):
super().__init__()

self.init_ui()

def init_ui(self):
label = QLabel('Amount')
amount_input = QLineEdit()
submit = QPushButton('Submit', self)
box = LogginOutput(self)

submit.clicked.connect(lambda: self.changeLabel(box, amount_input))

grid = QGridLayout()
grid.addWidget(label, 0, 0)
grid.addWidget(amount_input, 1, 0)
grid.addWidget(submit, 1, 1)
grid.addWidget(box, 2, 0, 5, 2)

self.setLayout(grid)
self.resize(350, 250)
self.setWindowTitle('GetMeStuff Bot v0.1')
self.show()

def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())

def changeLabel(self, box, user_input):
self.p = ProcessRunnable(target=run, args=(user_input.displayText(), box))
self.p.start()
user_input.clear()


if __name__ == '__main__':
app = QApplication(sys.argv)
widget = App()
sys.exit(app.exec_())

关于python - 在后台运行函数并更新 UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47560399/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com