Can someone explain to me the difference between the following two code examples? Why is the top one not working? It executes without error, but the window doesn't stay open.
from PyQt4 import QtGui
import sys
app = QtGui.QApplication(sys.argv)
QtGui.QMainWindow().show()
app.exec_()
and:
from PyQt4 import QtGui
import sys
app = QtGui.QApplication(sys.argv)
win = QtGui.QMainWindow()
win.show()
app.exec_()
In QtGui.QMainWindow().show() you are creating an object of QMainWindow and you are showing it. But you do no save that instance of the QMainWindow in your memory. So eventually python's garbage collection deletes that instance and your QMainWindow no longer shows.
In the second code: win = QtGui.QMainWindow() you save the object instance of QMainWindow to win in your memory. Python does not consider that as garbage because it is in use and hence your window stays open
Related
I created UI using Qt Designer. Then I converted the ui file to a .py file (pyuic -x) - it works fine if launched directly. Then I tried to subclass my ui in a separate file to implement additional logic. And this is where things start to go wrong. Inheriting from QMainWindow and my Qt Designer file works OK with no issues, as expected. However, the moment I set any WindowFlag for my QMainWindow (any flag - I tried these: StaysOnTop, FramelessWindowHint) and run the file, the window appears and instantly disappears. The program continues to run in a console window, or as a PyCharm process, but the window is gone. It looks to me like it is getting out of scope - but why setting a simple flag would make any difference to the garbage collector? Could someone explain this behaviour?
Minimum code required to reproduce this phenomenon:
from ui import Ui_MainWindow
from PyQt5 import QtCore, QtWidgets, QtGui
import sys
class Logic(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.setupUi(self)
self.show()
# self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
# self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_NoSystemBackground, True)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Logic()
sys.exit(app.exec_())
The window should appear and stay on the screen until one (or more) of the flags are uncommented. I use Python 3.8 (32-bit) with PyQt5. Run environment provided by PyCharm. Windows 10.
From the documentation of setWindowFlags():
Note: This function calls setParent() when changing the flags for a window, causing the widget to be hidden. You must call show() to make the widget visible again..
So, just move self.show() after setting the flags, or call it from outside the __init__ (after the instance is created), which is the most common and suggested way to do so, as it's considered good practice to show a widget only after it has been instanciated.
Don't critisize me using different classes - the reasons for his is becausethere will be more GUIs in my project created by the QtDesigner, but this should not be important for now.
In general, I have two Python scripts:
main.py:
from PyQt5 import QtCore, QtWidgets, QtGui
import sys
import time
from gui_class import Gui
app = QtWidgets.QApplication(sys.argv)
gui = Gui()
sys.exit(app.exec_())
gui_class.py:
from PyQt5 import QtWidgets
class Gui():
def __init__(self):
w = QtWidgets.QWidget()
w.resize(500, 500)
self.button = QtWidgets.QPushButton(w)
self.button.setGeometry(100, 100, 300, 300)
w.show()
If I run the main.py-script, then the window appears for a split second and disappears right away. I can't see it, I can't click it. The code does not terminate, though. It's still waiting for the application to finish - I can't do anything, though.
If I put a breakpoint before the line saying w.show() in the gui_class.py and simply continue the code after it stopped in that line, then the GUI is visible and I can click the button and the code terminates after I close the window - everything works as expected.
I am using PyQt5: 5.15.2 with Python3.7.
The problem is that w is a local variable that will be destroyed when the scope where it was created finishes executing. The solution is to extend its life cycle by increasing its scope by making it an attribute of the class, for this you must change w with self.w.
How QApplication() and QWidget() are connected?
This is an example code that I copied, it creates QApplication object and QWidget object, but there is no link between the two objects. I expected something like app.setWidget(did) to teach PySide/PyQt controller about the widget that was created.
# http://zetcode.com/gui/pysidetutorial/firstprograms/
# 1. PySide.QtGui is the class
import sys
from PySide import QtGui
# 2. setup the application
app = QtGui.QApplication(sys.argv)
# 3. create the widget and setup
wid = QtGui.QWidget()
wid.resize(250, 150)
wid.setWindowTitle('Simple')
# 4. Show the widget
wid.show()
# 5. execute the app
sys.exit(app.exec_())
What's the magic behind this?
QApplication is a singleton so it would be pretty easy, for QWidget to do: QApplication.instance() and interact with the QApplication instance.
In fact trying to instantiate QWidget before the QApplication leads to an error:
>>> QtGui.QWidget()
QWidget: Must construct a QApplication before a QPaintDevice
Which probably means this is what happens.
Edit: I've downloaded the qt sources and in fact, in src/gui/kernel/qwidget.cpp, line 328, there is:
if (!qApp) {
qFatal("QWidget: Must construct a QApplication before a QPaintDevice");
return;
}
Where qApp is a pointer to the QApplication instance(i.e. it is equivalent to calling QApplication.instance()).
So, in the end, the QWidget interacts with the QApplication via a global variable, even though it isn't necessary. They probably use qApp instead of QApplication.instance() to avoid unnecessary overhead that might happen when creating/destroying many QWidgets.
I have been experimenting around with using QT in python3 and figured out how to make a simple .ui file and import it directly into a python program. However I have to use python QT app while I would like to have my own custom loop in order to be able to add things to it. So what I have now is:
import sys
from PyQt4 import QtCore, QtGui, uic
class MyWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.desktop = uic.loadUi('mytest.ui')
self.desktop.show()
if __name__ == '__main__':
app = QtGui.QApplication.instance()
window = MyWindow()
sys.exit(app.exec_())
I've played around a bit with PGU where I have been able to do this with what would be
while True:
window.loop()
in the main which allows me to put in my own extra code that is unrelated to the GUI without dealing with multiple threads. However I have been unable find some equivalent to "loop" in qt and searching for "qt custom loop/update/blit python" hasn't found anything relevant besides suggestions to add a 0 second timer to my app and place extra code there which seems... inelegant to me. I would like to import a GUI into my app rather then building my app around a GUI.
Edit: Here is my updated code taking Phyatt's answer into account for anyone else looking for the same answer I was.
import sys
from PyQt4 import QtGui, QtCore, uic
class TestApp(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = uic.loadUi('mytest.ui')
self.ui.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
win = TestApp()
app.processEvents()
while True:
app.processEvents()
Instead of using your own custom loop, the more common way to do that is to have a timer event scheduled on the Qt loop, or to launch another thread.
If you still really wanted to use your own loop, you need to manually call QApplication.processEvents() in your loop and it probably should work.
http://qt-project.org/doc/qt-4.8/qcoreapplication.html#processEvents
http://doc.qt.io/qt-5/qtimerevent.html#details
MyWindow.start(100) # start the built in timer event for that QObject 10 times a second
And then in MyWindow put def timerEvent and it should work.
Hope that helps.
So I'm quit new to working with UI in python. I'm not really grasping a core concept and i think this simple question will help flip on the light switch.
As seen in the code snippet below, I imported a ui file made in Qt. This ui has a pushbutton on it. How do I make a click event on that button? I have gone through tutorials on how to code a button and use it. I understand that. It is the question of how to access the objects and manipulate the objects that are created by the ui file. What i really want to do is see how to perform a function (or instantiate a class or whatever) when a button is clicked. that function being one that i wrote. baby steps though. any answers and elaborations would be appreciated.
import sys
from PyQt4 import QtGui, uic, QtCore
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
uic.loadUi('myWidget.ui', self)
self.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MyWindow()
sys.exit(app.exec_())
like i said. very simple question but I'm just not really grasping the core concept here. thanks you.
self.ui=uic.loadUi('curveViewer.ui', self)
#where `your_pushbutton` is the button name specified in your .ui file:
self.ui.your_pushbutton.clicked.connect(self.onBtnClicked)
or just:
uic.loadUi('curveViewer.ui', self)
self.your_pushbutton.clicked.connect(self.onBtnClicked)
then define method onBtnClicked inside your class MyWindow:
def onBtnClicked():
print 'pushbutton clicked'
see New-style Signal and Slot Support
btw, it's better to remove self.show(), and make it:
window = MyWindow()
window.show()