Solved Load and Unload an instance of Qt Form
-
Is this possible in PyQt? - I would like to load on demand (called by a string reference) a Qt form with PyQt code into a main form, retaining a separate namespace for the instance of the form and cleanly unload the form when closed by user.
Principally the same thing how a Form in winform solution works. Each form there is stored in separate file(s) (code, form definition and designer file - in PyQt it would be a single file), can be loaded with
Dim frm as New MyForm
andfrm.Show()
and closed withMe.Close()
. Within the instance of the form a local namespace works, ensuring the main application doesn't get cluttered and instances mixed. If needed, one can drill into parent's namespace.I found close to nothing about the subject during my very extensive research. So either I'm so green in Python and it's so obvious that no one needs to ask, or it's rare to ask this from a PyQt application or it's too much to ask from Python by it's definition. Please don't stone me, if it's an apparent thing.
I did some tests myself loading the forms as standard python modules, but got into issues with main window name space cluttering, broken signals when more instances of one form existed and I don't think I really got the garbage collector to clean up stuff when the UI part of the module was
.close()
d. I'm not including any code for wouldn't provide much lead, if any. -
@Oak77
To start, have you read https://stackoverflow.com/questions/52471705/why-in-pyqt5-should-i-use-pyuic5-and-not-uic-loaduimy-ui and the solution there with its references?You can
uic.loadUi()
if you want to go down the dynamic route. I prefer the thepyuic5
route of getting a class. The resulting instance can be shown & closed in the same way as the VB.I don't quite what you mean about namespace etc. stuff. There is no clashing of variable/class names.
Unless there is something unsatisfactory with these routes I don't see what further you would want to achieve.
-
@JonB Thank you very much, it is well possible, that you directing me to this post is all I needed. I somehow missed this particular post. I've seen some examples (very few) of loading via uic, but it seemed to me it's purpose different. I will study both in detail.
Once gain, thank you very much! -
@JonB I looked into the two utilities and realized I've used both of them in course of my learning and getting familiar with PyQt. In the 2nd case,
uic.loadUi()
loads an XML definition of the form made by QtDesigner and I don't think it's possible/meant to be bundled with working code.In the first case,
pyuic5
is interesting. It converts XML into a python class with Qt definitions. In theory, it would load any proper python UI class, regardless if it's made by user or QtDesigner.
So I could imagine a way to make the module this way. But I am lost as for:The resulting instance can be shown & closed in the same way as the VB.
I'm sorry, perhaps I ask for something very obvious, but could you please point me at the right direction - how do I unload the module? I couldn't find anything about it
pyuic5
and unloading. -
and I don't think it's possible/meant to be bundled with working code
No idea what you mean here. The
loadUI()
route is perfectly acceptable for "working" code, if you want to supply the.ui
file at run-time and load it dynamically.With
pyuic5
you do not supply the.ui
file at run-time, but instead you have generated a.py
code file off it and you supply that instead.There isn't any "unloading", just closing. But there isn't any "unloading" in that sense in the VB code you show either. You asked for
Dim frm as New MyForm and frm.Show() and closed with Me.Close()
and that's what either way would do, with the
pyuic5
route being closer as it allows the new class as inDim frm as New MyForm
.There are things you can do in Python not available in C++ to "remove" code/classes at runtime. But I think you are getting hung up on this. Why do you keep talking about "how do I unload the module?" when there is no need to try to do this? Just create your widgets, show & close them. Let your Python references to forms go our of scope, or
Nothing
them, and maybe consider usingQt::WA_DeleteOnClose
(https://doc.qt.io/qt-5/qt.html#WidgetAttribute-enum) on, say, your forms/dialogs to ensure they get deleted at the Qt side. -
@JonB I could have been wrong in understanding how the things work in PyQt. I know as a fact that the described process in VB will cleanly dispose the loaded module. Users easily open and close hundreds of modules per working day and the RAM usage never spirals up. It would be of course beautiful if
close()
would do the same.I'll have to study pyuic5 and loading, no doubt. I'll start with roughly this scenario:
- Define sample UI file for a module
- Generate python UI class
- Add working code into that python UI class, such as loading data from database upon selection and button click or drawing into canvas, etc.
- Define
self.dw1 = QDockWidget
pyuic5.loadUI(myUIclass.py, self.dw1)
- I'm not sure if the syntaxe is right, but I will try to load the UI into theQDockWidget
- Test if code in module is working
- Close the module (i.e. by close button of the
QDockWidget
and see if RAM doesn't bloat, when the module(s) are opened and closed many times and/or use debugger to monitor the status
I'd be pleased if you could confirm if those steps sound roughly correct.
-
@Oak77 said in Load and Unload an instance of Qt Form:
VB will cleanly dispose the loaded module.
But it doesn't "dispose the loaded module", at least not the VB code you show. It disposes the instance you create (
Dim frm as New MyForm
), not the code/module itself. Python will release the instance when you no longer retain any references to it, as with all things Python.I still don't see why on the one hand you say "Generate python UI class", i.e. using
pyuic5
, and then still you are usingpyuic5.loadUI()
. I refer you again to https://stackoverflow.com/questions/52471705/why-in-pyqt5-should-i-use-pyuic5-and-not-uic-loaduimy-ui.pyuic5
-generate-class vsloadUI()
are alternatives. What's wrong with, say, the very first example approach at https://www.riverbankcomputing.com/static/Docs/PyQt5/designer.html ?At this point I suggest you create some minimal code for what you want to do, and see how that goes.
-
I did mean the instance, but I didn't realize all the details and consequences. I think you're right and put that way, it really makes sense.
...
pyuic5
-generate-class vsloadUI()
are alternatives.Oh, I see, I mixed the two together. I meant actually only loading/importing the resulting python UI class.
What's wrong with, say, the very first example approach at https://www.riverbankcomputing.com/static/Docs/PyQt5/designer.html ?
What I meant is, that the XML doesn't bear any python code, just UI definitions. But perhaps I could import a module which in turn loads the UI file directly, to keep it clean.
I apologize for my confused questions and thank you very much for helping me. You explained it very well. I'll read the relevant documentation again and test all sugested scenarios.
-
@Oak77 said in Load and Unload an instance of Qt Form:
What I meant is, that the XML doesn't bear any python code, just UI definitions. But perhaps I could import a module which in turn loads the UI file directly, to keep it clean.
That is what
pyuic5
, the way I'm recommending, does! You runpyuic5
on your.ui
file (during developemnt) and it creates a.py
file of Python code implementing all of the UI in a Python class, ready for you to use viaimport
, as per e.g. the first example on that linked page. That is the approach most people take from C++.