Some notes on porting from PyGTK to PyGObject
These are some notes I wrote as porting my on-again off-again hobby project Basketball GM from PyGTK to PyGObject. I did this because PyGTK is dead and stuck on GTK+ 2, and PyGObject is the future and already on GTK+ 3 through the use of GObject introspection. So, others going through the same transition might (or might not) find this useful. You can see the code I'm referring to on the pygobject branch on GitHub.
Based on what the documentation told me me, I ran pygi-convert.sh
on my code. I didn't expect this to work perfectly, but at least it did produce something that ran (i.e. created the main window).
However, there were tons of bugs with the functionality and a ton of error messages. Here is a probably incomplete list of things I did to fix those problems:
Gtk.main_iteration
no longer takes any arguments. Removing them seems to fix the error with no consequences. I probably didn't need to be messing with the arguments there to begin with.I had to manually set the "Show text" parameter of my
Gtk.ProgressBar
to "Yes" in Glade to get text to display on top of myGtk.ProgressBar
. I guess the default setting changed?gtk.Tooltips
was previously deprecated (which I did not know..), but now it's totally gone and replaced byGtk.Tooltip
. If I had been using thegtk.Tooltip
API to begin with, as I should have been, this wouldn't have been an issuegtk.ComboBox.get_active_text
is gone, so I worked around that by usingGtk.ComboBox.get_active_iter
, which seems more convoluted, but whatever.If you tell a
Gtk.ListStore
it's getting anint
, it only wants anint
. It won't take afloat
and do the best it can like it used to. This is good because it helped me find an obvious typo in my SQL schema. But it's bad because because SQLite'sTOTAL
function will return aFLOAT
even if you call it on anINTEGER
column. This is especially annoying as I have some convenience functions to handle the boilerplate forTreeView
s which relied on the old behavior from PyGTK. So I ended up manually comparing the column type of myGtk.ListStore
(fromGtk.ListStore.get_column_type
) withGObject.TYPE_INT
so I could manually make the input anint
if necessary.I use
Gtk.TreeViewColumn.set_cell_data_func
to truncate floats to one decimal place inTreeView
s. The second parameter (the data function) now requires a mandatory fifth parameter which I don't think I have any use for.I had to switch to a different way of checking if a window is open. I'm not sure why, to be honest.
It seems you can no longer do
del liststore[i]
to delete a row from aGtk.ListStore
. You need to do something much less Pythonic, likeliststore.remove(liststore.get_iter(i))
.To temporarily raise a window that is minimized or in the background, this solution from the old PyGTK FAQ doesn't work anymore. Instead, the better solution (as I learned on Stack Overflow) is to just call
Gtk.Window.present()
. This same method would have worked in PyGTK, but I wasn't aware of it.TreePath
objects no longer support indexing. So, if you want to access the numerical values in aTreePath
, you have to call theget_indices
method on it.I encountered a very strange bug related to connecting to the
response
signal from aGtk.Dialog
in Glade, and I wasn't able to figure out the root cause, so I worked around it by manually connecting to that signal.I ran into another issue that might be a bug in PyGObject, which I worked around by making my UI uglier and clunkier.
I'm not totally done. I'm still having some performance issues with updating large Gtk.TreeView
s, and I need to do some more testing to find any remaining bugs. But for the most part... things work. And porting wasn't that difficult or time consuming.
So in conclusion, the new bindings for GTK+ 3 are less Pythonic than PyGTK was, they're more glitchy, and there's less documentation. But they work well enough for most purposes. That's not really a useful conclusion, as I'm just repeating conventional wisdom, which turned out to be correct in this case.
Is porting worth the effort? In 2012, it would probably have been more efficient to put this time towards porting my software to a web app. But I'm just doing this for fun.
Try compiling the introspection code on ANY version of Xcode shipped in the last 2+ YEARS. The second that GIR so-called lexer hits it explodes on the blocking code.
The blocking code has beed submitted to the standards bodies as a proposed extension to the C/C++/Objective-C standards.
This would take a couple of productions in the lexer/parser to deal with.
The so-called "maintainers" of the lexer couldn't get around to adding things like "asm", "attribute", etc, well-documented in the "ansi" paragraph of the GCC "man page". added to the lexer until users yelled.
Do those "miracle people" even READ the FM?
RTFM!!
My biggest frustration is that is has been so many years since "The Red Dragon Book" or I'd just write the damned things myself.
And we are ALL supposed to be falling all over ourselves to make ourselves totally dependant on "developers" who can not/will not RTFM?
Comment by Bob Maynard — October 26, 2012 @ 6:56 pm
The problem, Bob, is that the FM seems to change way too often. To which particular version of the FM do you refer? I would love to R the FM, if you would point me at a version that is stable and trustworthy.
In PyGObject, the FM is produced from the code itself, via an apparently partially written and unsupported tool that seems to want to introspect everything via gobject and magically produce decent reliable documents. This FM will change whenever the code does; ‘a good idea!' I hear the shouts- and it might have been, except that right now, three years after the decision to abandon PyGTK, there is still no usable FM for PyGObject at all.
Compared to commercial solutions, PyGObject is a joke. The API specification is continuously in flux, and not stable long enough for anyone to seriously consider adopting it. But hey, who cares? at least the devs are having loads of fun!
Comment by Paul Sephton — October 31, 2012 @ 6:56 am
This will weirdly sound like spam, but thanks for great notes.
I'm in the process of porting an app for pygtk2 -> pygobect3 and found
these notes useful on multiple occasions. I actually ended up doing in
a few phases, since that app previously had to support back to gtk2-2.10
and libglade. really old gtk2 -> recent ish gtk2, libglade -> builder,
recent gtk -> gtk3 via pygtkcompat, then pygi-convert to pure gtk3.
Some of my notes:
1. importing from gtk into a module (ie, ‘from gtk import Widget') confuses pygi-convert.sh, so fix that first.
2, If still using libglade, that needs to be ported. https://developer.gnome.org/gtk2/stable/gtk-migrating-GtkBuilder.html has reasonable notes on that. gtk-builder-convert takes care of most of it.
3. Any objects that subclass gobject.GObject need to be updated to not use self.gobject_init(), but normal python super class init (either GObject.GObject.init or super($classnamehere).init())
4. If using threads but not for the gui, gdk.threads_init() -> GObject.threads_init()
5. Gtk.TextBuffer.get_text takes an additional required arg ‘include_hidden_chars'
6. Make sure your test cases aren't quietly doing an ‘import gtk' to get the constants, otherwise nosetests etc will segfault in unhelpful ways. (That may have caused an undue amount of cursing).
7. Replace get_parent_window() with get_toplevel() [for most cases...] Especially if you used gdkWindow.window. Also read the get_toplevel notes about multiple top level windows.
Comment by Adrian Likins — May 7, 2015 @ 10:10 am
Thanks for the comment Adrian! A few years from now, someone else will post here thanking you for all your notes 🙂
Comment by Jeremy Scheff — May 7, 2015 @ 5:14 pm