Reputation: 282
I am trying to solve a problem with PySide2, QThread and the Signal/Slot mechanism.
What I want to achieve is updating a specific progressbar which is passed as reference via signal/slot mechanism.
The problem is the call of self.gui_connection.signal_progress_value.emit(lambda: self.progressbar, value)
in my ProgressBarThread class.
The program stops after emitting the signal with a QProgressBar and an int value as arguments and causes a Seg Fault. If I just pass an int value to the signal and call emit in my ProgressBarThread everything is working fine.
Is it just not possible to pass an Object via Signal/Slot mechanism?
(Reduced) Code Samples:
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_())
ingestLayout.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
The problem is that the program stops after emitting the signal with a QProgressBar and a value as arguments and causes a Seg Fault. If I just pass an int value to the signal and call emit in my ProgressBarThread everything is working fine.
Is it just not possible to pass Object via Signal/Slot mechanism?
Upvotes: 1
Views: 467
Reputation: 282
SOLVED:
The problem was the lambda function in the emit function inside the QThread class. It seems like the object was not passed by the lambda function. Just using .emit(self,progressbar, 100) is working.
Upvotes: 0
Reputation: 244282
It is not necessary for the signal to send as QProgressBar dates, in addition to the GUI is not thread-safe, on the other hand it is not necessary to use a lambda.
Considering the above, the solution is:
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()
Update:
In the previous part of my answer I separate the GUI from the business logic as this will have a more scalable SW. But the OP seems to not want that so the simple solution is not to use the lambda method since it is unnecessary:
self.gui_connection.signal_progress_value.emit(self.progressbar, value)
self.gui_connection.signal_progress_value.emit(self.progressbar, 100)
Upvotes: 3