I'm trying to learn PySide for a project I'm working on. I'm working through the Zetcode tutorials, but from the very beginning I'm running problems. I am writing and running my code through Enthought's Canopy. When I run the code from the command line it works fine. This question may be related to my issue, however no answer is given there.
When I use the simplest code from the tutorial
import sys
from PySide import QtGui
wid = QtGui.QWidget()
wid.resize(250, 150)
wid.setWindowTitle('Simple')
wid.show()
everything runs correctly. The next example does more or less the same, except from an OOP perspective.
import sys
from PySide import QtGui
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Icon')
self.setWindowIcon(QtGui.QIcon('web.png'))
self.show()
def main():
ex = Example()
if __name__ == '__main__':
main()
When run the program flashes (I can see the window appear for a brief second) and then closes. Raising an exception before the end of main() will keep the window on the screen.
TL;DR
Why does putting the program in a class make it not work?
The difference between the two examples, is that first one keeps a reference to the widget as a global variable, whilst the second creates a local variable that gets garbage-collected when it goes out of scope (i.e. when the function returns).
The simplest way to fix this, is to make the ex variable global, like this:
def main():
global ex
ex = Example()
or you could just get rid of the main function, and simply do:
if __name__ == '__main__':
ex = Example()
The difference between running in and out of Canopy is that Canopy uses IPython QTConsole running, by default, in pylab mode with a QT GUI backend. The nice thing about this (and one of the many geniuses of ipython) is that you can have a live interaction between the command prompt and your GUI).
I suspect that you are bumping into pylab. A sophisticated program like Matplotlib can figure out whether the GUI event loop was already started, and adapt accordingly. But for your purpose, you probably just want to disable pylab mode, so that the IPython in Canopy acts more like a generic python. To do this, disable Pylab mode from the Canopy Preference menu (Python tab).
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.
This question already has answers here:
How to capture output of Python's interpreter and show in a Text widget?
(5 answers)
Closed 4 years ago.
I wrote a program to perform measurements and currently I launch it via the Spyder IDE. The program is written in Python 3.6.3. The GUI was made using PyQt5 and it should be the main focus of the user, but I also print() many informations in Spyder's console.
In preparation for switching to an .exe instead of a .py, since there will be no console anymore, I would like to add a LineEdit to my interface where all the printing would occur. Ideally it would display both my print()s and the various error messages generated during execution. How do I redirect those prints to a LineEdit?
Most of the information I found during my research was about making a LineEdit some kind of Windows cmd equivalent but examples were overkill compared to what I'm trying to do. Thanks in advance.
A quick and dirty method is to just redefine the builtin print function.
from PyQt5 import QtWidgets
IS_EXE = True # There is probably a way to detect this at runtime
class Window(QtWidgets.QPlainTextEdit):
def __init__(self):
super(Window, self).__init__()
self.setReadOnly(True)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
win = Window()
win.show()
# Replace the builtin print function
if IS_EXE:
print = win.appendPlainText
# Print some stuff
print('foo')
print('bar')
app.exec_()
I had a python console script that I wanted to add a basic status window to, so without knowing much about pyqt I added a window. If I started pyqt from my main thread, it blocked everything else, so I started it from another thread instead. It's been running fine like this for months, but I just noticed a warning (not sure how I missed it before):
WARNING: QApplication was not created in the main() thread. I'm wondering what problems this might cause.
This is a slimmed down version of the code I'm using, just updating the window titlebar:
from PyQt4 import QtGui, QtCore
import threading
import sys
from time import sleep
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
self.setWindowTitle(statusLine)
self.timer = QtCore.QBasicTimer()
self.timer.start(500, self)
def updateWindow(self):
self.setWindowTitle(statusLine)
def timerEvent(self, event):
if event.timerId() == self.timer.timerId():
self.updateWindow()
else:
super(MainWidget, self).timerEvent(event)
def startWindow():
app = QtGui.QApplication(sys.argv)
mw = MainWidget()
mw.show()
app.exec_()
if __name__ == '__main__':
global statusLine
statusLine = 'foo'
threadWindow = threading.Thread(target=startWindow)
threadWindow.start()
sleep(2) # process lots of data
statusLine = 'bar'
# keep doing stuff and updating statusLine
Edit: it looks like I don't get the warning with this simplified sample; instead, I seem to only get it if I start up multiple other python threads before the one that starts pyQt. However the question still stands: what's wrong with doing this?
I would say that since users interact with the GUI there is some danger that people kill the GUI without actually killing the main program, this can lead to:
Problems because another instance gets started leading to resource leakage, clashes, etc. &
Problems because the __main__ tries to update the GUI which no longer exists.
It seem to be generally considered best practice in programs with GUIs, whether QT or WX, to have the GUI as the __main__ and have child threads that do any background, computationally intensive, processing. Of course it is still a very good idea to explicitly kill any child threads in your OnExit method(s).
I have a QT4 and python 2.4 based GUI. The user starts using it by opening a file. In addition to explicit browsing, I would like to allow the user to specify a file to open as a command line argument. I am looking for some event in the QMainWindow (or wherever) that would allow me to detect when the application completed its initialization and is ready for user interaction at which point I could automatically open the file and populate the widgets. So far I could not find anything better than overriding showEvent which is not exactly it because the main window is still not visible at this point. It might be okay but I am looking for a proper way to do this. In some other UI toolkits that I have used in the past that would be something like “main form layout completed” event that would signal that the UI is safe to deal with. Is there something similar in QT4? I am running this on Linux if that matters.
You insights are greatly appreciated.
You don't need an event here. It is guaranteed that everything is loaded after __init__() and show() have run, so you can just put your code for the file opening after that.
import sys
from PyQt4 import QtGui
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.setupUI()
self.show()
# normal __init__ done
if len(sys.argv) > 1:
with open(sys.argv[1]) as f:
# do stuff with file
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())