ruby-****@sourc*****
ruby-****@sourc*****
2012年 10月 8日 (月) 04:50:05 JST
------------------------- REMOTE_ADDR = 184.145.80.187 REMOTE_HOST = URL = http://ruby-gnome2.sourceforge.jp/hiki.cgi?tut-gtk2-treev-crs ------------------------- @@ -167,3 +167,151 @@ === Toggle Button Renderers May Effect Other Related Values In Tree View + + + +Sometimes when certain cell value is changed by the user it may effect one or more other cells on the tree view indicating data related dependencies in tree view. In our 'Grocery List' example such dependencies can be found. Remember that in any product category row the column 'Count' displays the number of individual items from that category for which their respective 'Buy' columns are checked, i.e. in their model columns they contain Boolean value TRUE. + +For instance, lets consider the initial state of the 'Grocery List' display immediately after you start up the program, in which the row identified by product 'Paper Towels' originally had the 'Buy' column checked (TRUE), and in the 'Count' column value 1. At the same time the pertinent product category row (Cleaning Supplies), due to the 'Toilet Paper' row which is also checked and has value 3 in its 'Count' column, contains value 4 in its 'Count' column, and indeed the 'Buy' column checked. Should a user then toggle the 'Buy' column for 'Paper Towels' row to unchecked (FALSE), that would ultimately affect the pertinent product category's 'Count' value to display now decreased value 3. If however, the user also unchecks the 'Toilet Paper' row, we will witness two changes in the 'Cleaning Supplies' product category row, namely, its 'Count' value will become 0 (zero), and the 'Buy' column will become unchecked. This reveals two data dependencies in our tree view. But there is a third issue lurking behind these dependencies here, namely it should not be possible for a user to manually toggle the check button for any product category. + +Let's see how all this is managed: + +{{br}} +((*toggle-rndr-2.rb*)) + + #!/usr/bin/env ruby + require 'gtk2' + + # Show data dependent tree view behaviour (toggle button renderer). + + # Add three columns to the GtkTreeView. This time first + # Boolean 'Buy' column will appear as check-button, the + # other two columns 'Count' and 'Product' remain Integer + # and String respectively. + + def setup_tree_view(treeview) + # Create a new GtkCellRendererText, add it to the tree + # view column and append the column to the tree view. + + renderer = Gtk::CellRendererToggle.new + column = Gtk::TreeViewColumn.new("Buy", renderer, "active" => GItm::BUY_INDEX) + + column.set_cell_data_func(renderer) do |tvc, cell, model, iter| + iter[GItm::BUY_INDEX] = any_child_set_to_buy(iter) if iter.has_child? + end + + renderer.signal_connect('toggled') do |w, path| + iter = treeview.model.get_iter(path) + iter[GItm::BUY_INDEX] = !iter[GItm::BUY_INDEX] if (iter) + end + treeview.append_column(column) + + renderer = Gtk::CellRendererText.new + column = Gtk::TreeViewColumn.new("Count", renderer, "text" => GItm::QTY_INDEX) + column.set_cell_data_func(renderer) do |tvc, cell, model, iter| + fix_parents_total(iter) if !iter.has_child? + end + treeview.append_column(column) + + renderer = Gtk::CellRendererText.new + column = Gtk::TreeViewColumn.new("Product", renderer, "text" => GItm::PROD_INDEX) + treeview.append_column(column) + end + + def any_child_set_to_buy(parent) + tmp_iter = parent.first_child + return true if tmp_iter[GItm::BUY_INDEX] + (return true if tmp_iter[GItm::BUY_INDEX]) while tmp_iter.next! + return false + end + + def fix_parents_total(iter) + parent = iter.parent + tmp_iter = parent.first_child + total = 0 + total = tmp_iter[GItm::QTY_INDEX] if tmp_iter[GItm::BUY_INDEX] + (total += tmp_iter[GItm::QTY_INDEX] if tmp_iter[GItm::BUY_INDEX]) while tmp_iter.next! + parent[GItm::QTY_INDEX] = total + end + + class GItm + attr_accessor :product_type, :buy, :quantity, :product + def initialize(t,b,q,p) + @product_type, @buy, @quantity, @product = t, b, q, p + end + BUY_INDEX = 0; QTY_INDEX = 1; PROD_INDEX = 2 + PROD_CTG = 0; CHILD = 1 + end + + list = [ + GItm.new(GItm::PROD_CTG, true, 0, "Cleaning Supplies"), + GItm.new(GItm::CHILD, true, 1, "Paper Towels"), + GItm.new(GItm::CHILD, true, 3, "Toilet Paper"), + GItm.new(GItm::PROD_CTG, true, 0, "Food"), + GItm.new(GItm::CHILD, true, 2, "Bread"), + GItm.new(GItm::CHILD, false, 1, "Butter"), + GItm.new(GItm::CHILD, true, 1, "Milk"), + GItm.new(GItm::CHILD, false, 3, "Chips"), + GItm.new(GItm::CHILD, true, 4, "Soda") + ] + treeview = Gtk::TreeView.new + setup_tree_view(treeview) + + # Create a new tree model with three columns, as Boolean, + # integer and string. + store = Gtk::TreeStore.new(TrueClass, Integer, String) + + # Avoid creation of iterators on every iterration, since they + # need to provide state information for all iterations. Hence: + # establish closure variables for iterators parent and child. + parent = child = nil + + # Add all of the products to the GtkListStore. + list.each_with_index do |e, i| + + # If the product type is a category, count the quantity + # of all of the products in the category that are going + # to be bought. + if (e.product_type == GItm::PROD_CTG) + j = i + 1 + + # Calculate how many products will be bought in + # the category. + while j < list.size && list[j].product_type != GItm::PROD_CTG + list[i].quantity += list[j].quantity if list[j].buy + j += 1 + end + + # Add the category as a new root (parent) row (element). + parent = store.append(nil) + # store.set_value(parent, GItm::BUY_INDEX, list[i].buy) # <= same as below + parent[GItm::BUY_INDEX] = list[i].buy + parent[GItm::QTY_INDEX] = list[i].quantity + parent[GItm::PROD_INDEX] = list[i].product + + # Otherwise, add the product as a child row of the category. + else + child = store.append(parent) + # store.set_value(child, GItm::BUY_INDEX, list[i].buy) # <= same as below + child[GItm::BUY_INDEX] = list[i].buy + child[GItm::QTY_INDEX] = list[i].quantity + child[GItm::PROD_INDEX] = list[i].product + end + end + + # Add the tree model to the tree view + treeview.model = store + treeview.expand_all + + scrolled_win = Gtk::ScrolledWindow.new + scrolled_win.add(treeview) + scrolled_win.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC) + + window = Gtk::Window.new("Grocery List-2 (w/toggle btt.)") + window.resizable = true + window.border_width = 10 + window.signal_connect('destroy') { Gtk.main_quit } + window.set_size_request(300, 200) + window.add(scrolled_win) + window.show_all + Gtk.main