PySide2 使用 QProgressBar 作为信号参数
Posted
技术标签:
【中文标题】PySide2 使用 QProgressBar 作为信号参数【英文标题】:PySide2 use QProgressBar as signal argument 【发布时间】:2020-02-21 14:59:10 【问题描述】:我正在尝试解决 PySide2、QThread 和 Signal/Slot 机制的问题。
我想要实现的是更新通过信号/槽机制作为参考传递的特定进度条。
问题是在我的 ProgressBarThread 类中调用self.gui_connection.signal_progress_value.emit(lambda: self.progressbar, value)
。
程序在发出带有 QProgressBar 和 int 值作为参数的信号后停止并导致 Seg Fault。如果我只是将一个 int 值传递给信号并在我的 ProgressBarThread 中调用 emit 一切正常。
难道不能通过 Signal/Slot 机制传递 Object 吗?
(简化的)代码示例:
GUISignal.py
from PySide2.QtCore import QObject, Signal
from PySide2.QtWidgets import QProgressBar
class GUISignal(QObject):
signal_progress_value = Signal(QProgressBar, int)
main.py
# File: main.py
import sys
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication, QPushButton
from PySide2.QtCore import QFile
from config.configManager import ConfigManager
from layout.srcConfigLayout import SrcConfigLayout
from layout.ingestLayout import IngestLayout
if __name__ == "__main__":
app = QApplication(sys.argv)
ui_file = QFile("main_window.ui")
ui_file.open(QFile.ReadOnly)
loader = QUiLoader()
window = loader.load(ui_file)
ui_file.close()
window.show()
src_config_layout = SrcConfigLayout(window)
ingestLayout = IngestLayout(window)
src_config_layout.load_config()
ingestLayout.load_config()
sys.exit(app.exec_())
摄取布局.py
from PySide2.QtWidgets import QTableWidget, QTableWidgetItem, QPushButton, QProgressBar
from PySide2.QtCore import QObject, Slot
from util.ingestManager import IngestManager
from config.configManager import ConfigManager
from util.progressBarThread import ProgressBarThread
from functools import partial
class IngestLayout(QObject):
def __init__(self, parent):
super().__init__()
self.parent = parent
def handleIngestClicked(self, srcpath, destpath, progressbar):
ingestManager = IngestManager()
ingestManager.copy_to_destination(self, srcpath, destpath)
progressThread = ProgressBarThread(srcpath, destpath, progressbar, self.parent)
progressThread.gui_connection.signal_progress_value.connect(self.updateProgressbar)
progressThread.start()
@Slot(QProgressBar, int)
def updateProgressbar(self, progressbar: QProgressBar, value: int):
pass
# progressbar.setValue(value)
progressBarThread.py
from PySide2.QtCore import QThread
import os
import threading
import time
from signals.GUISignal import GUISignal
class ProgressBarThread(QThread):
def __init__(self, srcpath, destpath, progressbar, parent):
super(ProgressBarThread, self).__init__(parent)
self.gui_connection = GUISignal()
self.srcpath = srcpath
self.destpath = destpath
self.src_size = self.size(srcpath)
self.progressbar = progressbar
def run(self):
self.periodically_compare_folder_size()
def periodically_compare_folder_size(self):
dest_size = self.size(self.destpath)
while self.src_size > dest_size:
value = self.calc_progress(self.src_size, dest_size)
self.gui_connection.signal_progress_value.emit(lambda: self.progressbar, value)
dest_size = self.size(self.destpath)
time.sleep(2)
else:
self.gui_connection.signal_progress_value.emit(lambda: self.progressbar, 100)
print(100)
return
def size(self, path, *, follow_symlinks=False):
try:
with os.scandir(path) as it:
return sum(self.size(entry, follow_symlinks=follow_symlinks) for entry in it)
except NotADirectoryError:
return os.stat(path, follow_symlinks=follow_symlinks).st_size
def calc_progress(self, src_size, dest_size):
return dest_size / src_size * 100
问题是程序在发出带有 QProgressBar 和值作为参数的信号后停止并导致 Seg Fault。如果我只是将一个 int 值传递给信号并在我的 ProgressBarThread 中调用 emit 一切正常。
难道不能通过 Signal/Slot 机制传递 Object 吗?
【问题讨论】:
提供minimal reproducible example 你在哪里调用handleIngestClicked? 【参考方案1】:信号没有必要作为 QProgressBar 日期发送,此外 GUI 不是线程安全的,另一方面也没有必要使用 lambda。
综合以上,解决办法是:
class GUISignal(QObject):
signal_progress_value = Signal(int)
class ProgressBarThread(QThread):
def __init__(self, srcpath, destpath, parent):
super(ProgressBarThread, self).__init__(parent)
self.gui_connection = GUISignal()
self.srcpath = srcpath
self.destpath = destpath
self.src_size = self.size(srcpath)
def run(self):
self.periodically_compare_folder_size()
def periodically_compare_folder_size(self):
dest_size = self.size(self.destpath)
while self.src_size > dest_size:
value = self.calc_progress(self.src_size, dest_size)
self.gui_connection.signal_progress_value.emit(value)
dest_size = self.size(self.destpath)
time.sleep(2)
else:
self.gui_connection.signal_progress_value.emit(100)
print(100)
return
# ...
def handleIngestClicked(self, srcpath, destpath, progressbar):
ingestManager = IngestManager()
ingestManager.copy_to_destination(self, srcpath, destpath)
progressThread = ProgressBarThread(srcpath, destpath, self.parent)
progressThread.gui_connection.signal_progress_value.connect(progressbar.setValue)
progressThread.start()
更新:
在我回答的前一部分中,我将 GUI 与业务逻辑分开,因为这将具有更具可扩展性的 SW。但是 OP 似乎不希望这样,所以简单的解决方案是不要使用 lambda 方法,因为它是不必要的:
self.gui_connection.signal_progress_value.emit(self.progressbar, value)
self.gui_connection.signal_progress_value.emit(self.progressbar, 100)
【讨论】:
谢谢,我知道你的意思了。我很快就会试试这个。但是,如果可以将对象(例如 QProgressBar 或任何其他对象)传递给信号/插槽,则存在一个整体问题。好像不是故意的。 PySide2 的 Signal/Slot 文档仅描述了使用字符串或整数传递给插槽..【参考方案2】:已解决:
问题是 QThread 类中的 emit 函数中的 lambda 函数。似乎该对象不是由 lambda 函数传递的。只需使用.emit(self,progressbar, 100) is working.
【讨论】:
您的回答正是我在更新中指出的以上是关于PySide2 使用 QProgressBar 作为信号参数的主要内容,如果未能解决你的问题,请参考以下文章