Tkinter widgets: Write options *and* option tags dynamically from dictionary?

Johannes Eble skywalkerpackage at hotmail.com
Thu Feb 7 19:36:29 CET 2002


Laura,

Thanks very much for the code. I think it is very helpful for me.
I have analyzed your program and it seems to contain a lot of good
tricks. However, I probably didn't understand the main principle
behind. I suggest the best is if I wrtte some commends in between your
code

On Wed, 06 Feb 2002 18:23:55 +0100, Laura Creighton <lac at strakt.com>
wrote:

...

>> This doesn't work together with an optionDict={'labelText':'hello',
>> 'padx':'6'}
>---------------------------------------------------^^^^^^^^
>
>That is your problem.  Tkinter labels want to get their text
>set with text='hello', not labelText='hello'

Yes. Then, what I wanted to do define a configure (or a config) method
that worked exactly as the one(s) in the Tkinter module.
Unfortunately, I don't think I will understand the code inside Tkinter
in limited time ;<

>
>You have to make some design decisions here.  If you want to pass one big
>dictionary, you will then have to unpack it, parse it, and generate
>the correct keys for the correct widgets.  This is do-able; it is
>what PMW does.  But I didn't like that approach.  I just use 2 dictionaries.

Why didn't you like the PMW approach?

2 dictionaries for what? Do you mean one master dictionary for your
own megawidgets and for basic (Tkinter/Pmw) widget which appear in the
higher level of your GUI, and one or more lowlevel dictionaries for
the basic widgets which are contained by the highlevel widgets? I am
not clear what kinds of the two dictionaries you mean (see below).

>
>You also mention:
>>But then, how do I handle default values, and how do I ensure that the
>>objects are created with the right parameters? Maybe I should use a
>>local dictionary.
>
>[snip]
>
>>Is there an elegant solution to this problem?
>
>Well, I don't know if you would call this elegent but ...
>
>
>from __future__ import nested_scopes
>import Tkinter
>
>class LabelledWidget(Tkinter.Frame):

Ok, this is an example of your own megawidget

>    def __init__(self, parent, widgetName=Tkinter.Entry,
>                 labelDict={}, widgetDict={}, **frameDict):
>        
>        Tkinter.Frame.__init__(self, parent, **frameDict)
>
>        """
>        let us say that you always want the label to be written on a
>        cyan background in purple letters, unless you ask differently.
>        The widget you just want on a green background.
>        """
>        
>        ldefaults={'bg': 'cyan', 'fg' : 'purple'}
>        wdefaults={'bg': 'green'}
>        # overwrite any of the defaults with the dictionary you passed
>        ldefaults.update(labelDict)
>        wdefaults.update(widgetDict)
>       
>        self.label = Tkinter.Label(self, **ldefaults)
>        self.label.grid(row=0, column=0, sticky='w')
>        self.widget = widgetName(self, **wdefaults)
>        self.widget.grid(row=0, column=1, sticky='e')
>
>########
>if __name__ == '__main__':
>    root = Tkinter.Tk()
>    widgetList = (
>        [Tkinter.Entry,
>         {'text':'An entryfield label:'},{}],
>        [Tkinter.Button,
>         {'text':'A button label:', 'bg':'peachpuff'},
>         {'text':"I'm a button"}],
>        [Tkinter.Label,
>         {'text':'A label label:'}, {'text':"I'm a label", 'fg':'honeydew'}],
>    )

This is probably one of your two kinds of dictionaries, right?
It is specific for your megawidget, so, in general, you use one
dictionary for the construction of your megawidget out of its basic
(Tkinter) widgets.

>    # for testing.
>    def doit(widgetlist, part, method, **dict):
>        for w in widgetlist:
>            object = getattr(w, part, None)
>            if object:
>                command = getattr(object, method, None)
>                if callable(command):
>                    command(**dict)

This is a very interesting routine, but you can only use it together
with a uniform widgetlist (that is, for example, a list containing
only LabelWidget objects). Also, you can only apply the function to
one subwidget (basic widget) and one subwidget method thereof.

>
>    r = 0
>    widgets=[]
>    for name, ld, wd in widgetList:
>        w = LabelledWidget(root, name, ld, wd)
>        widgets.append(w)
>        w.grid(row=r, col=0, sticky='ew')
>        r += 1
>
>    r = 0
>    for w in widgets:
>        w.grid(row=r, col=0)
>        r += 1

Is there a reason why you have called the grid manager twice?

>
>    cmdList = (
>        ['pink label', 'label', 'configure', {'fg':'pink'}],
>        ['yellow widget', 'widget', 'configure', {'bg':'yellow'}],
>        )

Ok, I guess that is the second dictionary. It contains attributes of
the megawidgets (that is, the basic widgets) and the options. Well, I
can't see much more.

>
>    c = 0
>    for txt, part, method, dict in cmdList:
>        button = Tkinter.Button(root, text=txt,
>                                command=lambda w=widgets,
>                                p=part, m=method, d=dict :
>                                doit(w, p, m, **d))
>        button.grid(row=r, col=c, sticky='w')
>        c += 1
>    button = Tkinter.Button(root, text='Exit', command=root.destroy)
>    button.grid(row=r, col=c)
>    
>    root.mainloop()
>--------------------------------------
>
>Laura Creighton
>

Ok, what I try to do is the following:

I want to create a dialog box (a GUI window). This dialog box is
divided into several panes. The panes do have widgets (basic widgets
*and* composed widgets) in each side (left and right).

The structure of my dictionary (the only as you said is the case in
Pmw) is as follows

    # lstPanes = [(pane1_text, 
[(widget_pane1_left, {option_dict_pane1_left}),
 (widget_pane1_right, {option_dict_pane1_right})
]                         ),
                          (pane2_text,

.......

For example, the following list defines a dialog box with three panes.
The first pane has two LabelEntry megawidgets (my own created) on the
left and on the right, respectively. The second pane ('Compilation
mode') consists of a radiobar (again my own widget, just a column of
several radio bars) to the left and so on.
>From the third pane it is clear that the widgets inside a pane can
change and don't have to belong to one widget class. Also, the amount
of columns is variable (one in the second pane and three in the third
pane).


    lstPanes = [('Root class definition', [
                            (LabelEntry, {'labelText':'Root class'}),
                            (LabelEntry, {'labelText':'Root
procedure'})
                            ]),
                ('Compilation mode', [
                            (Radiobar2, {'radioTexts':['boost,
'no_check', 'all_checks, 'debug_check']})
                            ]),
                ('Pane3', [
                            (Checkbox, {....}),
                            (LabelEntry, {'labelText':'label5'}),
                            (RadioButton, {...})
                            ])
                ]


This scheme already works with Tkinter widgets in the columns of the
panes, but not with my own megawidgets (neither with Pmw megawidgets).

I would just find it useful to be able to code a whole GUI window in
this way, but perhaps it is not worth the effort after all.


Johannes



More information about the Python-list mailing list