Tix Vs Pmw which is better to use?
Cimarron Taylor
cimarron+google at taylors.org
Thu May 2 07:05:21 EDT 2002
I have not used Tix, but I have recently started working with Pmw.
Overall I'm pretty happy with the result. First the good things:
1. Pmw is easy to use and there are lots of examples!
2. Pmw has a very nice Notebook widget which makes it easy to create
the tab-dialog interface I wanted.
Now for the things which frustrated me:
3. Not much thought seems to have been given to making the megawidgets
support a consistant interface for reading and writing the data
values. you often have to violate encapsulation to do something
as mundane as setting the value of a widget. For example, suppose
I create a Pmw.Counter with the following code
counter = Pmw.Counter(
parent, labelpos='w', label_text='Number of tests',
entryfield_value=10, datatype='numeric',
entryfield_validate={'validator':'numeric', 'min':0, 'max':100},
increment=10
)
To set the value of the counter later on, I need to invoke the
setentry() method on the internal entry widget of the counter:
counter._counterEntry.setentry(value)
So how did I know to do this? The answer is, I studied the demos and
the source code and tried lots of things until I found something that
worked for this specific widget. Now suppose somewhere else I create
a read-only text box with the following code:
textbox = Pmw.ScrolledText(
parent, borderframe=1, text_padx=5, text_pady=5,
text_wrap='none', vscrollmode='static', hscrollmode='static'
)
textbox._textbox.configure(state='disabled')
I could find no obvious way to make the text box read-only without
once again acessing an internal member of the widget. Also, by trial
and error I have discovered that I can set the text of the widget via
textbox._textbox.configure(state='normal')
textbox.clear()
textbox.settext(value)
textbox._textbox.configure(state='disabled')
You get the idea. To me, the most important thing a megawidget must do
is manage the value shown to the user and one of the important programming
services the megawidget layer should provide is a simple and consistent
way of accessing that value. Hence I ended up rolling my own adaptor
classes for this purpose. Here is my own Counter:
class Counter:
"class providing a common get/set interface to Pmw.Counter"
def __init__(self, parent, labeltext, init, min, max, incr=1):
self.counter = Pmw.Counter(
parent, labelpos='w', label_text=labeltext,
entryfield_value=init, datatype='numeric',
entryfield_validate={'validator':'numeric', 'min':min, 'max':max},
increment=incr)
self.counter._counterEntry._entryFieldEntry.config(bg='white')
def get(self):
return self.counter._counterEntry.get()
def set(self, value):
return self.counter._counterEntry.setentry(value)
4. Pmw has a less-than-useful color abstraction. The Pmw.Color class
may be useful if you want to change the entire color scheme of an
application but it is difficult to use with a single widget. For example,
the Pmw.Color example suggests
defaultPalette = Pmw.Color.getdefaultpalette(parent)
normalcolor = Pmw.Color.changebrightness(parent, 'white', 0.85)
Pmw.Color.setscheme(parent, normalcolor)
.. create your widget here ..
apply(Pmw.Color.setscheme, (parent,), defaultPalette)
but suppose you want only the editable area of the counter to
have a white background and not the entire widget to be white.
The code you see above is a dead-end. To do what I wanted you
will need something like
self.counter._counterEntry._entryFieldEntry.config(bg='white')
5. The font system is also a little less than useful. Suppose you
want your text box to use a fixed width font. Once again you
will need to access the internal member:
typewriter = apply(Pmw.logicalfont, ('Typewriter', 0), {})
textbox = Pmw.ScrolledText(
parent, borderframe=1, text_padx=5, text_pady=5,
text_wrap='none', vscrollmode='static', hscrollmode='static')
textbox._textbox.configure(font=typewriter)
6. Be very careful about which geometry management scheme you choose.
If you accidently mix .pack() and .grid(), you may end up with an
application which freezes up the first time you resize it (of course,
this is probably more of a Tkinter problem than a Pmw one).
Now a question for the group. I would like to add to my interface a
text area which would contain the output of a separate long running make
process I need to spawn. I can't just call system() as that would block
the entire ui (unless I setup a separate thread). I've tried using fork()
and exec() but that seems to cause an X server error which kills the entire
application.
The best thing I've found so far is to run the separate process in an
xterm window by creating an instance of the following class:
class StartMake:
def __init__(self, target):
os.popen('xterm -e make %s &' % target)
I know there are probably a few paths I could start down:
a. use threads (yeah I know, but seems like overkill for this)
b. get a window id of a Tkinter widget and have xterm use that window (yuck!)
c. use a Tkinter alarm to poll the tail of a file containing the make output
(this is what I might end up doing, but seems clumsy)
Any suggestions?
Cim
More information about the Python-list
mailing list