Features/GTK3/Porting/GetBooks
This page is being performed while I'm porting Get Books Activity to Gtk3.
There is a ticket with some useful information that I'm using on the porting and to keep tracking this port. Besides, this wiki page will be useful to write some code snippets about what are the difficulties that I'm having on the port and maybe can be useful for someone else.
Steps that you should follow to Port an Activity to Gtk3
- Read the Sugar Official Wiki
- Read the specific Sugar Wiki Page that talks about how to porting. Please, follow those steps and if you have a suggestion about the process you can contact with us and let us know.
- Comment on IRC about the Activity that you are going to Port (#sugar on irc.freenode.net)
- Run this script that will convert automatically things as much as it can. This is to avoid some stressful manually conversions that a "simple script" can do using sed :)
- Convert all the from sugar. import to from sugar3. import
- Remove all things related with old toolbar support. They are no longer maintained
- Keep reading this wiki page to find out more things that you should do
Tips and Tricks
Create a "new" activity from the git version
It's really useful to keep the "original" version of the Activity that we are porting to know how it was working before the port. Sometimes you find an strange behaviour on the ported version of the activity and you think that you don't touch that part of the code or that the change shouldn't affect it. In these cases it's good to run the "original" version and test how it worked before the port.
I ask to the Sugar Developers mailing list about how to do this and they told me that I should change the bundle_id. In fact, we have to change the bundle_id and the name from the activity/activity.info file. I used:
name = Get Books Gtk3 bundle_id = git.GetBooksActivity
After this, you should run the build and dev setup commands:
cd get-books python setup.py dev python setup.py build
Now, you will see two Get Books activities: one called just "Get Books" and another one called "Get Books Gtk3".
Script to list all the enumeration
There is an script that list all the Gtk methods and it's useful to know the new name and from where it comes from. For example:
[humitos@michifus get-books]$ python pygi-enumerate.py | grep get_selection Gtk.AccelLabel.get_selection_bounds() (instance method) Gtk.Editable.get_selection_bounds() (instance method) Gtk.Entry.get_selection_bounds() (instance method) Gtk.IconView.get_selection_mode() (instance method) Gtk.Label.get_selection_bounds() (instance method) Gtk.SelectionData.get_selection() (instance method) Gtk.SpinButton.get_selection_bounds() (instance method) Gtk.TextBuffer.get_selection_bound() (instance method) Gtk.TextBuffer.get_selection_bounds() (instance method) Gtk.TreeView.get_selection() (instance method) [humitos@michifus get-books]$
Use the same keyboard and mouse
If you have an XO, I'm sure you want to take a look at this...
Code Snippets
On this section I will explain all the things that I had to do manually and took me some time to find out. Some of them are really simple but time-consuming so the idea is to reduce that time as much as we can.
Migrate custom signals
If you have defined custom gtk objects with custom signal you should migrate them to the new way to do this.
You should replace this:
from gobject import signal_new, TYPE_INT, TYPE_STRING, TYPE_BOOLEAN, \ TYPE_PYOBJECT, TYPE_NONE, SIGNAL_RUN_LAST
signal_new('extlistview-modified', gtk.TreeView, SIGNAL_RUN_LAST, TYPE_NONE, ())
by adding the signal definition inside the object that you are creating using the __gsignals__ dictionary like this (in this case Gtk.TreeView is the class that our object inherits):
from gi.repository import GObject
class ExtListViewColumn(Gtk.TreeViewColumn): __gsignals__ = { 'extlistview-modified': (GObject.SignalFlags.RUN_LAST, None, ()), }
The last argument of the signal definition are the argument types that the callback will receive. For example, that tuple could be (str, int) if the callback receives two arguments, a string and an integer (first and second argument position)
Access to the Window instance
sugar3.activity.Activity doesn't have the window attribute anymore instead of this it has the .get_window() method that do the same thing. So, you should change all the occurrences of them.
Change the mouse cursor
The window attribute that I mention before is used to set the mouse cursor. This is used when the activity is working on the background (maybe searching something on the Internet) and we want to show a work in progress cursor for example.
The old way to set the cursor is:
self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
And it should be replaced by the new one:
from gi.repository import Gdk self.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.WATCH))
In that case we are creating a new instance of Gdk.Cursor every time that this chunk of code is called. I think that we could define all the cursor used by the Activity once at the beginning of the file maybe. We should discuss about this.
Use Gtk.Box instead Gtk.HBox / Gtk.VBox | Gtk.HSeparator / Gtk.VSeparator
As its documentation says, Gtk.VBox and Gtk.HBox have been deprecated. So, we should use Gtk.Box instead them. Replacing:
self.list_box = gtk.HBox()
by:
self.list_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
and,
self.list_box = gtk.VBox()
by:
self.list_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
The same happens with Gtk.Separator: link to the documentation
Using Pixbuf
As I said, this is what I did, but maybe is not the correct way to do it or there is a better way. I think that if we are porting an Activity we can take advantage of this and improve the code. manuq told me that the image handling now is done with cairo, so we should port that to cairo. I didn't do this yet, but I'm researching about this.
So, what I did is keep using Pixbuf with Gtk3 version of the Activity. We need to replace and import some things:
from gi.repository import GdkPixbuf
Replace:
gtk.gdk.PixbufLoader()
by:
GdkPixbuf.PixbufLoader()
and,
gtk.gdk.pixbuf_new_from_file(file_path)
by:
GdkPixbuf.Pixbuf.new_from_file(file_path)
and,
gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, pixbuf.get_has_alpha(), pixbuf.get_bits_per_sample(), image_width, image_height)
by:
GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, pixbuf.get_has_alpha(), pixbuf.get_bits_per_sample(), image_width, image_height)
note the .new and the colorspace in the replacement.
and,
gtk.gdk.INTERP_BILINEAR
by:
GdkPixbuf.InterpType.BILINEAR
Notes
There are some things that I realized while I was doing the port. I will mention them here:
- Gtk.Widget.hide_all() doesn't exist anymore. We should use just .hide: http://developer.gnome.org/gtk3/3.5/GtkWidget.html#gtk-widget-hide
- This is not necessary because the code will work anyway but I think that it's better. I replaced GObject.TYPE_STRING by str on the definition of the Gtk.ListStore
self.treemodel = Gtk.ListStore(GObject.TYPE_STRING)
self.treemodel = Gtk.ListStore(str)
- There is something related with .pack_start that I couldn't find it out and understand either: http://wiki.sugarlabs.org/go/Features/GTK3/Porting#HBox.2C_VBox.2C_pack_start_and_pack_end
References / Useful links
- Guide you should follow: http://wiki.sugarlabs.org/go/Features/GTK3/Porting
- PyGtk documentation
- Reference Manual
- Gdk documentation:
- OLPC Documentation: http://wiki.laptop.org/go/Activities/PortingToGtk3