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

  1. Read the Sugar Official Wiki
  2. 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.
  3. Comment on IRC about the Activity that you are going to Port (#sugar on irc.freenode.net)
  4. 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 :)
  5. Convert all the from sugar. import to from sugar3. import
  6. Remove all things related with old toolbar support. They are no longer maintained
  7. 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...

Use Extended Python debugger

There is a library called epdb and it's useful to inspect the code while the Activity is running. I'm using this module when I'm not sure about the type of an object or I can test some different things that the code says in some point. So, I installed this package on my Fedora 17 by running this command

sudo yum install python-epdb

After that I put some trace point in the code where I can stop and make my tests by doing this:

import epdb;epdb.set_trace()

Finally I run Get Books Activity from the Terminal Activity to be able to write some code on a shell. This is the command that I use:

sugar-launch org.laptop.sugar.GetBooksActivity

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:

self.treemodel = Gtk.ListStore(GObject.TYPE_STRING)
self.treemodel = Gtk.ListStore(str)

References / Useful links