I figured I would finally write up some of the python tips and tricks I've learned. Python is what I like to call an extremely high level language. The things that are possible in it are purely pipe dreams and wishful thinking for other languages, as I hope to demonstrate in a series of these Python "tips". Here is a pretty cool one I've been working on that will "auto-generate" the menu bar for a GUI. The following assumes at least basic knowledge of Python + GUI programming (any language)

If you have ever done GUI programming you no doubt know that coding a GUI can be extremely repetitive and boring. Sure, GUI designer tools can greatly cut back on that, but it is still repetitive. In the case of a MenuBar, it requires adding each item and then setting up a call-back for it - one by one.

However, I recently came across someone suggesting to use Python's data-driven nature to try and auto-create some GUI elements, and I thought that a MenuBar would be the perfect application for this practice. I've since banged out a simple, working method, and I am quite pleased with the results.

To start out, I will post the source for a simple demonstration of this, followed by a breakdown:


Code:
import sys, os
import wx, wx.aui

class TMGui (wx.Frame):
    """GUI class for the main view of TM"""

    # Menu dict
    _menu = {'File': ['Open', 'Close'],
             'Edit': ['Copy', 'Paste']}
    _menuKeys = ['File', 'Edit']
   
    def __init__(self, parent, id=-1, title="", pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE |
                                            wx.SUNKEN_BORDER |
                                            wx.CLIP_CHILDREN):

        wx.Frame.__init__(self, parent, id, title, pos, size, style)
       
        # tell FrameManager to manage this frame       
        self._mgr = wx.aui.AuiManager(self)
       
        # Autogen menus
        mb = wx.MenuBar()
        for m in self._menuKeys:
            root = wx.Menu()
            for i in self._menu[m]:
                item = root.Append(wx.ID_ANY, i)
                try:
                    self.Bind(wx.EVT_MENU, getattr(self, 'On' + i), item)
                except AttributeError:
                    self.Bind(wx.EVT_MENU, self.DefaultMenuHandler, item)
            mb.Append(root, m)
        self.SetMenuBar(mb)
       
        # create several text controls
        text1 = wx.TextCtrl(self, -1, 'Pane 1 - sample text',
                            wx.DefaultPosition, wx.Size(200,150),
                            wx.NO_BORDER | wx.TE_MULTILINE)

        text2 = wx.TextCtrl(self, -1, 'Pane 2 - sample text',
                            wx.DefaultPosition, wx.Size(200,150),
                            wx.NO_BORDER | wx.TE_MULTILINE)

        text3 = wx.TextCtrl(self, -1, 'Main content window',
                            wx.DefaultPosition, wx.Size(200,150),
                            wx.NO_BORDER | wx.TE_MULTILINE)

        # add the panes to the manager
        self._mgr.AddPane(text1, wx.LEFT, 'Pane Number One')
        self._mgr.AddPane(text2, wx.BOTTOM, 'Pane Number Two')
        self._mgr.AddPane(text3, wx.CENTER)

        # tell the manager to 'commit' all the changes just made
        self._mgr.Update()

        self.Bind(wx.EVT_CLOSE, self.OnClose)


    def OnClose(self, event):
        # deinitialize the frame manager
        self._mgr.UnInit()
        # delete the frame
        self.Destroy()
   
    def DefaultMenuHandler(self, event):
        print 'Couldn\'t find On%s()' % (self.GetMenuBar().FindItemById(event.GetId()).GetItemLabel())

if (__name__ == "__main__"):
    app = wx.App()
    frame = TMGui(None, size=(750, 600))
    frame.Show()
    app.MainLoop()


For the purpose of this write-up, most of the above can be ignored Smile

The first part to notice is the following

Code:
    # Menu dict
    _menu = {'File': ['Open', 'Close'],
             'Edit': ['Copy', 'Paste']}
    _menuKeys = ['File', 'Edit']
This is the method I chose in which to represent my menus in data form (I don't need sub-menus, or I might have chosen a different representation - at this point it doesn't matter how this list data is gotten or in what form it is). The _menuKeys defines the ordering for the menu's to be created, as dicts (hash-maps) aren't sorted nor does it preserve the ordering.

Now that the menu storage is in place, on to the fun stuff Smile The following is how the menus are actually created.

Code:
        # Autogen menus
        mb = wx.MenuBar()
        for m in self._menuKeys:
            root = wx.Menu()
            for i in self._menu[m]:
                item = root.Append(wx.ID_ANY, i)
                try:
                    self.Bind(wx.EVT_MENU, getattr(self, 'On' + i), item)
                except AttributeError:
                    self.Bind(wx.EVT_MENU, self.DefaultMenuHandler, item)
            mb.Append(root, m)
        self.SetMenuBar(mb)
First, a MenuBar is created - standard affair as far as that is concerned. The next step is to loop through the _menuKeys (so that the order can be specified). For each 'root' item (each value in _menuKeys), a Menu is created. Then, for each value in the list that is stored in _menu with the key value 'm' (which is one of the values of _menuKeys), loop through and add it to the 'root' Menu.

So far that should be pretty simple if you are familiar with Python, and really it would work in pretty much any language that I know of. The fun part is really in these 4 lines:

Code:
                try:
                    self.Bind(wx.EVT_MENU, getattr(self, 'On' + i), item)
                except AttributeError:
                    self.Bind(wx.EVT_MENU, self.DefaultMenuHandler, item)


What is happening here is simple, yet not something that is possible in most languages. What is basically happening, is getattr() tries to access an attribute (in this case a function - but this isn't being enforced in any way) and retrieve it. Essentially getattr(self, X) == self.X. However, since 'X' in this case is a string, the latter isn't possible. If the attribute isn't found it throws an AttributeError exception, which is caught and then a default event handler function is used. The function used (either from getattr or the default) is then bound to the menu item being created to use as its callback function.

To see this in action, you need Python (tested with 2.5 - but any version should work as this isn't anything newly added) and wxPython (best stick with the latest version, however, as this demo is using the new-ish AUI frame manager).

The cool thing with this is that callback functions are auto-bound to the menu item, which makes editing a menu extremely simple. Try it out. At the end of the TMGui class, add the following and run the program:

Code:
    def OnCopy(self, event):
        print 'Copied some crap :)'
Now instead of printing out the default "Couldn't find OnCopy()" it will print out "Copied some crap Smile".

For a 'twist' on this, one could bind all menu items to the default handler, and then in the default handler call getattr to try and find the callback function. By doing it that way, the callback function can be created at runtime and it would work perfectly. Basically you would just have to change the DefaultMenuHandler to the following:

Code:
    def DefaultMenuHandler(self, event):
        mName = self.GetMenuBar().FindItemById(event.GetId()).GetItemLabel()
        try:
            getattr(self, 'On' + mName)(event)
        except AttributeError:
            print 'Couldn\'t find On%s()' % mName
       
    def OnCopy(self, event):
        self.OnPaste = self.OnClose


Now if you run it and choose Edit->Paste, it will print out "Couldn't find OnPaste()". However, if you selected Edit->Copy, and then choose Edit->Paste, the program will close, as there now IS a self.OnPaste() function - which was "created" in the OnCopy() function.

Neat, huh?

suggestions/other ideas you would like to see worked out are welcome - always looking for a challenge Smile
/me blinks

I'm sure I'd understand that if I pulled up the wxpython reference and took some time to read it, but.. I won't. Needless to say, that's some impressive work. If I was going to try to implement something like that, I might do something involving function lookups in the globals() dictionary. How? I have no idea right now, not that it matters anyway.

Ah, ok, that's actually quite simple after reading it once, reading your explanation, then reading it again. Well done. That kind of makes me want to come up with an excuse to write a GUI application in python (the last thing I wrote was six lines or so to fix the length of a rom image).
The Tari wrote:
/me blinks

I'm sure I'd understand that if I pulled up the wxpython reference and took some time to read it, but.. I won't. Needless to say, that's some impressive work. If I was going to try to implement something like that, I might do something involving function lookups in the globals() dictionary. How? I have no idea right now, not that it matters anyway.

Ah, ok, that's actually quite simple after reading it once, reading your explanation, then reading it again. Well done. That kind of makes me want to come up with an excuse to write a GUI application in python (the last thing I wrote was six lines or so to fix the length of a rom image).


Same thing would work for console apps - the point was more to hint at the possibilities of using getattr Wink

Oh, and I didn't make note of this earlier, but getattr will still work in an inherited class. Eg the following would work still:


Code:
class foo(TMGui):
   def OnOpen(self, event):
      print "lawl"
That looks neat, but I didn't understand all of it (I guess I didn't meet the "basic knowledge of Python" requirement). Python seems like a more interesting and fun language than I originally thought, though.
bfr wrote:
That looks neat, but I didn't understand all of it (I guess I didn't meet the "basic knowledge of Python" requirement). Python seems like a more interesting and fun language than I originally thought, though.


Give it a whirl, its a great language. Easily my favorite language to work with. The official tutorial is excellent: http://docs.python.org/tut/tut.html
the only language/library combination I've ever enjoyed designing GUIs in is AWT for Java, and that's just because its so simple. nice work.
elfprince13 wrote:
the only language/library combination I've ever enjoyed designing GUIs in is AWT for Java, and that's just because its so simple. nice work.


Oh god, GUIs in Java (at least with Swing) are HORRIBLE. They are a crime against humanity. Seriously, Java GUIs need to be put down. I would almost prefer using the craptacular atrocity in piss-poor design that is Win32 than use Swing in Java. *shudders*

And what the hell is with the retarded layout managers? Seriously, the border layout? What the hell were they smoking when they came up with that piece of crap?
Kllrnohj wrote:
[
Oh god, GUIs in Java (at least with Swing) are HORRIBLE. They are a crime against humanity.

I agree, AWT GUIs aren't that bad, but are fail in the feature department.

Quote:
And what the hell is with the retarded layout managers? Seriously, the border layout? What the hell were they smoking when they came up with that piece of crap?

CS 101 Students. personally I prefer a null layout manager for simple stuff, or a gridbag.
Kindly explain a gridbag for those of use not fluent in Java?
KermMartian wrote:
Kindly explain a gridbag for those of use not fluent in Java?


http://java.sun.com/javase/6/docs/api/java/awt/GridBagLayout.html

Essentially it aligns objects along a grid - hence the "Grid" part of the name Wink

But wow Kerm, 8 days later, haha - oh well, reminded me to do another one of these. I was thinking "misc python tricks" (things python offers that other languages don't - sort of a quick jump guide for those familiar with other languages and just starting in Python)...
I knew I'd get either a JFGI or a necro response, but I figured I might as well bump the thread.
Kllrnohj wrote:
But wow Kerm, 8 days later, haha - oh well, reminded me to do another one of these. I was thinking "misc python tricks" (things python offers that other languages don't - sort of a quick jump guide for those familiar with other languages and just starting in Python)...


which reminds me....are you as sad as I am that Guido dropping the LISPish features from Python 3000? I can't stand LISP on its own, but python with the ability to use some of the gnarly functional stuff is amazing.
elfprince13 wrote:
which reminds me....are you as sad as I am that Guido dropping the LISPish features from Python 3000? I can't stand LISP on its own, but python with the ability to use some of the gnarly functional stuff is amazing.


I haven't kept up with Python 3.0 development - what LISPish features in particular are you talking about?
Kllrnohj wrote:
I haven't kept up with Python 3.0 development - what LISPish features in particular are you talking about?

they were planning on removing lamba, for a long time actually, but I just checked and its been added back, however stuff like map and filter have been changed into iterators for some bizarre reason. and as far as I know reduce is still be removed. quite honestly, I'm thinking about not upgrading, because I like my languages to become less , not more, complicated to use (as in more flexible, less safe). Anyone who can't read the code doesn't deserve to be programming in a language that supports it. as much as I like python, I'm still not real happy about the lack of lack of the ?: operator, and I don't want more features being pulled.
elfprince13 wrote:
Anyone who can't read the code doesn't deserve to be programming in a language that supports it. as much as I like python, I'm still not real happy about the lack of lack of the ?: operator, and I don't want more features being pulled.


That is totally not a good thought process to have when coding Very Happy

Besides, the ternary operators are great for making code hard to read, but they suck because they don't offer anything. Its still an if/else, so its not like you gain any speed...
I rather like lambda. Granted, I've only used it once (well, twice, but they were in the same place of the program with only slight changes between them). The only thing I really miss from Python is a do:while construct (yeah, it doesn't fit into the enforced indentation scheme, but I'd still like it). It's not a big deal, though, since that can be done with a while loop and one more line of code.
Kllrnohj wrote:
That is totally not a good thought process to have when coding Very Happy

your smiley says it all Razz


Code:
public static int width(IntBST t) { return (t == null) ? 0 : (t.isLeaf() ? 2 : ((t.left() == null && t.right != null) ? 1 + width(t.right()) : ((t.left() != null && t.right == null) ? 1 + width(t.left()) : width(t.left()) + width(t.right()) ) ) ); }

surely that's perfectly clear to you? Wink
if its not, take 30 seconds to indent it, and it will be.
in all seriousness, 90% of my programming is on my own and I want the flexibility to be able to use stuff like that, because I don't care if other people have to read it. and even when I'm programming on a team (which I know I will more in the future) I still want the flexibility to be able to do that because (1) comments? (2) its still easy to read if you take the time to indent (3) with code that simple, if it works it doesn't need to be changed and the speed boost is worth the crap maintainability (4) its the language developers duty to make it easy on the programmer, not to decide what they can and can't do, especially for a general purpose language like python

Quote:
Besides, the ternary operators are great for making code hard to read, but they suck because they don't offer anything. Its still an if/else, so its not like you gain any speed...

in a lot of C-ish programming languages using ?: for assignment is 4-6 times then using if-else
Somewhere in that last sentence I believe you meant to put a +"faster".
elfprince13 wrote:
surely that's perfectly clear to you? Wink
if its not, take 30 seconds to indent it, and it will be.
in all seriousness, 90% of my programming is on my own and I want the flexibility to be able to use stuff like that, because I don't care if other people have to read it. and even when I'm programming on a team (which I know I will more in the future) I still want the flexibility to be able to do that because (1) comments? (2) its still easy to read if you take the time to indent (3) with code that simple, if it works it doesn't need to be changed and the speed boost is worth the crap maintainability (4) its the language developers duty to make it easy on the programmer, not to decide what they can and can't do, especially for a general purpose language like python


Yeah, its "clear", but if I was ever on a project with you and you coded something like that, I would stab you in the face Razz

Quote:
in a lot of C-ish programming languages using ?: for assignment is 4-6 times then using if-else


This isn't C though, but that doesn't matter. Ternaries aren't any faster than a regular if-then-else, because they ARE a regular if-then-else.

Go ahead and test this yourself... For me, ternaries were about 1 second slower than an if-then-else


Code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

unsigned long ITERATIONS = 2147483648;

int main(int argc, char *argv[])
{
    int beg, mid, end;
    int a, i;
    beg = clock();
    for (i = 0; i < ITERATIONS; i++)
        a = (i < beg) ? 0 : 1;
    mid = clock();
    for (i = 0; i < ITERATIONS; i++) {
        if (i < beg)
            a = 0;
        else
            a = 1;
    }
    end = clock();
    printf("First: %d\n", (mid - beg));
    printf("Second: %d\n", (end - mid));
    system("PAUSE");
    return 0;
}
I should realy get back to learning lIsp or Python...
Than maybe some of this would make sense to me :)
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 3
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement