First things first: Sorry for the substandard subject line,
but I couldn't come up with something better in half an hour's worth
of thinking. ;-) The following could be seen as a rant (though it
isn't meant to be one), as a story, and it even has a question or
two.
Maybe someone remembers the "Verifying Entry inputs" thread from
about a month ago I started.
This time I try to be less vague: I want to allow the user
to define a sequence of actions that a device (i.e. valve positioner)
shall execute. Example actions look like:
Step to 25 % for 20 s o visible
Step to 25 % for 30 s v visible
Step to 50 % for 10 s v visible
Ramp to 10 % in 30 s v visible
-- 1 -- - 2 - -3- - 4 - --- 5 ---
-- x -- denote field numbers,
o, v denote unchecked and checked ToggleButtons
My first attempt involved:
A ComboBox for field 1
Entries for fields 2 and 4 (without the units, they were just Labels),
a Label for field 3 (changes with 1, only for better english), and
a ToggleButton for field 5.
That's where I had the problems with validating the Entry inputs, as
they should be (float) numbers and be constrained to a certain range.
It did work quite well, especially the validation thing with the red
background. After playing with it for some time however, I started to
find serious flaws in my adding/removing lines logic (I didn't want to
really destroy widgets once created, so I just hid them). Anyway, I
couldn't fight the feeling that this was the wrong way to do it.
While idly playing with the pygtk demo after installing pygtk-2.6.1 I
found the editable cell demo and realized that this might get me a far
better way of representing my action list. It looked like there was
all I needed ...
Maybe I'm just plain dumb, but this whole TreeView thing looked
horrendously complex: There was the TreeView itself, ListStores,
TreeViewColumns, TreeIters, CellRenderers and you-name-its galore.
Don't get me wrong, but the documentation is rather terse. I managed
to get it mostly working with the mentioned demo, the pyGTK tutorial
(which _is_ really good, but lacking just so much of the seemingly
advanced, yet sorely needed topics), and trial-and-error programming.
I happily found a CellRendererCombo which, according to the reference,
"renders a gtk.ComboBoxEntry into a gtk.TreeView cell". No matter
what I tried, there wasn't anything resembling a ComboBoxEntry. It
behaved in every respect like a CellRendererText. Finally I found
another solution: Use a normal CellRendererText and a popup menu
for field 1. The whole thing now looks like this:
- The ListStore (model) has 4 columns: TYPE (TYPE_STRING),
POSITION (TYPE_DOUBLE), DURATION (TYPE_DOUBLE) and VISIBLE
(TYPE_BOOLEAN).
- The TreeView has 6 columns:
CellRendererText (TYPE), own cell_data_func,
CellRendererText (POSITION), editable, own cell_data_func,
CellRendererText (TYPE), own cell_data_func,
CellRendererText (DURATION), editable, own cell_data_func,
CellRendererToggle (VISIBLE), activatable,
CellRendererText [just the constant string "visible"]
The cell_data_funcs are exactly what I missed at first, and
what made it rather elegant on second try: I told the TreeViewColumn
that the 3rd renderer 'belonged' to the first column in the model also,
and do the translation "Ramp to" -> "in", "Step to" -> "for" myself.
Even better, I kept the internal token in the model and translated
both columns on rendering!
- The non-working ComboBox renderer made me find another solution that
might annoy GUI theoreticians but works rather well. I found a
'how can I have a popup menu in a TreeView' example and stole a
view lines. Right mouse button over column 1 lets you now select
'Step to' or 'Ramp to'. And colum 3 changes accordingly without me
doing anything! Hey, TreeView, although complex, is great.
- In the cell_data_funcs for the numbers I add the units.
- The "edited" signal is much more convenient than anything an Entry
widget has. Here you can easily validate the input and, if invalid,
beep at the user (this, gdk.display.beep(), does nothing for me)
and restore the previous value.
So I'm quite content now.
Some obeservations, however:
- The demos always have a 'backing store' for the data that's in the
ListStore. I saw no need for that. When I need the data, I simply
read it from the ListStore. Am I missing something?
- Although I set most columns as not editable, when you click on one
and start typing, a Entry-like widget appears to the lower right
of the TreeView and shows what is typed. Huh?
- The TreeView/ListStore makes it somewhat easy to allow the user to
rearrange the rows. Pushing a row down is a piece of cake:
model, iter = selection.get_selected()
if iter:
next = model.iter_next(iter)
model.swap(iter, next)
But moving up is rather cumbersome, as there is no iter_previous().
My solution is:
model, iter = selection.get_selected()
if iter:
path = model.get_string_from_iter(iter)
prow = int(path) - 1
prev = model.get_iter_from_string("%d" % prow)
model.swap(iter, prev)
Is there a better way? (btw., both functions make sure there is
a successor/predecessor, of course).
These are my remarks to the TreeView part of my program. Please stay tuned
so you don't miss the next episode, however it may be called. ;-)
This became rather longish, and I sure hope I didn't annoy anyone.
--
Hans-Joachim Widmaier
_______________________________________________
pygtk mailing list [email protected]
http://www.daa.com.au/mailman/listinfo/pygtk
Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/