Make complete python script from Qt Designer
-
wrote on 24 Apr 2023, 22:35 last edited by
I've created a complete python app starting from a MainWindow in Designer. It's loaded and run by a simple main wrapper which includes the MainWindow class py file import.
import sys from PySide6.QtWidgets import QApplication from MainWindow import Ui_MainWindow app = QApplication(sys.argv) mainWindow = Ui_MainWindow() mainWindow.show() sys.exit(app.exec())
However, I've been forced to hand edit the previously converted MainWindow python file so that the Ui_MainWindow class is based on a QMainWindow widget rather than the generic 'object' class that Designer automatically creates. I've also had to add an init() method.
Designer bases its setupUi() method on a QMainWindow, but not its own Ui_MainWindow class definition. Is there a way to configure Designer to set the parent of the class to be a QMainWindow, too ?
Hand edited from:
class Ui_MainWindow(object): # Why this? def setupUi(self, MainWindow): ...
to:
class Ui_MainWindow(QMainWindow): # Why not this? def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) def setupUi(self, MainWindow): # Why not just 'self'? ...
My edited MainWindow class works as expected, but I'd like Designer to outpu this in its .ui file to avoid this editing every time a new .ui file is saved.
-
I've created a complete python app starting from a MainWindow in Designer. It's loaded and run by a simple main wrapper which includes the MainWindow class py file import.
import sys from PySide6.QtWidgets import QApplication from MainWindow import Ui_MainWindow app = QApplication(sys.argv) mainWindow = Ui_MainWindow() mainWindow.show() sys.exit(app.exec())
However, I've been forced to hand edit the previously converted MainWindow python file so that the Ui_MainWindow class is based on a QMainWindow widget rather than the generic 'object' class that Designer automatically creates. I've also had to add an init() method.
Designer bases its setupUi() method on a QMainWindow, but not its own Ui_MainWindow class definition. Is there a way to configure Designer to set the parent of the class to be a QMainWindow, too ?
Hand edited from:
class Ui_MainWindow(object): # Why this? def setupUi(self, MainWindow): ...
to:
class Ui_MainWindow(QMainWindow): # Why not this? def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) def setupUi(self, MainWindow): # Why not just 'self'? ...
My edited MainWindow class works as expected, but I'd like Designer to outpu this in its .ui file to avoid this editing every time a new .ui file is saved.
wrote on 25 Apr 2023, 06:46 last edited by JonB@pascor
Because that is the way Designer intends it to be for PySide (or PyQt). TheUi_MainWindow
is a "helper" class, which is designed to accept a widget (yourMainWindow
/QMainWindow
) as a parameter which it fills inself.setupUi(self, MainWindow)
with the design-time content. It also works like this from C++. It is not intended to be derived fromQMainWindow
.When you run
pyuic
or whatever it is [pyside6-uic
] which parses the.ui
file and produces the.py
file from it you cannot alter this behaviour (unless you wrote your own equivalent ofpyuic
to produce what you want, which is a non-trivial task). Why should you want to? It works perfectly well as it is.If you have not already done so you should read Option A: Generating a Python class which explains how it works. Note that there is an alternative approach there, Option B: Loading it directly. That works by reading the
.ui
file at run-time instead of at design-time, and it produces a widget directly (viaQUiLoader.load()
) instead of a helper class. However I would not recommend it, you do not get any design-time support for the widgets you have added in Designer, and at run-time you do not get Python variables for those widgets, you have to useQObject.find(...)
to find them dynamically and assign them to variables.However, I've been forced to hand edit the previously converted MainWindow python file so that the Ui_MainWindow class is based on a QMainWindow widget rather than the generic 'object' class that Designer automatically create
Do not do this. It means you would lose all your work any time you make a change in Designer and need to run the pyuic again. Why did you feel you had to do so, other people work fine from Python or C++ with the way it works now with the helper class route?
P.S.
For your desired approach, there are ways of achieving it without you having to alter the generated.py
file, which you should never do. See the following:https://stackoverflow.com/a/70067108/489865
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self): super(MainWindow, self).__init__() self.setupUi(self)
The above uses multiple inheritance --- the
MainWindow
class is aQMainWindow
, but also inherits objects/methods from theUi_MainWindow
helper class. If you have designed a widget namedwidget
you will access it fromMainWindow
viaself.widget
. It is like the way you changed to, but without altering the generatedUi_MainWindow
class.https://stackoverflow.com/a/69625698/489865
class UI(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self)
The above uses encapsulation --- the
UI
class is aQMainWindow
, but it also contains aself.ui
member which is theUi_MainWindow
helper class, you can access its objects/methods viaself.ui
. If you have designed a widget namedwidget
you will access it fromMainWindow
viaself.ui.widget
. This is the way one does it from C++. -
@pascor
Because that is the way Designer intends it to be for PySide (or PyQt). TheUi_MainWindow
is a "helper" class, which is designed to accept a widget (yourMainWindow
/QMainWindow
) as a parameter which it fills inself.setupUi(self, MainWindow)
with the design-time content. It also works like this from C++. It is not intended to be derived fromQMainWindow
.When you run
pyuic
or whatever it is [pyside6-uic
] which parses the.ui
file and produces the.py
file from it you cannot alter this behaviour (unless you wrote your own equivalent ofpyuic
to produce what you want, which is a non-trivial task). Why should you want to? It works perfectly well as it is.If you have not already done so you should read Option A: Generating a Python class which explains how it works. Note that there is an alternative approach there, Option B: Loading it directly. That works by reading the
.ui
file at run-time instead of at design-time, and it produces a widget directly (viaQUiLoader.load()
) instead of a helper class. However I would not recommend it, you do not get any design-time support for the widgets you have added in Designer, and at run-time you do not get Python variables for those widgets, you have to useQObject.find(...)
to find them dynamically and assign them to variables.However, I've been forced to hand edit the previously converted MainWindow python file so that the Ui_MainWindow class is based on a QMainWindow widget rather than the generic 'object' class that Designer automatically create
Do not do this. It means you would lose all your work any time you make a change in Designer and need to run the pyuic again. Why did you feel you had to do so, other people work fine from Python or C++ with the way it works now with the helper class route?
P.S.
For your desired approach, there are ways of achieving it without you having to alter the generated.py
file, which you should never do. See the following:https://stackoverflow.com/a/70067108/489865
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self): super(MainWindow, self).__init__() self.setupUi(self)
The above uses multiple inheritance --- the
MainWindow
class is aQMainWindow
, but also inherits objects/methods from theUi_MainWindow
helper class. If you have designed a widget namedwidget
you will access it fromMainWindow
viaself.widget
. It is like the way you changed to, but without altering the generatedUi_MainWindow
class.https://stackoverflow.com/a/69625698/489865
class UI(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self)
The above uses encapsulation --- the
UI
class is aQMainWindow
, but it also contains aself.ui
member which is theUi_MainWindow
helper class, you can access its objects/methods viaself.ui
. If you have designed a widget namedwidget
you will access it fromMainWindow
viaself.ui.widget
. This is the way one does it from C++.wrote on 25 Apr 2023, 15:14 last edited by@JonB said in Make complete python script from Qt Designer:
class MainWindow(QMainWindow, Ui_MainWindow):
I thought it would be nice to have complete, "ready-to-use" modules that a simple top-level python script could run.
This key for me to understand is: "However I would not recommend it, you do not get any design-time support for the widgets you have added in Designer, and at run-time you do not get Python variables for those widgets ...". Why this wasn't clearly explained elsewhere is a terrible ommision and misdirection in multiple tutorials I've come across.
Using your recommendation, once the ui is designed by one person, another can write the rest of the application code without having to change the ui in Designer. It can be changed/customized by accessing the "Python variables for those widgets".
Thanks for your thorough response. This really should be "stickyed" somewhere on this forum in a easy to find place for all the newbies like me.
-
@JonB said in Make complete python script from Qt Designer:
class MainWindow(QMainWindow, Ui_MainWindow):
I thought it would be nice to have complete, "ready-to-use" modules that a simple top-level python script could run.
This key for me to understand is: "However I would not recommend it, you do not get any design-time support for the widgets you have added in Designer, and at run-time you do not get Python variables for those widgets ...". Why this wasn't clearly explained elsewhere is a terrible ommision and misdirection in multiple tutorials I've come across.
Using your recommendation, once the ui is designed by one person, another can write the rest of the application code without having to change the ui in Designer. It can be changed/customized by accessing the "Python variables for those widgets".
Thanks for your thorough response. This really should be "stickyed" somewhere on this forum in a easy to find place for all the newbies like me.
wrote on 25 Apr 2023, 15:24 last edited by JonB@pascor
When I did Python-Qt stuff a few years ago I used PyCharm as my IDE. Which I found very good. Certainly if you did elect to use thepyuic
/pyside6-uic
route of processing the.ui
file to produce a source file --- which the C++ build has to do --- rather than the run-timeQUiLoader::load()
you got invaluable development-time support like auto-completion of member variables etc. etc. It is true that the hassle of having to runpyuic
each time after changing the.ui
file was a minor irritant, but worth it for the other benefits one gained.I don't know what the editing/debugging experience for PySide inside Qt Creator is these days, but probably poor? There was no support at all in my time. Does it have an integrated Python debugger with your source code when you run? If not I would use PyCharm.
1/4