Grant Edwards wrote:
On 2008-06-14, Torsten Bronger <[EMAIL PROTECTED]> wrote:

I've never used any of the designers, but I agree 100% that
wxPython code is nasty ugly. wxPython has a very un-Pythonic
API that's is, IMO, difficult to use.
I know that such requests may start a never-ending thread but
I'd really like to know what you mean with this.

[...]

Well, if we want this thread to be never ending, I'd better put
a little dramatic hyperbole into my answer, so here goes... ;)

(blatant self-promotion warning: I'm one of the founders of Dabo, and it
sounds like you may like to take a look at it, given your comments below)


IMO, a few of the "un-Pythonic" things about wxPython are:

 1) Window ID numbers.

      "You don't need to know what it's for, just pass a -1."
Their very existence at the user level feels wrong.

    I'm told that for approximately 3 uber-sophisticated
    wxWidgets programmers window IDs can be useful in some rare
    situations.  Meanwhile everybody else working under
    "normal" conditions has to pass a useless positional
    parameter every time they instantiate a widget.  Things that
    are useful only in exceptional situations should only be
    visible in exception situations.

Dabo is a nice wrapper around wxPython, which among other things does
away with id numbers. There are a few places (wxMenu events, for one)
where you need to deal with window id's, but Dabo doesn't expose the
user to that, it handles it the way it should work.


 2) the "flags" parameter.

      "1975 called, and they want their bit-masks back."
The mashing together of a several dozen different,
    completely unrelated attributes into the "flags" parameter
    is a trick left over from C/assembly language programming
    on machines who's memory size was measure in KB.  Rather
    than OR-ing together a bunch of bit-patterns to make the
    window act the way you want, you should be setting
    individually named object attributes or passing optional,
    named parameters to the class method.

Dabo uses properties for everything, including the individual style
bits. And it handles making the setting in the right place so it "just
works" without the user needing to know, for instance, that flag x only
works after the class is fully instantiated, or vice-versa.

Properties can be set on the object once instantiated, sent to the
constructor, or set in a subclass:

# create a textbox by instantiating the baseclass
# and sending property values to the constructor:
txt = dabo.ui.dTextBox(self, Value="Hello", FontBold=True)

# tweak some other properties:
txt.FontItalic = True
txt.BackColor = "yellow"

 3) the parent/child tree

      "the only thing less well understood than Window IDs"

    I've been writing wxPython apps for about 9 years now, and
    I still have only a very vague idea what the parent/child
    tree is for.  Everybody I know just makes everything the
    child of the first panel they put in the application frame.
    The same people who specify Window IDs other than -1
    probably use complex parent/child trees for something.

Every container object needs to know about its children, and every
object needs to know about its parent. So, in Dabo we have 2 properties:
Children and Parent.

 4) sizers

      "they're like aspirin -- they work, but nobody knows exactly how"

    OK, that's a bit out-of-date since I seem to recall that
    somebody did finally figure out how aspirin works a couple
    years back.  The way sizers work seems pretty complex
    compared to other GUI toolkits I've used, and the extra
    complexity doesn't seem to provide any extra capability.
The one thing that seems to me to be particular complicated
    is controlling which objects "stretch" in what axis when a
    window is resized.  I've been using them for many years,
    but I've never gotten them more than about 90% figured out.

    Every time I write a wxPython apps, I'm initially surprised
    at its behavior when the window is resized and have to
    spend some trial-and-error time fiddling with the sizer
    parameters.  I don't remember having to do that in tkInter
    or in Trestle: things "just worked".

Sizers are admittedly a bit complex in Dabo, too. Or, sizers aren't
complex, but the code that creates them gets pretty wordy pretty fast.

vs = dabo.ui.dSizer("v")
hs = dabo.ui.dSizer("h")
hs.append(dabo.ui.dLabel(self, Caption="Name:"))
hs.append(dabo.ui.dTextBox(self))
vs.append(hs)


 5) binding

      "What? you wanted a button that _did_ something when you clicked it?"

    Binding has actually improved a bit in the past few years.
    It's not as obscure as it used to be, but it's still an
    extra explicit step that shouldn't be required. It should
    only take one line of code to create a button widget that
    calls a specified callable when it's clicked. Something
    like this:

      b = wx.Button(label="Click Me", action=myCallable)

    Instead you used to have to create a button and then call
    some utility function in some other object to bind that
    button to a callable (IIRC this was one place where Window
    IDs could be used).  Now, the button actually has a method
    you can use.  It's still an extra step...

Dabo takes event bindings as arguments to the constructor, such as:

def onButtonHit(self, evt):
  print "button hit"
but = dabo.ui.dButton(self, OnHit=self.onButtonHit)

Hit is the default event, where you use "action". For buttons, it occurs
when the user pushes it. There are a whole host of other events, such as
MouseHover, Idle, etc., and you can bind to any of them in the
constructor by using the syntax above.

We also do auto event binding:

class MyTextBox(dabo.ui.dTextBox):
  onValueChanged(self, evt):
    print "on value changed"

When instantiated, this textbox automatically runs the onValueChanged
handler when the value changes, even though we never made an explicit
binding.

(auto event binding can also be turned off easily).


 6) Thousands of wx.UPPER_CASE_INTEGER_HEX_CONSTANTS

      "After all, everything is really just a base-2 integer."
Since we don't have objects or attributes or named
    parameters or strings, all information must be passed into
    and out of the library as arbitrary integers constants. The
    really great thing about that sort of API is it's
    versatility: you can pass any value any where!  Pass a
    width in pixels where a bitmask of window attributes is
expected? No problem!

All these flags in the global wx namespace is IMO one of its biggest warts.

But in the end, wxPython is the best GUI toolkit for Python, by far,
which is why we picked it when embarking on Dabo. We definitely made the
right choice.

Well, the build I was running has finished, so that's probably
enough...

I guess I'll end the shameless self-promotion now, too.

Paul


--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to