-1

I am trying to start a Dialog including QListWidget. In this list every time a status is changed, the list should get a new line. At the end, if the backup process has finished, the dialog should terminate itself. Question: how I can update the status in the QListWidget during the process and how to close the dialog at the end without pushing buttons.

import random
import sys
import time
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QListWidget, QListWidgetItem, QMessageBox
class Backup_GUI(QDialog):
 final_signal = QtCore.pyqtSignal(int)
 def __init__(self):
 super().__init__(parent=None)
 self.setWindowTitle("Daily Backup ... ")
 self.layout = QVBoxLayout()
 self.info = QListWidget()
 self.info.addItem(QListWidgetItem("Please insert USB drive ... "))
 self.info.addItem(QListWidgetItem("Looking for new USB drive ... "))
 self.info.setAutoScroll(True)
 self.layout.addWidget(self.info)
 self.setLayout(self.layout)
 self.final_signal.connect(self.own_func)
 self.show()
 def own_func(self, backupcode):
 if backupcode == 1:
 self.info.addItem(QListWidgetItem(f"own_func() called; BackupCode {backupcode} ==> self.accept()"))
 self.accept()
 else:
 self.info.addItem(QListWidgetItem(f"own_func() called; BackupCode {backupcode} ==> self.reject()"))
 self.reject()
 def getting_started(self):
 self.info.addItem(QListWidgetItem(f"getting_started() called"))
 self.check_usb_drive()
 def check_usb_drive(self):
 # code checking if the correct usb drive is connectected
 # starting USB Monitor if no USB Drive is found
 self.info.addItem(QListWidgetItem(f"check_usb_drive() called"))
 self.info.addItem(QListWidgetItem(f"randomly time.sleep(random.randint(1,5))"))
 time.sleep(random.randint(1, 5))
 self.backup_routine()
 def backup_routine(self):
 # building backup-command for mariadbbackup
 # copying files
 # process of backing up
 self.info.addItem(QListWidgetItem(f"backup_routine() called"))
 self.info.addItem(QListWidgetItem(f"randomly time.sleep(random.randint(1,5))"))
 time.sleep(random.randint(1, 5))
 backup_ok = random.choice([True, False])
 if backup_ok:
 self.info.addItem(QListWidgetItem(f"Signal 1 emitted, backup ok"))
 self.final_signal.emit(1)
 else:
 self.info.addItem(QListWidgetItem(f"Signal 0 emitted, backup not ok"))
 self.final_signal.emit(0)
if __name__ == '__main__':
 app = QtWidgets.QApplication(sys.argv)
 backup_window = Backup_GUI()
 backup_window.getting_started()
 answ = backup_window.exec()
 if answ == QMessageBox.Accepted:
 print("all good")
 else:
 print("not so good")
 print("finished and closed ... or not")

So I think I know the problem: I call getting_started() before doing the .exec() command. So technically the .accept/.reject signal emits in a moment, the dialog has not been shown/initialized/... - therefore I call self.show() in init.

Whatsoever - the dialog never closes.

Here I already work with the signal idea. When I added the random sleep lines I recognized, that the dialog is show (due to .show() in init) but nothing is prompted. Only after finishing all code, the lines are prompted. I am slowly getting the idea, why threading is proposed... I never did that before.

5
  • as for me it shouldn't be closed by QDialog but by code which is backuping USB drive, or it should emit signal which is connected to some function in QDialog with closing code. Commented Oct 11, 2025 at 12:38
  • As of now, it's really difficult to provide a proper answer, as we don't really know your implementation (except for a vague explanation) and its background. That said, despite the common usage of Qt dialog's exec() function (which is also used in static functions of QDialog subclasses such as QMessageBox), the documentation for that very function also warns against its usage. Based on the code you provided, your attempts are inappropriate to begin with: calling show() in the __init__ or eventually calling accept() or reject() in getting_started() won't change anything as soon as » Commented Oct 12, 2025 at 2:24
  • » you're also calling exec() right afterwards: since all the above calls are synchronous (done as soon as the previous call has returned), it doesn't really matter what you do before exec(), as that very call would simply show again the dialog anyway while simply ignoring anything done before. It would be like trying to show again the dialog after it's been closed. There are many ways to work around your issues (but, as said, without knowing more it's difficult to really tell you what would work better). For starters, you shouldn't try to implicitly show() the dialog to begin with. » Commented Oct 12, 2025 at 2:29
  • » Also, if getting_started() is blocking, then you should consider moving its implementation in a separate thread (assuming it's IO bound, not CPU bound). Then, what you could do is to internally call open() using a properly parented single-shot QTimer object only in case the timer interval has elapsed, or simply delete the dialog if it not needed. The concept is similar to QProgressDialog, which is shown only after a certain amount of time after its creation. In any case, calling exec() after doing a blocking computation that could potentially ignore the dialog is simply inappropriate. Commented Oct 12, 2025 at 2:37
  • Revision #3 adds code but also removes context to a degree that it's not clear what you are asking anymore. Is it an answer to the initial question or an attempt at clarification? Commented Oct 12, 2025 at 6:13

1 Answer 1

1

I don't know how you run backup code but I would run it in QThread and use signal to execute dlg.accept at the end of thread.

This way it doesn't block GUI and it can use exec() to display dialog.
It can also react on clicked buttons in this dialog.

class BackUpThread(QThread):
 finished = pyqtSignal()
 def run(self):
 # code
 self.finished.emit()
# ---
dialog = MyDialog()
thread = BackUpThread()
thread.finished.connect(dialog.accept)
thread.start()
dialog.exec()

I created minimal working code with dialog without buttons.

from PyQt5.QtWidgets import QApplication, QDialog, QLabel, QVBoxLayout
from PyQt5.QtCore import QThread, pyqtSignal
class BackUpThread(QThread):
 finished = pyqtSignal()
 def run(self):
 import time
 for i in range(5):
 print(f"Long running code ... {i}")
 time.sleep(1)
 self.finished.emit()
class MyDialog(QDialog):
 def __init__(self):
 super().__init__()
 self.setWindowTitle("Working...")
 layout = QVBoxLayout()
 layout.addWidget(QLabel("Please wait ..."))
 self.setLayout(layout)
if __name__ == "__main__":
 app = QApplication([])
 dialog = MyDialog()
 thread = BackUpThread()
 thread.finished.connect(dialog.accept)
 thread.start()
 dialog.exec()
 print("Task finished and dialog closed")

And if you need to keep code inside dialog then

from PyQt5.QtWidgets import QApplication, QDialog, QLabel, QVBoxLayout
from PyQt5.QtCore import QThread, pyqtSignal
class BackUpThread(QThread):
 finished = pyqtSignal()
 def run(self):
 import time
 for i in range(5):
 print(f"Long running code ... {i}")
 time.sleep(1)
 self.finished.emit()
class MyDialog(QDialog):
 def __init__(self):
 super().__init__()
 self.setWindowTitle("Working...")
 layout = QVBoxLayout()
 layout.addWidget(QLabel("Please wait ..."))
 self.setLayout(layout)
 def start_backup(self):
 # thread has to be assigned to self.
 self.thread = BackUpThread()
 self.thread.finished.connect(self.accept)
 self.thread.start()
if __name__ == "__main__":
 app = QApplication([])
 dialog = MyDialog()
 dialog.start_backup()
 dialog.exec()
 print("Task finished and dialog closed")

EDIT:

I created version for new code from question.

It uses QThread to execute long-running code - so this doesn't block exec() and it can display QDialog correctly.

But QThread can't directly change GUI (it can't add QLabel) - so this needs another signal to send message to QDialog which add QLabel.

import random
import sys
import time
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import (
 QDialog,
 QVBoxLayout,
 QListWidget,
 QListWidgetItem,
 QMessageBox,
)
class BackupThread(QtCore.QThread):
 message = QtCore.pyqtSignal(str)
 final_signal = QtCore.pyqtSignal(int)
 def run(self):
 self.getting_started()
 def getting_started(self):
 # self.info.addItem(QListWidgetItem(f"getting_started() called"))
 self.message.emit("getting_started() called")
 self.check_usb_drive()
 def check_usb_drive(self):
 # code checking if the correct usb drive is connectected
 # starting USB Monitor if no USB Drive is found
 # self.info.addItem(QListWidgetItem(f"check_usb_drive() called"))
 self.message.emit("check_usb_drive() called")
 value = random.randint(1, 5)
 # self.info.addItem(QListWidgetItem(f"randomly time.sleep(random.randint(1,5))"))
 self.message.emit(f"randomly time.sleep(random.randint(1,5)): {value}")
 # time.sleep(value)
 for i in range(value):
 self.message.emit(f"sleeping: {i}")
 time.sleep(1)
 self.backup_routine()
 def backup_routine(self):
 # building backup-command for mariadbbackup
 # copying files
 # process of backing up
 # self.info.addItem(QListWidgetItem(f"backup_routine() called"))
 self.message.emit("backup_routine() called")
 value = random.randint(1, 5)
 # self.info.addItem(QListWidgetItem(f"randomly time.sleep(random.randint(1,5))"))
 self.message.emit(f"randomly time.sleep(random.randint(1,5)): {value}")
 # time.sleep(value)
 for i in range(value):
 self.message.emit(f"sleeping: {i}")
 time.sleep(1)
 backup_ok = random.choice([True, False])
 if backup_ok:
 # self.info.addItem(QListWidgetItem(f"Signal 1 emitted, backup ok"))
 self.message.emit("Signal 1 emitted, backup ok")
 self.final_signal.emit(1)
 else:
 # self.info.addItem(QListWidgetItem(f"Signal 0 emitted, backup not ok"))
 self.message.emit("Signal 0 emitted, backup not ok")
 self.final_signal.emit(0)
class BackupGUI(QDialog):
 def __init__(self):
 super().__init__(parent=None)
 self.setWindowTitle("Daily Backup ... ")
 self.layout = QVBoxLayout()
 self.info = QListWidget()
 self.info.addItem(QListWidgetItem("Please insert USB drive ... "))
 self.info.addItem(QListWidgetItem("Looking for new USB drive ... "))
 self.info.setAutoScroll(True)
 self.layout.addWidget(self.info)
 self.setLayout(self.layout)
 # self.show()
 def add_message(self, text):
 self.info.addItem(QListWidgetItem(text))
 def own_func(self, backupcode):
 print("backupcode:", backupcode)
 if backupcode == 1:
 self.info.addItem(
 QListWidgetItem(
 f"own_func() called; BackupCode {backupcode} ==> self.accept()"
 )
 )
 self.accept()
 else:
 self.info.addItem(
 QListWidgetItem(
 f"own_func() called; BackupCode {backupcode} ==> self.reject()"
 )
 )
 self.reject()
if __name__ == "__main__":
 app = QtWidgets.QApplication(sys.argv)
 backup_window = BackupGUI()
 backup_worker = BackupThread()
 backup_worker.message.connect(backup_window.add_message)
 backup_worker.final_signal.connect(backup_window.own_func)
 backup_worker.start()
 # backup_window.getting_started()
 answ = backup_window.exec()
 if answ == QMessageBox.Accepted:
 print("all good")
 else:
 print("not so good")
 print("finished and closed ... or not")
answered Oct 11, 2025 at 18:58
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks, furas! This solution works fine, and I consider to recode as that. I hoped to show the user what state we are during the whole process, so I added a QListWidget and added lines every time I got something done. But this won't work in here. So I only show "Please wait..."
more important element is to use QThread so code doesn't block GUI. Even if you would run exec() then still your backup code may block GUI and it will freeze. But thread can make other problem: some GUIs don't like to work in thread (if GUI was created in one thread then other thread may not have access to GUI) and it may need to use signals to send message from other thread to main thread, an main thread may need to update widgets.
I added new version using your code. It runs code in QThread and it uses signal to send message to QDialog which add QLabel. This way backuping doesn't block GUI code.
Thank you very much, @furas for this brilliant answer. I did not dive into the threading thing until now. It seems, I need to. I will adept your solution to my process.
Hi again. I tried to implenet the thread. I struggle with exchanging information between functions in the backup thread. As there Is no init, the variables aren't accessible within the class. So if I define something in func_a, I do not can access that variable in func_b. Every exchange must be done with signals? Even within the thread class?
did you define variables with self.? You don't have to define them in __init__ but you can still use __init__(*args, **kwargs) in Thread, but you have to remeber to use super().__init__(*args, **kwargs). But maybe you shuld send values as parameters func_b(param1, param2) and send back with return param1, param2. Signals are needed to communicate betweeen main thread and backup thread.
there is some weird behaviour due to my code. I am seting up USB monitor, waiting for connection of the correct USB drive. If the thread starts, I need to check, if the correct USB drive is already connected and backing-up can start directly, or the monitor needs to be set up. then another function needs to start the backing-up. Now it works... a bit weird: I would like to show a QMessageBox during the process ("Correct USB drive found, start?") ... and here in 9 out of 10 cases the program freezes. But sometimes, it doesnt... I could imagine, this has something to do with the Threading.
maybe create minimal working code and put as new question - you will have more space to show code, and other people may see it so maybe other people will help you too,

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.