491. How to have a start stop button in GtkTreeView - Part 2 - using treestore?

Problem

This is in response to Giovanni's post titled "a button with an image in it in a Treeview".

In Part 1, I've showed a solution using GtkListStore.

In this Part 2, I present the same solution, but using GtkTreeStore instead of GtkListStore so that the projects and activities can be shown in a treeview manner as shown below.

Note that I've also changed the millisecond timer to a seconds timer because it seems that having more than 6 concurrent millisecond timer running in a treeview seems to be a bit too taxing for the treeview. You can try it for yourself. The timer doesn't get updated often enough, even though we set the timeout at 1ms interval.

How to have a start stop button in GtkTreeView - Part 2 - using treestore?


Solution

  • We use exactly the same technique as outlined in Part 1.
  • We make use of the technique from How to display a tree structure from array? to populate a tree model.
  • Note also that I've added 2 more columns to the tree model: column 7 to store the ID of the item, and column 8 to store the ID of its parent.

Sample Code

The following image files are required by the sample code below. Please save a copy of the image files and put them in the same directory where you store the sample code.

 button_start32.gif
 button_stop32.gif
 button_blank32.gif (note: this one looks empty, but there's a blank image there.
 I added a border so that you can see it.)

1   
2   
3   
4   
5   
6   
7   
8   
9   
10   
11   
12   
13   
14   
15   
16   
17   
18   
19   
20   
21   
22   
23   
24   
25   
26   
27   
28   
29   
30   
31   
32   
33   
34   
35   
36   
37   
38   
39   
40   
41   
42   
43   
44   
45   
46   
47   
48   
49   
50   
51   
52   
53   
54   
55   
56   
57   
58   
59   
60   
61   
62   
63   
64   
65   
66   
67   
68   
69   
70   
71   
72   
73   
74   
75   
76   
77   
78   
79   
80   
81   
82   
83   
84   
85   
87   
88   
89   
90   
91   
92   
93   
94   
95   
96   
97   
98   
99   
100   
101   
102   
103   
104   
105   
106   
107   
108   
109   
110   
111   
112   
113   
114   
115   
116   
117   
118   
119   
121   
122   
124   
125   
126   
127   
128   
129   
130   
131   
132   
133   
134   
138   
139   
140   
145   
146   
147   
149   
150   
151   
152   
154   
155   
156   
158   
159   
160   
161   
162   
164   
165   
166   
168   
169   
170   
171   
172   
173   
174   
175   
176   
177   
178   
180   
181   
182   
183   
192   
193   
<?php
$window = new GtkWindow();
$window->set_title($argv[0]);
$window->set_size_request(400, 400);
$window->connect_simple('destroy', array('Gtk','main_quit'));
$window->add($vbox = new GtkVBox());

// display title
$title = new GtkLabel("Start/Stop buttons in GtkTreeView\n".
"      Part 2 - using Tree Model");
$title->modify_font(new PangoFontDescription("Times New Roman Italic 10"));
$title->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#0000ff"));
$title->set_size_request(-1, 40);
$vbox->pack_start($title, 0);
$vbox->pack_start(new GtkLabel(), 0);

// the 2D table
$data = array(
array('Project 1',    0, 0, 0, 0, '', 0, 1, 0),
array('Activity 1.1', 1, 0, 0, 0, '', 0, 2, 1),
array('Activity 1.2', 1, 0, 0, 0, '', 0, 3, 1),
array('Project 2',    0, 0, 0, 0, '', 0, 4, 0),
array('Activity 2.1', 1, 0, 0, 0, '', 0, 5, 4),
array('Activity 2.2', 1, 0, 0, 0, '', 0, 6, 4),
array('Activity 2.3', 1, 0, 0, 0, '', 0, 7, 4),
array('Activity 2.4', 1, 0, 0, 0, '', 0, 8, 4),
);

display_table($vbox, $data);

$window->show_all();
Gtk::main();

function display_table($vbox, $data) {

    // Set up a scroll window
    $scrolled_win = new GtkScrolledWindow();
    $scrolled_win->set_policy( Gtk::POLICY_AUTOMATIC,
        Gtk::POLICY_AUTOMATIC);
    $vbox->pack_start($scrolled_win);

    // Creates the list store
    if (defined("GObject::TYPE_STRING")) {
        $model = new GtkTreeStore( // note 1
            GObject::TYPE_STRING,  // col 0: column title
            GObject::TYPE_BOOLEAN, // col 1: boolean - 1 to display start/stop icon
            GObject::TYPE_BOOLEAN, // col 2: boolean - 0=stop 1=start
            GObject::TYPE_DOUBLE,  // col 3: start time in float
            GObject::TYPE_DOUBLE,  // col 4: time elapsed in float
            GObject::TYPE_STRING,  // col 5: time elapsed in string
            GObject::TYPE_LONG,    // col 6: timeout id
            GObject::TYPE_LONG,    // col 7: id of this item
            GObject::TYPE_LONG     // col 8: parent id of this item
            );


    } else {
        $model = new GtkTreeStore( // note 1
            Gtk::TYPE_STRING,  // col 0: column title
            Gtk::TYPE_BOOLEAN, // col 1: boolean - 1 to display start/stop icon
            Gtk::TYPE_BOOLEAN, // col 2: boolean - 0=stop 1=start
            Gtk::TYPE_DOUBLE,  // col 3: start time in float
            Gtk::TYPE_DOUBLE,  // col 4: time elapsed in float
            Gtk::TYPE_STRING,  // col 5: time elapsed in string
            Gtk::TYPE_LONG,    // col 6: timeout id
            Gtk::TYPE_LONG,    // col 7: id of this item
            Gtk::TYPE_LONG     // col 8: parent id of this item
            );
    }

    // Creates the view to display the list store
    $view = new GtkTreeView($model);
    $scrolled_win->add($view);
    $view->connect('button-press-event', 'on_buttonpress', $model);

    // setup col 0
    $cell_renderer = new GtkCellRendererText(); // note 2
    $column = new GtkTreeViewColumn('Project/Activity', $cell_renderer, 'text', 0);
    $column->set_cell_data_func($cell_renderer, 'format_col', 0);
    $view->append_column($column);

    // setup col 1 (the buttons)
    $cell_renderer = new GtkCellRendererPixbuf();
    $column = new GtkTreeViewColumn();
    $column->pack_start($cell_renderer);
    $column->set_cell_data_func($cell_renderer, 'format_col', 1);
    $column->set_title('Action');
    $view->append_column($column);

    // setup col 2
    $cell_renderer = new GtkCellRendererText();
    $column = new GtkTreeViewColumn('Time', $cell_renderer, 'text', 5);
    $column->set_cell_data_func($cell_renderer, 'format_col', 5);
    $view->append_column($column);

    // pupulates the data
    $nodes = array(); // note 3
    $nodes[0] = null; // root
    for ($row=0; $row<count($data); ++$row) {
        $id = $data[$row][7];
        $parent = $data[$row][8];
        $values = array();
        for ($col=0; $col<count($data[$row]); ++$col) {
            $values[] = $data[$row][$col];
        }
        $nodes[$id] = $model->append($nodes[$parent], $values);
    }

    $view->expand_all(); // note 4

    if (method_exists($view, 'set_enable_tree_lines')) $view->set_enable_tree_lines(1);

    $selection = $view->get_selection();
    $selection->set_mode(Gtk::SELECTION_NONE);
}

// self-defined function to display alternate row color
function format_col($column, $cell, $model, $iter, $col_num) {
    $path = $model->get_path($iter);
    if ($col_num==1) {
        if ($model[$iter][8]!=0) { // note 5
            if ($model[$iter][2]==0) {
                $pixbuf = GdkPixbuf::new_from_file("button_start32.gif");
            } else {
                $pixbuf = GdkPixbuf::new_from_file("button_stop32.gif");
            }
        } else {
            $pixbuf = GdkPixbuf::new_from_file("button_blank32.gif");
        }
        $cell->set_property('pixbuf', $pixbuf);
    }
}

function on_buttonpress($view, $event, $model) {
    if ($event->button==1 && $event->type==4) {
        // get the row and column
        $path_array = $view->get_path_at_pos($event->x, $event->y);
        $path = $path_array[0]; // note 6
        $col = $path_array[1];
        $col_title = $col->get_title();
        if ($col_title!='Action') return false; // note 7
        if ($model[$path][2]) {
            $model[$path][2] = 0;
            $elapsed_time = microtime(1) - $model[$path][3];
            $model[$path][4] += $elapsed_time;
            Gtk::timeout_remove($model[$path][6]);
        } else { // note 7
            $model[$path][2] = 1;
            $model[$path][3] = microtime(1);
            $model[$path][6] = Gtk::timeout_add(1000, 'update_time', 
                $model, $path); // note 8
        }
        return true;
    } elseif ($event->button==2) {
        return false;
    } elseif ($event->button==3) {
        return false;
    }
}

function update_time($model, $path) {
    $elapsed_time = microtime(1) - $model[$path][3];
    $elapsed_time += $model[$path][4];
    $model[$path][5] = date('H:i:s', $elapsed_time);
    return true;
}


?>

Output

As shown above.
 

Explanation

  1. Note that we changed this from GtkListStore to GtkTreeStore. We also added column 7 to store the ID of the item, and column 8 to store the ID of its parent.
  2. Note that we have changed the "Project/Activity" to the first column so that the projects and activities are presented in a hierarchical manner.
  3. We make use of the technique as outlined in How to display a tree structure from array? to populate the data to the treemodel.
  4. Expand the entire tree.
  5. Display start/stop button for activities only.
  6. Note that for treemodel, this is not changed to $path = $path_array[0]
  7. We only want to process button clicks on the start/stop buttons.
  8. Note that I've changed this from 1 millisecond to 1 second.

Related Links

Add comment


Security code
Refresh