Note how this application implements a mainloop by the activate signal and .run() method call:
import gi
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk
def on_activate(app):
win = Gtk.ApplicationWindow(application=app)
btn = Gtk.Button(label="Hello, World!")
btn.connect('clicked', lambda x: win.close())
win.set_child(btn)
win.present()
app = Gtk.Application(application_id='org.gtk.Example')
app.connect('activate', on_activate)
app.run(None)
Correct me: I think this activate signal is emitted as an event to synchronise the creation of GTK windows, widgets, etc, so that the GUI part does not race to happen before the application is ready. What else is the point?
Then, when the Window, widgets, etc are all drawn, the application gets stuck in a mainloop by .run(). This way the application doesn't exit immediately, but rather waits for user input.
What bothers me here is that I don't see the value of the Application class. Because of it, we got an event on signal activate to tell "oh the app is ready", but what is this app anyway? Why can't I just do .run() on Gtk.ApplicationWindow or even Gtk.Window?
In order to answer those questions above, I invented/discovered this question that, if answered, I think it will address my frustration:
Question: What is the GT4 approach in running GUI apps without using Application or ApplicationWindow classes?
Background
I come from a TKinter background. There, I could make an app simply by saying something like:
import tkinter
class MyApp(tkinter.Tk):
def __init__(self):
super().__init__()
self.title('some title')
if __name__ == '__main__':
myapp = MyApp()
myapp.mainloop()
It feels much simpler, without a spaghetti of creating two objects (like in GTK4) with signals emitting through them just to barely get a .run() mainloop.
But I have to use GTK4 for this project, and Tkinter is not an option. So I have to learn the suitable GTK4 for me.
According to the docs: "GtkApplication is a high-level API for writing applications.
It supports many aspects of writing a GTK application in a convenient fashion, without enforcing a one-size-fits-all model.
Currently, GtkApplication handles GTK initialization, application uniqueness, session management, provides some basic scriptability and desktop shell integration by exporting actions and menus and manages a list of toplevel windows whose life-cycle is automatically tied to the life-cycle of your application.
While GtkApplication works fine with plain GtkWindows, it is recommended to use it together with GtkApplicationWindow."
Source: https://docs.gtk.org/gtk4/class.Application.html
The clasic hello world from Gtk3 will not work on Gtk4
This works:
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
win = Gtk.Window()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
But if you change "3.0" by "4.0" does not work and the reason is on the migration guide:
"GTK 4 removes the gtk_main_* family of APIs. The recommended replacement is GtkApplication"
https://docs.gtk.org/gtk4/migrating-3to4.html#stop-using-gtk_main-and-related-apis
Gtk have a different approach to Tk, you will need to be used to that.
And GTK4 is still new, you will found more examples about Gtk3 code, so, keep the migration guide at hand
Related
I am writing an application that has to react to system wide keypresses on Mac OS X.
So I found some key logger examples that should work and hit a wall, because all examples are based on NSSharedApplication() and PyObjC AppHelper.runEventLoop() while my application is written in wxPython.
Here I post a modification of the simplest example from https://github.com/ljos
that I thought it should work. But it does not.
from AppKit import *
import wx
class AppDelegate(NSObject):
def applicationDidFinishLaunching_(self, aNotification):
NSEvent.addGlobalMonitorForEventsMatchingMask_handler_(NSKeyDownMask, handler)
def handler(event):
print (u"%#", event)
app = wx.App()
delegate = AppDelegate.alloc().init()
NSApp().setDelegate_(delegate)
app.MainLoop()
It is obvious that the MainLoop() doesn't catch the delegated NSEvents.
After app = wx.App() the NSApp() is returned correctly. So why doesn't this work? How do I make it work?
As nobody answered I went searching around with different angle in view.
So I discovered that Quartz module can be used to get to keyboard and mouse events. No custom loop needed, therefore wx.App() and wx.App.MainLoop() aren't getting in the way.
I also found a nice package named pynput that does just that for me, thus sparing me plenty of time. Quartz is pretty complicated, a lot of scrambled names for functions and constants. But it does a good job.
I am writing a program on Python 3.4 that uses GTK Map widget (OsmGpsMap) and pygame for joystick input.
Here is a simple program that displays a window with a map:
import gi
from gi.repository import Gtk
from gi.repository import OsmGpsMap
window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
window.connect('destroy', Gtk.main_quit)
map_widget = OsmGpsMap.Map()
window.add(map_widget)
window.show_all()
Gtk.main()
But if I just add a
import pygame
line (without pygame.init() and etc.), the program doesnt launch and I receive a message that python.exe have stopped.
So, how is it possible to fix that?
Do not mix frameworks. The frameworks may interact poorly with each other or conflict completely. If it works on your (operating) system, that doesn't mean it will work on another (operating) system or with a different version of one of the frameworks. Mixing frameworks always means some kind of undefined behavior.
Pygame is based on SDL2, so there is no chance that PyGame can display an osmgpsmap which is a Gtk+ widget.
I have just started learning about creating GUI apps in Python. I decided to use Gtk version 3.
According to the (official?) tutorial on http://python-gtk-3-tutorial.readthedocs.org/ the proper way of building a hello world application is:
from gi.repository import Gtk
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.set_default_size(200, 100)
self.connect('destroy', Gtk.main_quit)
self.show_all()
MyWindow()
Gtk.main()
In other tutorial (http://www.micahcarrick.com/gtk3-python-hello-world.html) I found completly different aproach which is:
from gi.repository import Gtk, Gio
class HelloWorldApp(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self, application_id="apps.test.helloworld",
flags=Gio.ApplicationFlags.FLAGS_NONE)
self.connect("activate", self.on_activate)
def on_activate(self, data=None):
window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL)
window.set_title("Gtk3 Python Example")
window.set_border_width(24)
label = Gtk.Label("Hello World!")
window.add(label)
window.show_all()
self.add_window(window)
if __name__ == "__main__":
app = HelloWorldApp()
app.run(None)
Could someone experienced in this category tell me in what way should I write Gtk 3 apps in python these days? I'm already familiar with writing GUIs (spent few months in Java's Swing) so you can go on with terms like events, callbacks and so on..
Choosing to write your new program with a GtkApplication or just a GtkWindow depends on the functionality you require, and to some extent the intended audience.
For most cases, especially when you are still learning the toolkit, I would tend to agree with valmynd that GtkApplication is unnecessarily complicated. GtkApplication provides a lot of extra functionality that you probably don't want or need in smaller applications.
For larger, more complete applications I agree with Dragnucs, the second approach is superior and can provide better integration into the desktop environment. From GNOME Goal: Port to GtkApplication (see also the GtkApplication docs):
Porting your application to use GtkApplication has quite nice benefits:
GtkApplication handles GTK+ initialization, application uniqueness, session management, provides some basic scriptability and desktop shell integration by exporting actions and menus and manages a list of toplevel windows whose life-cycle is automatically tied to the life-cycle of your application.
However I disagree with Dragnucs about why the GtkWindow approach is introduced in the tutorial. I tend to think simple examples with very little boilerplate are more appropriate for a tutorials Getting Started section (but, I do think that the read-the-docs tutorial needs to be updated to include at least some mention of GtkApplication).
In applications I've written I tend to either subclass both GtkApplication and GtkWindow or for single window quick-and-nasty applications just subclass GtkWindow. Your decision will depend on your applications needs.
Technical difference: There is also an important technical difference between how the two examples are implemented. The example with just a GtkWindow creates a new Gtk main loop for each instance of the program. The example with GtkApplication creates a single main loop attached to the first instance and each subsequent call to run(None) will request that the original instance create a new window (the new instance will then exit). Try opening up two terminals and running your application in each window, notice that one terminal will wait until all the windows have closed before becoming sensitive again. You can change this behaviour by using G_APPLICATION_NON_UNIQUE instead of G_APPLICATION_FLAGS_NONE.
The second code example looks unnecessary complicated to me, the first looks perfectly fine. The author of that second tutorial has put a link to another, even more simple example (Source):
from gi.repository import Gtk
window = Gtk.Window(title="Hello World")
window.connect("destroy", lambda w: Gtk.main_quit())
window.add(Gtk.Label("Hello World!"))
window.show_all()
Gtk.main()
There is nothing wrong with either approaches. You can use all the default widgets, not subclassing anything, like in the example above. Or you can subclass certain widgets, mainly as a way to give your code a nice structure and having re-usable custom/modified widgets in the end. That is all up to you.
The same applies to Qt and many other GUI frameworks, btw.
The second approach is better. It makes the application more integrated into the desktop and informs more about what it does or is meant to do. It also provides you with more tools to use with your application.
I think the first approach is just not up to date or something. The second one is really the preferred way.
You can see that the application Gnome-music is actually using the second approach with Gtk.Application usage. All the official Gnome apps are using Gtk.Application, and all Gtk application should be using it too.
well maybe crazy is a bit too strong of a word, but what I am asking is if there are any window toolkits out there that don't have me do this:
class MyApp(SomeWindowClass):
I really don't want to use a library made by someone who is so obsessed with objects that he/she thinks that there should be a class for the app (which there will only be one instance of, so I don't see why anyone would want to do that extra typing)
(btw, no offense intended towards anyone who agrees with the way these libraries are set up, I just really want to know if there is anything out there with a tad bit less objects)
In general GUI toolkits rely on having some form of event loop running, the Application class in these toolkits is generally in charge of that event loop and marshaling events from the underlying window manager. Sure they could call the class EventLoopManager or something, but you need it either way so its just a naming thing then. In some cases though some toolkits who often use events can occasionally be used without them, and then you certainly dont want it to be some automatic thing.
There is PyQT.
Tkinter has one object per window/dialog, not app, and requires no classes to get something painted on the screen. It does, however, have its own main loop (like all the other GUI libraries). Obligatory Hello World:
from Tkinter import *
root = Tk()
w = Label(root, text="Hello, world!")
w.pack()
root.mainloop()
PyGTK another toolkit; which is python binding of Gtk. It is well structured with having excellent window and event loop system.
A typical example to show a window
import gtk
class Application:
def __init__(self):
self.window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
self.window.show()
if __name__ == "__main__":
app = Application()
gtk.mainloop()
Another recommendation is PyQt; which is python binding of Qt.
Typical hello world example is
import sys
from qt import *
app = QApplication(sys.argv)
myLabel = QLabel("Hello world!", None)
app.setMainWidget(myLabel)
myLabel.show()
app.exec_loop()
PyQt and PyGtk are widely used for rapid GUI development. From my experience pyGtk is poor in the online documentation/support compared to pyQt. But both are favorite of mine.
As you see with the answers above, GUI programming is almost always heavily object oriented, and there are good reasons for this: the graphical elements share a lot in terms of how they can be positioned within one-another, caring about whether the mouse pointer is over them etc. Furthermore, the C++ kits that qt, wx, gtk et al. wrap are already structured on a class/inheritance hierarchy, so you should not be surprised that the python wrappers are also.
If you want only simple GUI elements, then you may consider easyGUI (simple message boxes, text edit, choices), triatsUI (interactive object wrappers, primarily for controlling graphical objects) , which each solve some part of the GUI interactions without explicitly having you write GUI code.
For editing the values of fields in a record-like structure, you could also investigate GUIdata.
PS: there are various graphical tools out there to let you design your GUIs and link together some of the events, e.g., QtDesigner, that can help you avoid much of the tedious class definition code.
I'm newbie in python and I'm trying to use pyinotify with a GUI interface using pygtk. I have two classes, my gtk class which doesn't do much, only displays stuff, and a class that handles the monitoring.
When I run them separately they do their work but when I try to load the gtk class from the other one, it only runs the gtk class, the monitor class only runs when I exit the GUI. If I comment the last line the monitor runs fine. I think the "myGTK.main" is blocking the whole process. Is there anything I can do?
pyinotify.ProcessEvent.__init__(self)
self.notifier = pyinotify.ThreadedNotifier(self.watch_manager, self)
self.watch_manager.add_watch('/test', pyinotify.IN_CREATE, rec=True)
self.notifier.start()
self.myGTK.main()
Thanks.
Per the comments, the solution appears to be to add
gobject.threads_init()
near the top of the script. This, and other useful information about using threads with pygtk can be found in this faq.