Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Tk design tips rqd

43 views
Skip to first unread message

Derek Fountain

unread,
Sep 12, 2002, 4:57:59 AM9/12/02
to
This is a bit of a vague question, if it's a question at all. But I'll try
it anyway.

I've been writing a GUI app in Tcl/Tk and it's growing into a monster. It's
currently at about 7000 lines and there's plenty left to get done. The
thing is, I've never written anything this big before and the code is
getting scruffy. I'm starting to loose control of it.

The app contains 4 "areas", each area having a BWidget notebook to control
all it's data, controls, etc. Each notebook has between 3 and 5 pages, and
each page has between 1 and 3 lists, at least one text widget, and at least
2 entry fields. Those display and allow editing of the data the notebook
page is trying to manage. GUI components are shared where possible.
Additionally, on each page there's a number of buttons, most of which cause
a jump to another notebook page, but some offer "new", "delete", etc. Menu
entries and control buttons are dynamically added, deleted and greyed out
as appropriate for each page. Cut, copy and paste are about to be added.
None of it is too complicated, but there's a *lot* of it.

I've attempted to write all this in the manner I'm used to for Tcl/Tk,
which is basically to get the GUI looking right with all the data and
control widgets in place, then start adding functionality. This idea hasn't
scaled too well. I've got each page of each notebook in one namespace,
which felt like a good idea at the time. Now I'm finding all sort of
problems: I'm having to call routines and access variables in other
namespaces to find referenced information - my data is often somehow remote
from where I want to access it; I'm having to make leaps across namespaces
in order to switch from one page to another, often passing a mess of local
and remote variables in order to get the called routine to behave as I
want; I'm having to create more and more global variables to handle what's
on menus, which buttons are greyed out, etc. I'm getting namespace spagetti!

I've got some experience with Qt, and love it. The underlying structure
allows you to make a set a connections and forget about them, knowing that
when something changes in one part of the program, all the right things
will automatically happen elsewhere. A chain of events ripples through the
system, causing all the menus to change, buttons to invalidate, lists to
select the right entries, pages to raise as required. The closest I can see
to getting to this with Tcl/Tk is to use variable traces on key globals.
I've not had much luck with those in the past - things rapidly get
complicated and I end up with circular variable updating.

Generally speaking, what features of Tcl are there which I should be using
to keep control of this sort of data mangement application? Have I missed
an obvious technique? Do I need to go to an object system? A friend once
asked if you could write a seriously complicated application like Photoshop
in Tcl. At the time I said yes, but now I'm not so sure.

--
---
Derek

Rick Hedin

unread,
Sep 12, 2002, 1:31:05 PM9/12/02
to
> I've got some experience with Qt, and love it. The underlying structure
> allows you to make a set a connections and forget about them, knowing that
> when something changes in one part of the program, all the right things
> will automatically happen elsewhere. A chain of events ripples through the
> system, causing all the menus to change, buttons to invalidate, lists to
> select the right entries, pages to raise as required. The closest I can
see
> to getting to this with Tcl/Tk is to use variable traces on key globals.
> I've not had much luck with those in the past - things rapidly get
> complicated and I end up with circular variable updating.

Hmm. Is there a document, or a short example, that shows the difference
between Qt and Tk in this area? I would sure like to understand how they
accomplish this.


Regards,

Rick


Donal K. Fellows

unread,
Sep 13, 2002, 5:15:41 AM9/13/02
to
Derek Fountain wrote:
> Generally speaking, what features of Tcl are there which I should be using
> to keep control of this sort of data mangement application? Have I missed
> an obvious technique? Do I need to go to an object system? A friend once
> asked if you could write a seriously complicated application like Photoshop
> in Tcl. At the time I said yes, but now I'm not so sure.

I'm going to mention three things here, but I don't know how much they are a
solution to what you are after.

Firstly, I'm very keen on code to create GUIs from descriptions of them so that
all I have to do to add an element to the display is say what it is, what type
it is (which roughly equates to saying how you want it to be edited), what a
description of that value is, and where in the interface I want it to go. This
method seems to scale very well, particularly for "configure a bunch of
variables/properties" type apps.

Secondly, I really try very hard to hide away variables and force everything to
access them through procedures. OK, this is a habit I've got from doing lots of
Java programming, but it seems to scale fairly well.

Thirdly, adding an OO system is no problem. Lots of people do just that, and I
believe there are some written in pure Tcl (stooop?) so what you said about
writing complex apps in Tcl could well still be true. ;^D

Donal.
--
Donal K. Fellows http://www.cs.man.ac.uk/~fellowsd/ donal....@man.ac.uk
-- Short attention span since- ooh! Shiny thing on the floor!
-- Chad R. Orzel <orz...@earthlink.net>

Derek Fountain

unread,
Sep 13, 2002, 9:05:37 AM9/13/02
to
> I'm going to mention three things here, but I don't know how much they are
> a solution to what you are after.

Allow me to ask something more specific:

Say you have a window on screen which shows the contents of a buffer. Let's
say the buffer is internally an array and the window shows the key/value
pairs nicely. In your application's status window (or whatever), the
currently highlighted buffer entry is displayed somehow.

Elsewhere in your application a button is clicked and a new value needs to
be added to that buffer and made the "active" entry. Also, a "check the
value and possibly sound alarm" routine needs to be called. This requires
the chain of events:

data is copied to the buffer;
the window showing the buffer is updated with the new entry;
the new entry is selected (in the list or whatever);
the change in selection makes the application status window update;
the alarm checking routine needs to be called.

In Qt, the original button would emit a signal (in the Qt sense) announcing
the new data - end of story there. A slot on the buffer object would take
the signal, and would copy the new data in the buffer. It would then emit
another signal - end of story. This second signal would trigger a slot in
the window code which would cause a GUI update and reselection, which would
emit another signal - end of story. This third signal would trigger a slot
in the application status code which would update itself. The first signal
would also trigger a slot in the alarm checking object which would do it's
thing.

Now, how, generally speaking, would you go about coding that in Tcl? I'd do
it by having the script on the button call the copy-data-into-buffer code,
which in turn would call the window update, which in turn would call the
reselection, which in turn would call the status updating. The original
script would then call the alarm check. This is fine until things get more
complicated and you find yourself, say, not wanting to do the selection
step when called from a different button. So I start to reorder things and
pass extra parameters to indicate what should or shouldn't happen further
down the chain. That's the sort of problem I'm finding myself with.

Is there an alternative to my approach? Traces? Virtual events maybe?
Perhaps I'm just addicted to Qt's fire-and-forget structure and need to
change my way of thinking?

--
---
Derek

Rick Hedin

unread,
Sep 13, 2002, 4:41:58 PM9/13/02
to
(I wrote this once before. Hope it goes this time.)

Hi Derek.

I appreciate the clear example. Now I have a mental picture.

> Say you have a window on screen which shows the contents of a buffer.
Let's
> say the buffer is internally an array and the window shows the key/value
> pairs nicely. In your application's status window (or whatever), the
> currently highlighted buffer entry is displayed somehow.
>
> Elsewhere in your application a button is clicked and a new value needs to
> be added to that buffer and made the "active" entry. Also, a "check the
> value and possibly sound alarm" routine needs to be called. This requires

> the chain of events: (and following)

I have dug out TrollTech's documentation. Still mulling it over.

For years I've been mulling over writing separate modules that fire when a
set of states obtains. Seems to me this has some possibilities, not least
that you can deal with the module by itself and not have to consider what
other modules are doing. But it has yet to see the light of day.

Thanks again for the information.


Regards,

Rick

Marty Backe

unread,
Sep 13, 2002, 8:43:34 PM9/13/02
to
This is how I design 'complex' Tcl applications:

* Each major component (dialogs, menus, megawidgets, etc.) operate as
independant entities. What I mean by this is no component knows anything
about any other component (exept one, which I'll get to in a minute).
* Each component has options which define callbacks to execute when
particular actions for that component occur. For instance, a menu
component will execute a specified callback when a menu item is selected.
Which item that was selected will be passed to the callback function.
Each component will also have methods with provide any necessary feedback
to the component. Using the menu example, the callback might return some
status which then gets sent to the menu component (so for example the menu
item can be disabled).
* As mentioned in the first bullet, there does exist one 'master'
component that knows about all other components. This component is the
orchestra leader which coordinates all traffic between the components.
Thus all components (except for the master) can be independently developed
and tested (and changed) without having to touch a slew of other code.

I use Itcl/Itk to accomplish this. Each component is a seperate class file.
Very easy to create huge programs without feeling like things are getting
out of control.

BTW, what I've described is a Design Pattern, but I can't quite remember the
name right now.


Marty


--
Marty Backe
-------------------------------
http://www.lucidway.org

- Who is John Galt?

Marco Maggi

unread,
Sep 15, 2002, 4:10:57 AM9/15/02
to

"Derek Fountain" wrote:
>I've got some experience with Qt, and love it. The
>underlying structure allows you to make a set a connections
>and forget about them, knowing that when something changes
>in one part of the program, all the right things will
>automatically happen elsewhere.

You need to implement the "Observer" pattern.

*Maybe* you can try to use tags for this. Every time you
create a widget (the observer) that needs to be informed of
a set of events, insert a tag:

button .b ...
bindtags .b [concat MYTAG [bindtags .b]]

then bind virtual events:

bind MYTAG <<myevent>> [list ::ns::proc .b ...]

and append it to the list of widgets that need a
notification of the set of events:

lappend events(MYSET) .b

in one [proc]:

proc observer::registerWidget { set widget callback } {
variable events

bindtags $widget [concat $set [bindtags $widget]]
foreach e $events($set:EVENTS) {
bind $set <<$e>> [list $callback $widget $e]
}
lappent events($set:WIDGETS) $widget
}

or something like that. Now when an event in the set occurs,
you just send the event:

proc observer::sendEvent { e } {
variable events

set set $events($e)
foreach w $events($set) {
event generate $w $e
}
}

or something like that. The "events" array maps virtual
events to event sets, and event sets to widgets.

Obviously you need the Model/View/Controller pattern for
your application. I suggest to split your script in
different files and test separately the data structure
modules with the tcltest package. Personally I like this:

namespace eval mystructns {
namespace alias \
"" ::mystruct \
"" namespace inscope [namespace current]

proc ...
}

and then use the [mystruct] command to access data
structures much like [array] does (note that [namespace
inscope] adds a stack level):

mystruct subcommand structname ?arg ...?

where "subcommand" is a procedure in the "mystructns"
namespace.

Most of these "data structures" are wrappers around one or
more arrays, with added type checking private methods (I
mean... procedures) and so on.

Hope this helps.

Ciao,
Marco

--
"Love & Peace" -- Vash the Stampede

Cameron Laird

unread,
Sep 25, 2002, 10:37:56 AM9/25/02
to
In article <873csb7...@bolt.moon>,

Marco Maggi <marco...@tiscalinet.N0SPAM.it> wrote:
>
>"Derek Fountain" wrote:
>>I've got some experience with Qt, and love it. The
>>underlying structure allows you to make a set a connections
>>and forget about them, knowing that when something changes
>>in one part of the program, all the right things will
>>automatically happen elsewhere.
>
>You need to implement the "Observer" pattern.
.
[much wise advice]
.
.
I hope to accumulate more intelligence on this topic at
<URL: http://wiki.tcl.tk/4165 >.
--

Cameron Laird <Cam...@Lairds.com>
Business: http://www.Phaseit.net
Personal: http://phaseit.net/claird/home.html

ulis

unread,
Sep 25, 2002, 4:29:04 PM9/25/02
to
> I've got some experience with Qt, and love it. The underlying structure
> allows you to make a set a connections and forget about them, knowing that
> when something changes in one part of the program, all the right things
> will automatically happen elsewhere. A chain of events ripples through the
> system, causing all the menus to change, buttons to invalidate, lists to
> select the right entries, pages to raise as required.

What I do for such event:

I design some groups of events that have the same consequence (action).
I assign a virtual event to each group.
I raise the virtual event from all (widget/event) pairs that fall in this group.
I tie this virtual event to the action corresponding to the wanted consequence.

The actions can fire other virtual events.
Care must be taken in casethey they fall in recursion (->design bug).

HTH

ulis
(sorry for my so bad english)

0 new messages