PyMaemo/UI tutorial/Navigation

(use <source> and <code>)
Line 1: Line 1:
-
== Navigation ==
 
-
 
In previous examples only one widget was added to a window so we could simply use a <code>container_add()</code> to “pack” it into the window. To pack more than one widget into a window it is necessary to use container widgets.
In previous examples only one widget was added to a window so we could simply use a <code>container_add()</code> to “pack” it into the window. To pack more than one widget into a window it is necessary to use container widgets.
To develop Hildon user interfaces, you can use any container widget provided by GTK+. For more details, [http://www.pygtk.org/pygtk2tutorial/index.html PyGTK 2.0 Tutorial] includes a good introduction to this topic.
To develop Hildon user interfaces, you can use any container widget provided by GTK+. For more details, [http://www.pygtk.org/pygtk2tutorial/index.html PyGTK 2.0 Tutorial] includes a good introduction to this topic.
-
Apart from supporting those containers, the Hildon framework also provides a new container widget called HildonPannableArea, a scrolling widget designed for touch screens. This chapter will cover all that is necessary to properly use this widget.
+
Apart from supporting those containers, the Hildon framework also provides a new container widget called <code>HildonPannableArea</code>, a scrolling widget designed for touch screens. This chapter will cover all that is necessary to properly use this widget.
== Pannable Area ==
== Pannable Area ==
-
HildonPannableArea is used to create an area with another widget inside which will be accessible regardless of the size using the touchscreen to scroll it. You may insert any type of widget into a pannable area, and it will be accessible by dragging on the area with the fingers.
+
<code>HildonPannableArea</code> is used to create an area with another widget inside which will be accessible regardless of the size using the touchscreen to scroll it. You may insert any type of widget into a pannable area, and it will be accessible by dragging on the area with the fingers.
This widget can be “panned” (scrolled) in any direction using the touchscreen with the fingers. One remarkable characteristic is that the scrolling is “kinetic”, meaning that the motion will continue from the initial motion by gradually slowing down to stop.
This widget can be “panned” (scrolled) in any direction using the touchscreen with the fingers. One remarkable characteristic is that the scrolling is “kinetic”, meaning that the motion will continue from the initial motion by gradually slowing down to stop.
Line 16: Line 14:
To create a new pannable area you can choose either of the following functions:
To create a new pannable area you can choose either of the following functions:
-
 
+
<source lang="python">
     hildon.PannableArea()
     hildon.PannableArea()
     hildon.hildon_pannable_area_new_full(mode, enabled, vel_min, vel_max, decel, sps)
     hildon.hildon_pannable_area_new_full(mode, enabled, vel_min, vel_max, decel, sps)
-
 
+
</source>
The first one creates a new pannable area with the properties set to the default values. The second one allows you to set the value of the most important properties of this widget:
The first one creates a new pannable area with the properties set to the default values. The second one allows you to set the value of the most important properties of this widget:
Line 31: Line 29:
Once the area is created you can then place your object into the pannable window using the following function.
Once the area is created you can then place your object into the pannable window using the following function.
-
 
+
<source lang="python">
     add_with_viewport(self, child)
     add_with_viewport(self, child)
-
 
+
</source>
-
That is a convenience function used to add a child to a GtkViewport, and add the viewport to the pannable area.
+
That is a convenience function used to add a child to a <code>GtkViewport</code>, and add the viewport to the pannable area.
=== Warning ===
=== Warning ===
-
Widgets that have native scrolling should be added directly inside a pannable area. For example, widgets such as GtkTextView, GtkTreeView, GtkIconView and GtkLayout should be added by calling <code>add()</code>. Otherwise, panning could not work properly.
+
Widgets that have native scrolling should be added directly inside a pannable area. For example, widgets such as <code>GtkTextView</code>, <code>GtkTreeView</code>, <code>GtkIconView</code> and <code>GtkLayout</code> should be added by calling <code>add()</code>. Otherwise, panning could not work properly.
== Pannable area example ==
== Pannable area example ==
Functions explained above are enough for a simple example. The following example packs a table with 100 toggle buttons into a pannable area.
Functions explained above are enough for a simple example. The following example packs a table with 100 toggle buttons into a pannable area.
 +
<source lang="python">
 +
# Based on C code from:
 +
# "Hildon Tutorial" version 2009-04-28
 +
# Example 5.1, "Example of a pannable area"
-
    # Based on C code from:
+
import gtk
-
    # "Hildon Tutorial" version 2009-04-28
+
import hildon
-
    # Example 5.1, "Example of a pannable area"
+
 
-
   
+
def create_table():
-
    import gtk
+
    # create a table of 10 by 10 squares.  
-
    import hildon
+
    table = gtk.Table (10, 10, False)
-
   
+
-
    def create_table():
+
-
        # create a table of 10 by 10 squares.  
+
-
        table = gtk.Table (10, 10, False)
+
-
   
+
-
        # set the spacing to 10 on x and 10 on y
+
-
        table.set_row_spacings(10)
+
-
        table.set_col_spacings(10)
+
-
   
+
-
        table.show()
+
-
   
+
-
        # this simply creates a grid of toggle buttons on the table
+
-
        # to demonstrate the scrolled window.
+
-
        for i in range(10):
+
-
            for j in range(10):
+
-
                data_buffer = "button (%d,%d)\n" % (i, j)
+
-
                button = gtk.ToggleButton(data_buffer)
+
-
                table.attach(button, i, i+1, j, j+1)
+
-
   
+
-
        return table
+
-
   
+
-
    def app_quit(widget, data=None):
+
-
        gtk.main_quit()
+
-
   
+
-
    def main():
+
-
        window = hildon.StackableWindow()
+
-
   
+
-
        window.connect("destroy", app_quit)
+
-
   
+
-
        pannable_area = hildon.PannableArea()
+
-
   
+
-
        table = create_table()
+
-
   
+
-
        # pack the table into the scrolled window
+
-
        pannable_area.add_with_viewport(table)
+
-
   
+
-
        # Add the box into the window
+
-
        window.add(pannable_area)
+
-
   
+
-
        window.show_all()
+
-
   
+
-
        gtk.main()
+
-
   
+
-
    if __name__ == "__main__":
+
-
        main()             
+
-
In the example above you can see that the following two calls are enough to use a pannable area. The rest of the code of the example is no different to that used in a GTK+ application.
+
    # set the spacing to 10 on x and 10 on y
 +
    table.set_row_spacings(10)
 +
    table.set_col_spacings(10)
 +
 
 +
    table.show()
 +
 
 +
    # this simply creates a grid of toggle buttons on the table
 +
    # to demonstrate the scrolled window.
 +
    for i in range(10):
 +
        for j in range(10):
 +
            data_buffer = "button (%d,%d)\n" % (i, j)
 +
            button = gtk.ToggleButton(data_buffer)
 +
            table.attach(button, i, i+1, j, j+1)
 +
 
 +
    return table
 +
 
 +
def app_quit(widget, data=None):
 +
    gtk.main_quit()
 +
 
 +
def main():
 +
    window = hildon.StackableWindow()
 +
 
 +
    window.connect("destroy", app_quit)
 +
 
 +
    pannable_area = hildon.PannableArea()
 +
    table = create_table()
 +
 +
    # pack the table into the scrolled window
 +
    pannable_area.add_with_viewport(table)
 +
 +
    # Add the box into the window
 +
    window.add(pannable_area)
 +
 +
    window.show_all()
 +
 +
    gtk.main()
 +
 +
if __name__ == "__main__":
 +
    main()             
 +
</source>
 +
In the example above you can see that the following two calls are enough to use a pannable area. The rest of the code of the example is no different to that used in a GTK+ application.
 +
<source lang="python">
     # Create a new pannable area.
     # Create a new pannable area.
     pannable_area = hildon.PannableArea()
     pannable_area = hildon.PannableArea()
     # Pack the table into the pannable area
     # Pack the table into the pannable area
     pannable_area.add_with_viewport(table);
     pannable_area.add_with_viewport(table);
-
 
+
</source>
-
To see all the buttons, users can scroll with the fingers. In this example, horizontal and vertical panning are activated as that is needed to allow users to be able to interact both all the widgets. The property <code>“mov-mode”</code> controls if the area can scroll horizontally, vertically (default value) or both, using <code>hildon.MOVEMENT_MODE_HORIZ, hildon.MOVEMENT_MODE_VERT</code> or <code>hildon.MOVEMENT_MODE_BOTH</code>, respectively.
+
To see all the buttons, users can scroll with the fingers. In this example, horizontal and vertical panning are activated as that is needed to allow users to be able to interact both all the widgets. The property <code>“mov-mode”</code> controls if the area can scroll horizontally, vertically (default value) or both, using <code>hildon.MOVEMENT_MODE_HORIZ</code>, <code>hildon.MOVEMENT_MODE_VERT</code> or <code>hildon.MOVEMENT_MODE_BOTH</code>, respectively.
== Additional features ==
== Additional features ==
Line 111: Line 109:
These functions allow to scroll or jump to a position which ensures that a certain point or a certain child widget is visible for the user.
These functions allow to scroll or jump to a position which ensures that a certain point or a certain child widget is visible for the user.
-
For example, the first of the functions changes the current position on the pannable area to ensure position (x,y) is visible. The movement is a quick jump. The second function performs a smoothly scroll towards the selected position.
+
For example, the first of the functions changes the current position on the pannable area to ensure position (x,y) is visible. The movement is a quick jump. The second function performs a smooth scroll towards the selected position.
 +
<source lang="python">
 +
def jump_to(self, x, y)
 +
def scroll_to(self, x, y)
 +
</source>
 +
It is also possible to jump or scroll to a certain descendent of the area using the following functions, the argument should be a reference to a descendent widget.
 +
<source lang="python">
 +
def jump_to_child(self, child)
 +
def scroll_to_child(self, child)
 +
</source>
 +
Here is a modified version of the previous example. The pannable area is packed into an <code>gtk.VBox</code> and a new button is also added to navigate to the last clicked button.
-
    def jump_to(self, x, y)
+
'''Example of a pannable area and a “jump-to” button'''
-
    def scroll_to(self, x, y)
+
<source lang="python">
 +
# Based on C code from:
 +
# "Hildon Tutorial" version 2009-04-28
 +
# Example 5.2, "Example of a pannable area and a "jump-to" button"
-
It is also possible to jump or scroll to a certain descendent of the area using the following functions, the argument should be a reference to a descendent widget.
+
import gtk
 +
import hildon
-
    def jump_to_child(self, child)
+
# Pointer to the last clicked button
-
    def scroll_to_child(self, child)
+
last_clicked_button = None
-
Here is a modified version of the previous example. The pannable area is packed into an <code>gtk.VBox</code> and a new button is also added to navigate to the last clicked button.
+
# Callabck to set last clicked button
 +
def clicked(button):
 +
    global last_clicked_button
 +
    last_clicked_button = button
-
'''Example of a pannable area and a “jump-to” button'''
+
def go_to_last_clicked(button, pannable_area):
-
 
+
     pannable_area.scroll_to_child(last_clicked_button)
-
    # Based on C code from:
+
-
     # "Hildon Tutorial" version 2009-04-28
+
-
    # Example 5.2, "Example of a pannable area and a "jump-to" button"
+
      
      
-
    import gtk
+
def create_table():
-
    import hildon
+
      
      
-
     # Pointer to the last clicked button
+
     # create a table of 10 by 10 squares.
-
     last_clicked_button = None
+
     table = gtk.Table (10, 10, False)
      
      
-
     # Callabck to set last clicked button
+
     # set the spacing to 10 on x and 10 on y
-
     def clicked(button):
+
     table.set_row_spacings(10)
-
        global last_clicked_button
+
    table.set_col_spacings(10)
-
        last_clicked_button = button
+
 
 +
    table.show()
      
      
-
     def go_to_last_clicked(button, pannable_area):
+
     # this simply creates a grid of toggle buttons on the table
-
        pannable_area.scroll_to_child(last_clicked_button)
+
    # to demonstrate the scrolled window.
-
       
+
     for i in range(10):
-
     def create_table():  
+
         for j in range(10):
-
          
+
            data_buffer = "button (%d,%d)\n" % (i, j)
-
        # create a table of 10 by 10 squares.
+
            button = gtk.ToggleButton(data_buffer)
-
        table = gtk.Table (10, 10, False)
+
            button.connect("clicked", clicked)
-
       
+
            table.attach(button, i, i+1, j, j+1)
-
        # set the spacing to 10 on x and 10 on y
+
-
        table.set_row_spacings(10)
+
-
        table.set_col_spacings(10)
+
      
      
-
        table.show()
+
    return table
-
       
+
 
-
        # this simply creates a grid of toggle buttons on the table
+
def app_quit(widget, data=None):  
-
        # to demonstrate the scrolled window.
+
    gtk.main_quit()
-
        for i in range(10):
+
-
            for j in range(10):
+
-
                data_buffer = "button (%d,%d)\n" % (i, j)
+
-
                button = gtk.ToggleButton(data_buffer)
+
-
                button.connect("clicked", clicked)
+
-
                table.attach(button, i, i+1, j, j+1)
+
-
       
+
-
        return table
+
      
      
-
    def app_quit(widget, data=None):  
+
def main():
-
        gtk.main_quit()
+
    window = hildon.StackableWindow()
 +
    pannable_area = hildon.PannableArea()
 +
   
 +
    window.connect("destroy", app_quit)
          
          
-
     def main():
+
     pannable_area.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
-
        window = hildon.StackableWindow()
+
-
        pannable_area = hildon.PannableArea()
+
-
       
+
-
        window.connect("destroy", app_quit)
+
              
              
-
        pannable_area.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
+
    button = gtk.Button("Go to last clicked button")
-
               
+
    button.connect("clicked", go_to_last_clicked, pannable_area)
-
        button = gtk.Button("Go to last clicked button")
+
 
-
        button.connect("clicked", go_to_last_clicked, pannable_area)
+
    table = create_table()
-
   
+
 
-
        table = create_table()
+
    # pack the table into the scrolled window
-
   
+
    pannable_area.add_with_viewport(table)
-
        # pack the table into the scrolled window
+
 
-
        pannable_area.add_with_viewport(table)
+
    # Create a box and pack the widgets into it
-
   
+
    vbox = gtk.VBox(False, 0)
-
        # Create a box and pack the widgets into it
+
 
-
        vbox = gtk.VBox(False, 0)
+
    vbox.pack_start(button, False, False, 0)
-
   
+
    vbox.pack_start(pannable_area, True, True, 0)
-
        vbox.pack_start(button, False, False, 0)
+
 
-
        vbox.pack_start(pannable_area, True, True, 0)
+
    # Add the box into the window
-
   
+
    window.add(vbox)
-
        # Add the box into the window
+
 
-
        window.add(vbox)
+
     window.show_all()
-
      
+
    gtk.main()
-
        window.show_all()
+
-
        gtk.main()
+
-
   
+
-
    if __name__ == "__main__":
+
-
        main()
+
 +
if __name__ == "__main__":
 +
    main()
 +
</source>
The example used a global variable to store a reference to the last clicked button. This reference will be used by the callback <code>go_to_last_clicked</code> to jump to it by calling one of the navigation functions. This is the function used as a handler for the signal “clicked” of the button outside the pannable area.
The example used a global variable to store a reference to the last clicked button. This reference will be used by the callback <code>go_to_last_clicked</code> to jump to it by calling one of the navigation functions. This is the function used as a handler for the signal “clicked” of the button outside the pannable area.

Revision as of 14:45, 22 June 2010

In previous examples only one widget was added to a window so we could simply use a container_add() to “pack” it into the window. To pack more than one widget into a window it is necessary to use container widgets.

To develop Hildon user interfaces, you can use any container widget provided by GTK+. For more details, PyGTK 2.0 Tutorial includes a good introduction to this topic.

Apart from supporting those containers, the Hildon framework also provides a new container widget called HildonPannableArea, a scrolling widget designed for touch screens. This chapter will cover all that is necessary to properly use this widget.

Contents

Pannable Area

HildonPannableArea is used to create an area with another widget inside which will be accessible regardless of the size using the touchscreen to scroll it. You may insert any type of widget into a pannable area, and it will be accessible by dragging on the area with the fingers.

This widget can be “panned” (scrolled) in any direction using the touchscreen with the fingers. One remarkable characteristic is that the scrolling is “kinetic”, meaning that the motion will continue from the initial motion by gradually slowing down to stop.

The use of this widget is very similar to GTK+ scrolled windows. In fact, both widgets implement a similar concept and its APIs are very similar.

To create a new pannable area you can choose either of the following functions:

    hildon.PannableArea()
    hildon.hildon_pannable_area_new_full(mode, enabled, vel_min, vel_max, decel, sps)

The first one creates a new pannable area with the properties set to the default values. The second one allows you to set the value of the most important properties of this widget:

  • mode : Used to change the behaviour of the pannable area allowing to choose whether to use the “kinetic” effect described above.
  • enabled : Enable or disable finger-scroll.
  • vel_min, vel_max : Allows developers to adjust how many pixels the child widget will be move per “frame”.
  • decel : Value for the deceleration property.
  • sps : Amount of scroll events to generate per second.

For most applications, default values of these properties will be right and thus, the simpler constructor will be enough. In the Hildon reference manual you can read more about all pannable area’s properties.

Once the area is created you can then place your object into the pannable window using the following function.

    add_with_viewport(self, child)

That is a convenience function used to add a child to a GtkViewport, and add the viewport to the pannable area.

Warning

Widgets that have native scrolling should be added directly inside a pannable area. For example, widgets such as GtkTextView, GtkTreeView, GtkIconView and GtkLayout should be added by calling add(). Otherwise, panning could not work properly.

Pannable area example

Functions explained above are enough for a simple example. The following example packs a table with 100 toggle buttons into a pannable area.

# Based on C code from:
# "Hildon Tutorial" version 2009-04-28
# Example 5.1, "Example of a pannable area"
 
import gtk
import hildon
 
def create_table():
    # create a table of 10 by 10 squares. 
    table = gtk.Table (10, 10, False)
 
    # set the spacing to 10 on x and 10 on y 
    table.set_row_spacings(10)
    table.set_col_spacings(10)
 
    table.show()
 
    # this simply creates a grid of toggle buttons on the table
    # to demonstrate the scrolled window. 
    for i in range(10):
        for j in range(10):
            data_buffer = "button (%d,%d)\n" % (i, j)
            button = gtk.ToggleButton(data_buffer)
            table.attach(button, i, i+1, j, j+1)
 
    return table
 
def app_quit(widget, data=None):
    gtk.main_quit()
 
def main():
    window = hildon.StackableWindow()
 
    window.connect("destroy", app_quit)
 
    pannable_area = hildon.PannableArea()
 
    table = create_table()
 
    # pack the table into the scrolled window 
    pannable_area.add_with_viewport(table)
 
    # Add the box into the window
    window.add(pannable_area)
 
    window.show_all()
 
    gtk.main()
 
if __name__ == "__main__":
    main()

In the example above you can see that the following two calls are enough to use a pannable area. The rest of the code of the example is no different to that used in a GTK+ application.

    # Create a new pannable area.
    pannable_area = hildon.PannableArea()
    # Pack the table into the pannable area
    pannable_area.add_with_viewport(table);

To see all the buttons, users can scroll with the fingers. In this example, horizontal and vertical panning are activated as that is needed to allow users to be able to interact both all the widgets. The property “mov-mode” controls if the area can scroll horizontally, vertically (default value) or both, using hildon.MOVEMENT_MODE_HORIZ, hildon.MOVEMENT_MODE_VERT or hildon.MOVEMENT_MODE_BOTH, respectively.

Additional features

Pannable areas provide a set of convenience functions that make it easier to move to a certain element inside the area without users interaction.

These functions allow to scroll or jump to a position which ensures that a certain point or a certain child widget is visible for the user.

For example, the first of the functions changes the current position on the pannable area to ensure position (x,y) is visible. The movement is a quick jump. The second function performs a smooth scroll towards the selected position.

def jump_to(self, x, y)
def scroll_to(self, x, y)

It is also possible to jump or scroll to a certain descendent of the area using the following functions, the argument should be a reference to a descendent widget.

def jump_to_child(self, child)
def scroll_to_child(self, child)

Here is a modified version of the previous example. The pannable area is packed into an gtk.VBox and a new button is also added to navigate to the last clicked button.

Example of a pannable area and a “jump-to” button

# Based on C code from:
# "Hildon Tutorial" version 2009-04-28
# Example 5.2, "Example of a pannable area and a "jump-to" button"
 
import gtk
import hildon
 
# Pointer to the last clicked button
last_clicked_button = None
 
# Callabck to set last clicked button
def clicked(button):
    global last_clicked_button
    last_clicked_button = button
 
def go_to_last_clicked(button, pannable_area):
    pannable_area.scroll_to_child(last_clicked_button)
 
def create_table(): 
 
    # create a table of 10 by 10 squares.
    table = gtk.Table (10, 10, False)
 
    # set the spacing to 10 on x and 10 on y
    table.set_row_spacings(10)
    table.set_col_spacings(10)
 
    table.show()
 
    # this simply creates a grid of toggle buttons on the table
    # to demonstrate the scrolled window.
    for i in range(10):
        for j in range(10):
            data_buffer = "button (%d,%d)\n" % (i, j)
            button = gtk.ToggleButton(data_buffer)
            button.connect("clicked", clicked)
            table.attach(button, i, i+1, j, j+1)
 
    return table
 
def app_quit(widget, data=None): 
    gtk.main_quit()
 
def main():
    window = hildon.StackableWindow()
    pannable_area = hildon.PannableArea()
 
    window.connect("destroy", app_quit)
 
    pannable_area.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
 
    button = gtk.Button("Go to last clicked button")
    button.connect("clicked", go_to_last_clicked, pannable_area)
 
    table = create_table()
 
    # pack the table into the scrolled window
    pannable_area.add_with_viewport(table)
 
    # Create a box and pack the widgets into it
    vbox = gtk.VBox(False, 0)
 
    vbox.pack_start(button, False, False, 0)
    vbox.pack_start(pannable_area, True, True, 0)
 
    # Add the box into the window
    window.add(vbox)
 
    window.show_all()
    gtk.main()
 
if __name__ == "__main__":
    main()

The example used a global variable to store a reference to the last clicked button. This reference will be used by the callback go_to_last_clicked to jump to it by calling one of the navigation functions. This is the function used as a handler for the signal “clicked” of the button outside the pannable area.

You can test the different navigation functions by just changing the call in the mentioned callback.

When you use the navigation functions that allow to navigate to a certain child, the widget must be already realized. You can check it with gtk.WidgetFlags.realized. If you want to call it during the initialization process you can use the navigation function inside a callback to the “realized” signal.