Solved Show a Loading Home Dialog before starting MainWindow
-
@hachbani Basic approach is: show progress dialog, start your long lasting operation, on each iteration emit a signal which is connected to https://doc.qt.io/qt-5/qprogressdialog.html#value-prop slot.
-
I updated the code a follow:
created a signal: self.change_val = QtCore.Signal(int) and connected it to a slot set_progress_val
in the LoadData, I emit change_val signal at different places.
Also did some changes in the if name == 'main'
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): change_val = QtCore.Signal(int) def __init__(self, file_name,parent=None): """ super(MainWindow, self).__init__(parent) .. __init__ code lines """ change_val.connect(self.set_progress_val) self.progress = QtWidgets.QProgressDialog('loading...', 'cancel', 0, 100, self) self.progress.show() self.LoadData(d.path) @QtCore.Slot(int) def set_progress_val(self, val): self.progress.setValue(val) def LoadData(self, file_path): """ Parsing lines of code .. change_val.emit(30) .. .. change_val.emit(60) .. .. """ self.progress.hide() #Parsing finished -> show the mainWindow self.show() class HomeDialog(QtWidgets.QDialog, home_dialog.Ui_Dialog): def __init__(self, parent=None): super(HomeDialog, self).__init__(parent) self.setupUi(self) self.openB6.clicked.connect(self.get_file_name) def get_file_name(self): file_name = QtWidgets.QFileDialog.getOpenFileName(self, 'Open config file', dir=path.join("/"), filter="B6 (*.b6)") if not file_name[0]: return None else: self.path = file_name self.accept() if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) app.setStyle(ProxyStyle()) d = HomeDialog() if d.exec_(): mainWin = MainWindow(file_name=d.path) mainWin.show() sys.exit(app.exec_())
I get the following error:
'str' object has no attribute 'connect'
-
@hachbani
On which line?self.change_val[int].connect(self.set_progress_val)
? Are you using PyQt5, PySide2, or what? -
I'm using Pyside2, pyothn 3.8
I get the error on the follwoing line
mainWin = MainWindow(file_name=d.path)
-
The idea of
self.change_val[int].connect(self.set_progress_val)
is that when i'm going to call self.change_val.emit(20), the set_progress_val is called with 20 as argument, maybe I'm implementing this wrong -
@hachbani
I'll look into the possibleconnect()
issue in a moment. ButI get the error on the follwoing line
mainWin = MainWindow(file_name=d.path)
mainWin = MainWindow(file_name=d.path)
I don't see your
MainWindow.init()
has afile_name
argument?Actually, before I look, could we establish where exactly this error is really coming from? With
print()
statements/debugger, do you actually get to theself.change_val[int].connect(self.set_progress_val)
line at all? Or is the error really that constructor line?? -
Just a pasting mistake, I updated the code in my last comment.
if I remove all the progressBar code parts, the program will run successfully, but with nothing being shown while the LoadData function is runnung
-
class MainWindow(QtWidgets.QMainWindow, file_name, Ui_MainWindow): def __init__(self, parent=None):
How do you manage
file_name
in the class declaration? How do you not have it in the__init__()
? I'm not going to guess if this is in fact not the code you have, that's what pasting is for.... -
@JonB said in Show a Loading Home Dialog before starting MainWindow:
Actually, before I look, could we establish where exactly this error is really coming from? With print() statements/debugger, do you actually get to the self.change_val[int].connect(self.set_progress_val) line at all? Or is the error really that constructor line??
I added a print before and after the connect line, I get the first print and then the
'str' object has no attribute 'connect'
the file_name argument gets passed to LoadData at the end of
MainWindow __init__()
-
@hachbani said in Show a Loading Home Dialog before starting MainWindow:
the file_name argument gets passed to LoadData at the end of MainWindow init()
class MainWindow(QtWidgets.QMainWindow, file_name, Ui_MainWindow):
I don't understand. It's not an "argument", is it? You have it in the list of classes from which
MainWindow
derives?? Unless your knowledge of Python is better than mine. -
Sorry sorry, feel ridiculous, it was a mistake, the file_name is indeed an argument to MainWindow init(). I corrected the code in the comment. It was in init in my code, just a rookie mistake while making the minimalistic code
-
@hachbani
Please understand, we have to get pasting/correct code right! When you've tried to help in this forum for as much as I have you never know what posters have actually got if it's not accurate, and I have wasted too much time over the years answering questions about user code which is not actually the code! :)I'll think about your
connect()
now.... -
Sorry again, I acknowledge that I've been wasting your time for a stupid mistake I've made pasting the code due to a lack of attention.
-
@hachbani
That's OK. At least you were polite enough to apologize, which is more than some :)You may know PyQt5 more than me. But two things come to mind about your signal declaration
self.change_val = QtCore.Signal(int)
- PyQt5 signals should be class members, not instance members?
- You should be using
pyqtSignal(int)
, notQtCore.Signal(int)
? - Similarly,
@pyqtSlot
decorator instead of@QtCore.Slot(int)
? Don't know if that matters.
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): change_val = pyqtSignal(int)
Do either of those improve? Reference https://doc.bccnsoft.com/docs/PyQt5/signals_slots.html
-
@hachbani said in Show a Loading Home Dialog before starting MainWindow:
I'm using Pyside2, pyothn 3.8
I'm using Pyside2, I'll think more about your first point
PyQt5 signals should be class members, not instance members? -
@hachbani said in Show a Loading Home Dialog before starting MainWindow:
I'm using Pyside2,
Damn, so you are, that changes everything! I got distracted because someone called, damn....
PyQt5 signals should be class members, not instance members?
Yes, and I think this applies to PySide2 too, and is the cause of your error message. See 'PySide.QtCore.Signal' object has no attribute 'connect'?
And/or further, does your
MainWindow.__init__()
callsuper().__init__()
? Omitting that can apparently lead to that error message too.Finally, since your own signal does not seem to have multiple overloads, you could try plain
change_val.connect(self.set_progress_val)
without that
[int]
. -
I've declared change_val as a class member, I get an error
name 'change_val' is not defined
I've updated the code in my last comment. I do indeed call super in MainWindow init()
-
@hachbani said in Show a Loading Home Dialog before starting MainWindow:
've declared change_val as a class member, I get an error name 'change_val' is not defined
Refer the https://wiki.qt.io/Qt_for_Python_Signals_and_Slots I quoted earlier for examples. It may be that to reference the class variable you have to go
self.change_val
orMainWindow.change_val
? -
Thanjs @JonB for you help ! Indeed the self.change_val worked. I get my ProgressDialog, I added some prints in set_progress_val to debug if it's been called correctly, and it is. The problem is the progressDialog freezes, no progress is shown.
Here's the actual code:
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): change_val = QtCore.Signal(int) def __init__(self, file_name,parent=None): """ super(MainWindow, self).__init__(parent) .. __init__ code lines """ self.change_val[int].connect(self.set_progress_val) self.progress = QtWidgets.QProgressDialog('loading...', 'cancel', 0, 100, self) self.progress.setValue(0) self.progress.show() self.LoadData(d.path) @QtCore.Slot(int) def set_progress_val(self, val): print('progress setVal called, new val: ', self.progress.value()) self.progress.setValue(val) def LoadData(self, file_path): """ Parsing lines of code .. self.change_val.emit(30) .. .. self.change_val.emit(60) .. .. """ self.change_val.emit(100) #Parsing finished -> show the mainWindow self.show() class HomeDialog(QtWidgets.QDialog, home_dialog.Ui_Dialog): def __init__(self, parent=None): super(HomeDialog, self).__init__(parent) self.setupUi(self) self.openB6.clicked.connect(self.get_file_name) def get_file_name(self): file_name = QtWidgets.QFileDialog.getOpenFileName(self, 'Open config file', dir=path.join("/"), filter="B6 (*.b6)") if not file_name[0]: return None else: self.path = file_name self.accept() if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) app.setStyle(ProxyStyle()) d = HomeDialog() if d.exec_(): mainWin = MainWindow(file_name=d.path) mainWin.show() sys.exit(app.exec_())
Here's a screenshot of the displayed window when progressDialog is displayed:
-
@hachbani
BecauseLoadData()
is in your GUI thread, Qt does not get to redraw/refresh the UI while it is running. I assume your bar shows once as "full" right at the end? I haven't usedQProgressBar
, but I imagine you should play with one of the following:-
Move
LoadFile()
to its own thread. That's the nicest, but can be the most code. -
Try
self.progress.repaint()
in your slot. -
Try
QCoreApplication.processEvents()
in your slot.
Is
set_progress_val()
called against eachemit()
? I think it is, because you are usingDirectConnection
; if not you can always callset_progress_val()
directly instead of going via a signal. -