Create treeview on GLADE

Sunday, April 19, 2009

Creating GtkTreeView with Glade-3, part 1

In today's bit, I'll show you how to create tree view and underlying model completely from glade GUI builder.

Contents:
  1. Creating GtkTreeView with Glade-3, part 1
  2. Creating GtkTreeView with Glade-3, part 2
Creating tree view

For this tutorial, you'll need glade >= 3.6, since creating non-widget objects is not possible in previous versions.

I would also recommend you to read GtkTreeView tutorial and Micah Carrick's glade tutorial before trying to follow this one, since I won't be discussing underlying principles much.

Do we start glade now? No, not yet. We need to create an outline of our application. We'll create simple application with GtkTreeView. Tree view will have 3 columns:
  1. "Image" column that will display image and it's name
  2. "Penetration" column that will display progress bar
  3. "Description" column with some text
If you read the tree view tutorial, you know that when creating tree view, we need three components: model (store for our data), view (tree view columns and cell renderers) and controller (GtkTreeView). And according to our plan, we'll need three tree view columns and four cell renderers:
  1. pixbuf cell renderer for displaying image (in first column)
  2. text cell renderer for displaying image name (in first column)
  3. progress cell renderer for progress bar and (in second column)
  4. another text cell renderer for description (in third column)
We'll place data for those renderers inside GtkListStore with three columns: two text columns for stock ids and descriptions and one numeric column for progress values.

Now we can (finally;) start glade.

After startup, a small dialog ask us about some project properties. Most of the options are fine default, but just make sure you select proper gtk version or GtkBuilder will moan badly when trying to create GUI if your version is less that the one specified in builder file. You've been warned;) We'll first create out model and populate it with data. Open the widget gallery and click Tree Model -> List Store. Under "Objects", we see our newly created GtkListStore. Let's modify it a bit by changing it's name to "datastore" and adding three columns: If you ever manually created GtkListStore, you may wonder where all of the G_TYPE_* macros go in column definitions. In glade, you can simply specify type without the macros. So GDK_TYPE_PIXBUF is in glade simply specified as GdkPixbuf, G_TYPE_STRING as gchararray, G_TYPE_INT as gint ... And you don't event need to know their exact name by heart, since glade offers helpful popup.

Now we'll scroll down a bit add some data into our model. Stock item ids are taken from here. And since glade hides some of the descriptions, I wrote down my original table here too.
?
.-----------+----------+---------------------------------.
| Image     | Progress | Desc                            |
+-----------+----------+---------------------------------+
| gtk-add   |    12    | Add icon is bad at penetrating. |
| gtk-cdrom |    78    | CD-ROM image fares pretty well. |
| gtk-cut   |    98    | Scissors rock!!                 |
`-----------+----------+---------------------------------'
And that's it. We now have our model part of Model/View/Controller (MVC) complex.

Now we'll create a window and connect it's "destroy" signal to gtk_main_quit function, add vbox with two rows to it and pack GtkTreeView into upper section of vbox (lower section is only a placeholder for the next part of the tutorial, where we'll be adding some controls to our application). When we place our tree view into vbox, glade ask us about model. We'll click on "..." and select "datastore" model, click OK and OK again. And controller part of MVC is done too. Now for the last part - creating display components. To add columns and renderers to out tree view, we need to select it and the click "Edit ..." button. Another window will open, looking something like this: In general tab, we'll only rename "treeview1" to "treeview". We could change entries in our data store in this window too, but we already did that when creating model.

Now we'll open "Hierarchy" tab and start creating columns and renderers. First we need to add column for images and their names. We click on "Add" button and edit newly created GtkTreeViewColumn by renaming it to "imgcol" and setting title to "Image". After we're done with editing, we need to right-click on our column and select "Add child Pixbuf item". This will add GtkCellRendererPixbuf renderer to the column. We'll rename this renderer to "imgcell" and set it's stock id property to Image model column. Now we need to add text renderer which will display image's name, but this time we'll be adding it a little different. First, we select previous cell renderer and then click "Add" button. This created new cell renderer, without the defined type. We'll rename this cell renderer to "namecell", set it's type to "Text" and it's text property to "Image" model column. Now we need to create second column for progress bar. Select the last tree view column inside the left part of the window where columns and renderers reside and click "Add" button. Then rename newly created column to "progresscol" like we renamed first column and set title to "Penetration". Now create new progress cell renderer. Did you manage to do it by yourself? If you did, congratulations. If not, you should right-click your column and select "Add child Progress item". Now rename this cell renderer to "progresscell" and set it's "Value" property to "Progress" model column. And now it's time for you to create last column, rename it to "desccol", set it's title to "Description"; add text cell renderer to it, rename it to "desccell" and set it's "text" property to "Desc" model column. After you're finished, your Hierarchy tab should look something like this: Close the Edit window and you should see your tree view already populated with some data. And that is actually all that we needed to do today. All that is left for us to do is to store glade project and test our product. I wrote a simple C application that will load our builder file and create an application from it.
?
/*
  * treeview.c - Application for testing builder file.
  *
  * Compile with:
  *  gcc -o treeview treeview.c $(pkg-config --cflags --libs gtk+-2.0 gmodule-export-2.0)
  */
  
#include <gtk/gtk.h>
  
int
main( int    argc,
       char **argv )
{
     /* Vars */
     GtkWidget  *window;
     GtkBuilder *builder;
     GError     *error;
  
     /* Initialization */
     gtk_init( &argc, &argv );
  
     /* Create builder and load UI file */
     builder = gtk_builder_new();
     if ( ! gtk_builder_add_from_file( builder, "treeview.builder" , &error ) )
     {
         g_print( "Error occured while loading UI file!/n" );
         g_print( "Message: %s/n" , error->message );
         g_free( error );
  
         return ( 1 );
     }
  
     /* Get main window and connect signals */
     window = GTK_WIDGET( gtk_builder_get_object( builder, "window1" ) );
     gtk_builder_connect_signals( builder, NULL );
  
     /* Destroy builder */
     g_object_unref( G_OBJECT( builder ) );
  
     /* Show main window and start main loop */
     gtk_widget_show( window );
     gtk_main();
  
     return ( 0 );
}
I hope you enjoyed this tutorial and be back for the second part, where we'll also learn how to manipulate data inside our store. Bye.

Friday, April 24, 2009

Creating GtkTreeView with Glade-3, part 2

In first part of this Glade tutorial, we created tree view and added some data to it. Today, wi'll expand our sample application a bit and add some controls that will enable us to modify tree view content - moving items up and down.

Contents:
  1. Creating GtkTreeView with Glade-3, part 1
  2. Creating GtkTreeView with Glade-3, part 2
Manipulating data

First thing we need to do it to create widgets that trigger the modifications of tree view data. Let's load builder project from first part of this tutorial and fill the bottom compartment of vbox.

I'll just quickly describe what needs to be done in order to create GUI like the one on the image below:
  1. Add GtkFrame in bottom compartment of vbox (the one that we left empty in previous post), change it's label to "Controls" and it's expand property to NO.
  2. Add GtkTable with 2 columns and 1 row into the frame and set column spacing to 6 px.
  3. Add two buttons to the table: left one should be "gtk-go-up" stock button and right one "gtk-go-down". Connect their "clicked" signal to "cb_move" function.
The final result should look something like this: You can download builder file from here, just to be sure that we're working with the same GUI;) Now let's start coding. And for the first time, I'll provide sample application for C and Python coders.

First, we need to load our interface. If you listened to my advice and read Micah Carrick's tutorial first, there is nothing new in my code snippets.

All that we need to do now is to write cb_move function that will take care of moving our tree view items. The way we'll be doing it is to check which button has been pressed and based on this decide if we're moving item up or down. Actual move will be done using gtk_list_store_swap function. And that's it! Now dissect this code and have fun.
?
/* treeview.c */
  
#include <gtk/gtk.h>
  
typedef struct _Data Data;
struct _Data
{
     GtkWidget *up;   /* Up button */
     GtkWidget *down; /* Down button */
     GtkWidget *tree; /* Tree view */
};
  
  
G_MODULE_EXPORT void
cb_move( GtkWidget *button,
          Data      *data )
{
     gboolean          direction = TRUE; /* TRUE means "move up", FALSE "move down" */
     GList            *rows, *tmp;       /* List of selected row */
     GtkTreeModel     *model;            /* Model with data, which we'll be moving. */
     GtkTreeSelection *sel = gtk_tree_view_get_selection( GTK_TREE_VIEW( data->tree ) );
  
     /* If nothing is selected, return. */
     if ( ! gtk_tree_selection_count_selected_rows( sel ) )
         return ;
  
     /* In which direction we'll be moving? */
     if ( button == data->down )
         direction = FALSE;
  
     /* Get selected rows */
     rows = gtk_tree_selection_get_selected_rows( sel, &model );
  
     /* Get new path for each selected row and swap items. */
     for ( tmp = rows; tmp; tmp = g_list_next( tmp ) )
     {
         GtkTreePath *path1, *path2; /* Paths. */
         GtkTreeIter  iter1, iter2;  /* Iters for swapping items. */
  
         /* Copy path */
         path1 = (GtkTreePath *)tmp->data;
         path2 = gtk_tree_path_copy( path1 );
  
         /* Move path2 in right direction */
         if ( direction )
             gtk_tree_path_prev( path2 );
         else
             gtk_tree_path_next( path2 );
  
         /* Compare paths and skip one iteration if the paths are equal, which means we're
          * trying to move first path up. */
         if ( ! gtk_tree_path_compare( path1, path2 ) )
         {
             gtk_tree_path_free( path2 );
             continue ;
         }
  
         /* Now finally obtain iters and swap items. If the second iter is invalid, we're
          * trying to move the last item down. */
         gtk_tree_model_get_iter( model, &iter1, path1 );
         if ( ! gtk_tree_model_get_iter( model, &iter2, path2 ) )
         {
             gtk_tree_path_free( path2 );
             continue ;
         }
         gtk_list_store_swap( GTK_LIST_STORE( model ), &iter1, &iter2 );
         gtk_tree_path_free( path2 );
     }
  
     /* Free our paths */
     g_list_foreach( rows, (GFunc)gtk_tree_path_free, NULL );
     g_list_free( rows );
}
  
int
main( int    argc,
       char **argv )
{
     GtkBuilder *builder;
     GtkWidget  *window;
     Data        data;
  
     gtk_init( &argc, &argv );
  
     /* Create builder */
     builder = gtk_builder_new();
     gtk_builder_add_from_file( builder, "treeview.builder" , NULL );
  
     window    = GTK_WIDGET( gtk_builder_get_object( builder, "window1" ) );
     data.up   = GTK_WIDGET( gtk_builder_get_object( builder, "move_up" ) );
     data.down = GTK_WIDGET( gtk_builder_get_object( builder, "move_down" ) );
     data.tree = GTK_WIDGET( gtk_builder_get_object( builder, "treeview" ) );
  
     gtk_builder_connect_signals( builder, &data );
     g_object_unref( G_OBJECT( builder ) );
  
     gtk_widget_show( window );
     gtk_main();
  
     return ( 0 );
}
?
#!/usr/bin/env python
# treeview.py
  
import pygtk
pygtk.require( "2.0" )
import gtk
  
class SampleApp( object ):
     def __init__( self ):
         builder = gtk.Builder()
         builder.add_from_file( "treeview.builder" )
  
         self .win   = builder.get_object( "window1" )
         self .up    = builder.get_object( "move_up" )
         self .down  = builder.get_object( "move_down" )
         self .tree  = builder.get_object( "treeview" )
  
         builder.connect_signals( self )
  
     # Callbacks
     def cb_move( self , button ):
         # Obtain selection
         sel = self .tree.get_selection()
  
         # If nothing is selected, return.
         if sel.count_selected_rows == 0 :
             return
  
         # In which diection we'll be moving?
         if button == self .up:
             direction = True
         else :
             direction = False
      
         # Get selected path
         ( model, rows ) = sel.get_selected_rows()
  
         # Get new path for each selected row and swap items. */
         for path1 in rows:
             # Move path2 in right direction
              if direction:
                 path2 = ( path1[ 0 ] - 1 , )
             else :
                 path2 = ( path1[ 0 ] + 1 , )
  
             # If path2 is negative, we're trying to move first path up. Skip
             # one loop iteration.
             if path2[ 0 ] < 0 :
                 continue
  
             # Obtain iters and swap items. If the second iter is invalid, we're
             # trying to move the last item down. */
             iter1 = model.get_iter( path1 )
             try :
                 iter2 = model.get_iter( path2 )
             except ValueError:
                 continue
             model.swap( iter1, iter2 )
  
     def gtk_main_quit( self , object ):
         gtk.main_quit()
  
if __name__ == "__main__" :
     app = SampleApp()
     app.win.show()
     gtk.main()
I hope you enjoyed this tutorial. Come back soon when I'll show you how to deal with GtkDialog and it's derivates in conjunction with glade. Bye
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值