I am new to PyQT and I have just started learning about it through this video: https://www.youtube.com/watch?v=JBME1ZyHiP8
When I ran the code on my Ubuntu 14.04
import sys
from PyQt4 import QtGui # Always have these two imports
app = QtGui.QApplication(sys.argv)
window = QtGui.QWidget()
window.setGeometry(50,50,500,300)
window.setWindowTitle("PyQt start")
window.show()
The window crated just flashes and closes down. How do I get the window
to stay so that I can interact with it? The code in the Youtube video
above demonstrated it on a Windows platform. Do I have to append anything Ubuntu specific to my code?
You aren't running the app, add this line to the end:
sys.exit(app.exec_())
From the relevant documentation:
int QApplication.exec_ ()
Enters the main event loop and waits until exit() is called, then returns the value that was set to exit() (which is 0 if exit() is called via quit()).
It is necessary to call this function to start event handling. The main event loop receives events from the window system and dispatches these to the application widgets.
Generally, no user interaction can take place before calling exec(). As a special case, modal widgets like QMessageBox can be used before calling exec(), because modal widgets call exec() to start a local event loop.
Related
Considering a very basic HelloWorld PyQt5 application like:
app = QApplication(sys.argv)
window = QWidget()
window.setWindowTitle('PyQt5 app')
window.setGeometry(100, 100, 280, 80)
window.move(60, 15)
helloMsg = QLabel('<h1>Hello World!</h1>', parent=window)
helloMsg.move(60, 15)
window.show()
sys.exit(app.exec_())
It constructs a QApplication, a parent-less QWidget becoming the main window, adds a QLabel and shows it.
My question is: how does the QApplication know about the main window?
There is nothing in this code connecting the two.
Perhaps it is a naive question but just looking at this, it seems like magic.
How is the main window's paint event added to the application's event queue without telling so in the source code ? How does the QApplication instance know what is going to be added below in the source code?
tl;dr
There's no "magic" involved: sub-modules can access their "main" modules, and each module of Qt can know if a QApplication instance is running.
Long version
I think that's an interesting question, especially for those who are not that into low level programming. For instance, I've always given the QApplication as some sort of a "cartesian" assumption: «it exists».
As a premise, I'm not going to give you a very technical and low-level explanation: I don't have enough skills to do so (and I really welcome any other answer or edit to this), but I'm assuming that's not what you're looking for.
[Almost] technically speaking, you've to remember that Qt - and PyQt along with it - is an environment (the exact term is framework). As such, each one of its sub elements (classes, and eventually instances of them) "know" about that environment.
QApplication (and its base classes QGuiApplication and QCoreApplication) is a class that is internally accessible from any "sub" Qt module.
It's something like the builtin types (str, int, bool, etc.) that are accessible to any module. For example, the os.path is a python module that you can import as standalone, but it knows what the main os module is, and each function of os.path actually uses some part of that main module.
Like most frameworks, Qt has what is called called an event loop, which is usually run as soon as you call Q[*]Application.exec(). An event loop is something that generally blocks itself waiting for something to happen (an event) and eventually react to it.
Whenever a Qt class needs it, it internally calls the Q[*]Application.instance() method to ensure that an instance of the application is running, meaning that an event loop is active and running. For example Qt widgets need that to be able to show the interface and interact with it: tell the operating system that a new window has been created, therefore it has to be drawn on the screen, so the OS will say "ok, let's show it" by sending Qt an event requesting the drawing, then Qt will "send" that event to that window that will finally draw itself by telling Qt how it's being painted; finally Qt will "tell" the OS what's going to be shown. At the same time, that window might need to know if some keyboard or mouse event has been sent to it and react in some way.
You can see this in the Qt sources: whenever a new QWidget is created, it ensures that a QApplication exists by calling QCoreApplication.instance().
The same happens for other Qt objects that require an application event loop running. This is the case of QTimer (that doesn't require a graphical interface, but has to interface with the system for correct timing) and QPixmap (which needs to know about the graphical environment to correctly show its image), but in some specific cases it also depends on the platform (for example, creation of a QIcon on MacOS requires a running event loop, while that's not necessary on Linux and Windows).
So, finally, that's what (roughly) happens when you run your code:
# create an application instance; at this point the loop is not "running"
# (but that might be enough to let know most classes about the current system
# environment, such as available desktop geometries or cursor position)
app = QApplication(sys.argv)
# create a widget; an application exists and the widget can "begin" to create its
# interface using the information provided by it, like the system default font
# (it's actually a bit more complicated due to cross-platform issues, but let's
# ignore those things now)
window = QWidget()
window.setWindowTitle('PyQt5 app')
window.setGeometry(100, 100, 280, 80)
window.move(60, 15)
helloMsg = QLabel('<h1>Hello World!</h1>', parent=window)
helloMsg.move(60, 15)
# "ask Qt to prepare" the window that is going to be shown; at this point the
# widget's window is not shown yet even if it's "flagged as shown" to Qt, meaning
# that "window.isVisible()" will return True even if it's not actually visible yet
window.show()
# start the event loop by running app.exec(); sys.exit will just "wait" for the
# application to return its value as soon as it actually exits, while in the
# meantime the "exec" function will run its loop almost as a "while True" cycle
# would do; at this point the loop will start telling the OS that a new window
# has to be mapped and wait from the system to tell what to do: it will probably
# "answer" that it's ok to show that window, then Qt will tell back the widget
# that it can go on by "polishing" (use the current style and app info to finally
# "fix" its size) and begin drawing itself, then Qt will give back those drawing
# information allowing the OS to actually "paint" it on the screen; then it will
# be probably waiting for some user (keyboard/mouse) interaction, but the event
# loop might also tell the OS that the window is willing to close itself (as a
# consequence of a QTimer calling "widget.close", for instance) which could
# possibly end with ending the whole event loop, which is the case of
# https://doc.qt.io/qt-5/qguiapplication.html#quitOnLastWindowClosed-prop
# which would also cause the application to, finally, return "0" to sys.exit()
sys.exit(app.exec_())
I am trying to write a function that launches a popup in a kivy gui based on a conditional. The method is outside of the main app because I am using multithreading to have both run at the same time. Every time I try to initialize a new popup in the method which is outside of the main app, it crashes. If I don't initialize it, it runs fine. Any ideas?
You must perform GUI related operations (like opening or dismissing a Popup) on the kivy thread.
In your code, where you would call sm.open_unrecognized() do Clock.schedule_once(sm.open_unrecognized, 0). This will schedule the call to open_unrecognized on the kivy thread after the next frame is displayed.
Dismissing the Popup can be done automatically (any click outside the Popup) or explicitly using something like a Button in the Popup. Note that any code called by a Button event is performed on the kivy thread.
See the Clock documentation for more information
I have been trying to use tkinter to make a gui to select some excel files and sheets from those files.
I have a lot of experience in python, but I'd probably say a novice at tkinter.
The code I have written to select the files is shown below (typos are likely because I cannot access the internet on the machine these files are on, so I am typing it up here).
My question is about mainloop(), the update functions, and after(). I had mainloop() at the end of my code, but my program wouldn't terminate (i.e. the terminal would remain) after it did what it does. So I removed the mainloop() and now it functions perfectly without any mainloop(), update(), or after() calls.
I don't really understand this and would really like to. I get that mainloop() stops the code from progressing until the root closes, but I thought nothing would show up without mainloop(), and this code does wait for the user to close the windows before continuing.
I was also wondering if I do not have mainloop (or the like), the code still closes fine whether or not I have root.destroy() at the end of the App class (commented in the code). I don't get that either.
This information would help me make better and correct code in the future and hopefully improve this one. FYI, I have searched for the answer to this everywhere and could not find one, at least one I could understand. Thanks a bunch.
This is as minimal as I can think of for this code.
Code (Edited from original post):
import tkinter as tk
from tkinter import ttk
class App:
def __init__(self, parent):
self.root = parent
self.root.withdraw()
tl = tk.Toplevel(parent)
b = ttk.Button(tl, text="Test widget")
b.grid()
tl.wait_window()
# This line does not change how the code functions at all
#self.root.destroy()
def run(self):
# Whether or not this is a function in the class or called globally doesn't matter.
self.root.mainloop()
if __name__ == "__main__":
root = tk.Tk()
app = App(root)
# This is the mainloop() call. If I include it, the program does not close after
# running the code in App.__init__. Without it, it runs perfectly.
# app.run()
mainloop enters an event listening loop for your tk.Tk() application.
You should create only one object with Tk() in every tkinter application.
Now this loop will "globally" listen for GUI events and will call your registered callbacks (event handlers). Code before the mainloop call will run but nothing will be shown or updated in the screen until you call it. No event (user input, mouse movement, keyboard and others) will be queued and responded to before that. But the widgets are created and configured, just not shown yet. If you call root.update() without ever entering the mainloop you will eventually see the window flash and disappear because everything is running, and you force window refresh, but then suddenly the program ends.
So configure your GUI and in the end always run root.mainloop()
Now when you call wait_window, you are creating a local event loop (and blocking the mainloop if it was running). For a perfect explanation of wait_window by Brian see here
What happened to you is that your app is happily running with your local event loop, but without mainloop. That would be a strange thing to do. Even if you are making as Brian explains, a modal window, you want to "return" to the mainloop after the window closes.
As for the after method it just registers a callback function to run after your choosen time, or after the mainloop becames idle, with after_idle (nothing more to refresh, no events in the queue..., also think as soon as possible). And if you want to re-run that function again in the same way it should re-register with after or after_idle before returning.
So always use your mainloop() ending call - or should I say event loop starting call :)
Edit:
Also, don't do things in your code that are slow, or that may block the loops, such as calling sleep or any other blocking function. That will freeze the GUI, until the block ends.
Tkinter method "deiconify" seems not working on ubuntu(12.04, unity), but the the following code works as expected on windows 7. I need to show the window even it is minimized when something happens in another project https://github.com/thinker3/youdao.
from time import sleep
from Tkinter import Tk
class GUI():
def __init__(self):
self.root = Tk()
self.root.title("Test")
self.root.protocol("WM_DELETE_WINDOW", self.close_handler)
self.root.mainloop()
def close_handler(self):
self.root.iconify()
sleep(1)
self.root.deiconify()
if __name__ == '__main__':
gui = GUI()
What you are seeing is apparently the difference between Windows and non-Windows. It looks to me like the Windows behavior is a bug. At the very least, it's not expected behavior. What you are seeing on ubuntu is what I would expect to see.
For a GUI to do anything, the event loop must be running. Everything that happens is the response to an event. The drawing of a window on the screen is a response to an event. A button click is a response to an event. The visual effect of a button being pressed is a response to an event. And so on.
When you call iconify, that causes an event to be sent to the app saying "remove the window from the screen". The event loop sees the event, and redraws (or "un"draws) the window. The reverse happens when you call deiconify -- the system gets a redraw event, and tkinter redraws the window on the screen.
In your code, you never give the event loop a chance to respond to these events. You ask it to iconfiy, then you sleep, then you deiconify, all without giving the event loop a chance to respond. While the sleep command is running no events are processed. So, when you wake from sleep, the system hides the window, and then microseconds later it redraws it.
What is probably happening on windows is that the window manager is getting the iconfiy command and removing the window without tkinter's involvement. In other words, tkinter doesn't actually do anything to make it go away. On X11-based systems, however, this is all managed by the event loop.
If you want the window to go away, and a second later reappear, use the event loop to your advantage. Allow the event loop to update the screen, and arrange for the deiconify to happen at sometime in the future. For example:
def close_handler(self):
self.root.iconify()
self.after(1000, self.root.deiconify)
This should work on all platforms. It allows the event loop to respond to the iconify event, and then a second later the deiconify command will run.
I am testing a gui built using PyQt and I would like the ability to interact with the gui using python code that is executed after the PyQt event loop starts (app.exec_()). Another way of saying this is I would like the call to app.exec_ to return immediately as if the gui were modeless, followed by further python code which interacts with the gui.
I found this example of running the PyQt loop in a thread but don't want to do something so unconventional. Is there any way to get the PyQt message loop to continue processing messages while also executing python code in the main thread after exec_ has been called?
One option here is to use a QtCore.QTimer.singleShot() call to start your python code after calling `exec_()'.
For example:
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
# Setup the GUI.
gui = MyGui()
gui.showMainWindow()
# Post a call to your python code.
QtCore.QTimer.singleShot(1000, somePythonFunction)
sys.exit(app.exec_())
This will execute the function somePythonFunction() after 1 second. You can set the time to zero to have the function added immediately queued for execution.
As a possible easy answer, try not calling app.exec_() in your script and running your PyQt program using python -i My_PyQt_app.py.
For example:
## My_PyQt_app.py
import sys
from PyQt5.QtWidgets import QApplication, QWidget
app = QApplication(sys.argv)
window = QWidget()
window.show()
# Don't start the event loop as you would do normally!
# app.exec_()
Doing this should allow you to run the GUI through the terminal and interact with it in the command line.
I got it. I can execute the test script line-by-line from the main thread using exec and then run the gui from a worker thread.
Not exactly sure what you wanna do. Are you looking for something like Py(known as PyCrust) for PyQt?
The easiest way is to use IPython:
ipython --gui=qt4
See ipython --help or the online documentation for more options (e.g. gtk, tk, etc).