Re: Two SimpleLists with one data stucture

On May 13, 2006, at 1:50 PM, Vasya Leushin wrote:

muppet ÐÐÑÐÑ:
Not sure quite what you're trying to do, so i can't tell you exactly
what you're doing wrong.  Can you describe more concretely what you
want it do?
I want to get a data structure like this:
 $v = [[1,'Color1',128,[[10,5,'White'],[20,15,'Blue'],]]];
                1st list                         2nd list
Such a complex data structure.:)

The second level of indirection on the last outer column is what i was looking for. With that in place, i don't immediately see why your original code didn't work.

Then I want this $v to be dumped to file and used in another app. Well, I add a row to the 1st list, edit it, then if I need subvalues for this row I activate it and put rows to the 2nd list. Then I add next row to the 1st list and so on... Then if I want to make some changes I activate certain row in 1st list and subvalues (if any) of this row show in the 2nd list In the end all data is in $v.

One problem with this scheme is that SimpleList does not do external data stores. That is, when you update the second list, you're actually *copying* the data out of the first list and into the second list. If the user modifies something in the second list, he's modifying a copy of the data in $s2, not the data pointed to by $s1. From what you said, this isn't quite what you want.

Of course, you can set things up such that changes in $s2 get flushed back to $s1, but that is going to be error-prone in the long run. Possible alternatives:

- Make $s2 refer to external data using custom cell data callbacks on the TreeViewColumns. This requires abandoning SimpleList for $s2 (because this sort of thing is not Simple).

- Store actual Gtk2::ListStore instances in $s1's hidden data column instead of perl arrays. Then you just set a new model into $s2 when you change the edited row.

Of course, that's not mandatory, and there is more than one way to do it, but this is one I invented. Shortly speaking, each array (even empty one) in the 4th column of each row needs to be mapped (?) to the 2nd list upon doubleclicking a row.

Rather easily done. Without much change to your original code, i knocked together a working example in only a couple of minutes.

One change i did make was to update the second list when the selection changes, rather than when the user activates the row. If the second list is supposed to let you edit the contents of a row in the first list, then it can be confusing and disorienting for the user to allow the selected row to change without changing the contents of the "external editor list". That is, i could double click row 5, edit something, then single click row six, and try to edit again -- but the second list will still be editing row 5!

The code below uses the second trick mentioned above, replacing the second SimpleList's ListStore, as well as refilling the second list on change of selection rather than row-activated. It also shows how to use TiedList outside of a SimpleList, and how to use tied() to get to the model underneath a TiedList. Rather a lot of magic, actually. :-/ But hey, it's Saturday, why not? :-)

#!/usr/bin/perl -w

use strict;
use Gtk2 -init;
use Gtk2::SimpleList;

my $window = Gtk2::Window->new;
$window->signal_connect (destroy => sub { Gtk2->main_quit });

my $hbox = Gtk2::HBox->new;
$window->add ($hbox);

my $s1 = Gtk2::SimpleList->new (
        'Number' => 'int',
        'Function' => 'text',
        'Default' => 'int',
        'Subvalues' => 'scalar',
# Hide the Subvalues column.  This will remove it from the treeview,
# not from the model.  One alternative to this is to use a column of
# SimpleList type 'hidden', but that creates a column which only holds
# text strings.  Yet another alternative is to create a new SimpleList
# column type for holding scalars in a hidden column...  but this is
# easier.  :-/
$s1->remove_column ($s1->get_column (3));

my $s2 = Gtk2::SimpleList->new (
        'Value' => 'int',
        'Midpoint' => 'int',
        'Description' => 'text',

@{ $s1->{data} } = (
[1, 'Volume', 0, make_sub_list ([ 32, 128, 'Preset1'], [ 220, 128, 'Preset2'])], [2, 'Pan', 128, make_sub_list ([ 64, 128, 'position in the stereo field'])],
  [3, 'Treble', 128, make_sub_list ([196, 128, 'high frequencies'])],
[4, 'Mid', 128, make_sub_list ([157, 128, 'midrange frequencies'])],
  [5, 'Bass',   128, make_sub_list ([200, 128, 'low frequencies'])],
  [6, 'Color1', 128, make_sub_list ([10,5,'White'],[20,15,'Blue'])],
[7, 'Whee', 255, make_sub_list ([10,5,'Ready'],[15,20,'Set'], [42,-1,'Go'])],

$hbox->add ($s1);
$hbox->add ($s2);

$s1->get_selection->signal_connect (changed => sub {
        my ($selection) = @_;
        my $slist = $selection->get_tree_view;
        my ($sel) = $slist->get_selected_indices;
        # Here's something a little bit evil...  we've stored the subvalues
        # as the TiedList objects that SimpleList uses.  However, we need the
        # underlying ListStore for the TreeView.  The ListStore is stored in
        # the TiedList object, so we have to get to that with tied().  Note
        # that we had a reference to the tied object, so we have to
        # dereference that when calling tied(), or it doesn't work.
        my $tiedlist = $slist->{data}[$sel][3];
        $s2->set_model (tied(@$tiedlist)->{model});
        # If you actually want to be able to use $s2->{data}, we have to
        # update it separately.  Perhaps we should override set_model()
        # on SimpleList?
        $s2->{data} = $tiedlist;


sub make_sub_list {
my $store = Gtk2::ListStore->new (qw(Glib::Int Glib::Int Glib::String));
        # Note this nifty trick for filling the list...
        tie my @a, 'Gtk2::SimpleList::TiedList', $store;
        @a = @_;
        return \ a;

muppet <scott at asofyet dot org>

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]