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

Laura Creighton lac at strakt.com
Fri Feb 8 15:27:52 CET 2002


> Laura,
> 
> Thanks very much for the code. I think it is very helpful for me.

You are most welcome.

> I have analyzed your program and it seems to contain a lot of good
> tricks. 

Isn't python cool?

>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 ;<

I think you are correct there.  
> 
> >
> >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?

If I did the pmw approach my code would be idiosyncratic, personal,
tailored for me, different by using names that I pick for the
dictionary.  Every time I got a new cool idea of how i might want
to configure a widget I would have to think up a new name and
write more code.  My approach just lets me use the native tkinter
way of doing things. 

One virtue of it was that I could haul this code out of a directory,
make 17 changes, test it, and post it here so you could have it.
This means that my code is relatively reusable, more so than you
would get if you made your less anonymous.

But I do this stuff without conscious thought.  So sometimes
when I do things like that its just the wrong approach.  But I
still like this.  Might be better to make it a factory function,
though.

What I lose is the ability to use keyword arguments fg='cyan' and the
like.  I thought it was worth it.

> 
> 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).

I mean  labelDict and widgetDict

> >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

ah, it _is_ the widget.
> 
> >    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.

{'text':'A label label:'}  -- that is dictionary 1
{'text':"I'm a label", 'fg':'honeydew'} -- that is dictionary 2

> 
> >    # 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.

What are you talking about? I used it with an Entry, a Button and a Label,
and it worked great.

This is just a quick bit of code for testing purposes, as the comment
said.  The name 'doit' is sort of a give away as well.

> 
> >
> >    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?

Sloppiness when I did a cut and paste. and later when I proofread
the article.

> 
> >
> >    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.

No. this is just standard boilerplate testing junk that I use for
testing widgets I make all the time.  There is nothing remarkable
about this, it is just that I write the tests first, and the code
later.  So I wanted to make sure that I sent you a widget that
was capable of doing this, so these were the tests I made first.

> 
> >
> >    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

I'm afraid I am not following you here.  If you post all the code and
the test cases you are using to not generate what you want I suspect
we could fix this in pretty short order.

Laura Creighton




More information about the Python-list mailing list