wxPython: Struggling with some of the concepts

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

Cadmus
Posts: 26
Joined: Thu Jan 22, 2009 6:01 pm UTC

wxPython: Struggling with some of the concepts

Postby Cadmus » Wed Dec 15, 2010 4:18 pm UTC

Ok, so for making little helpers, trackers, loggers, and other such useful utilities I've taken to writing applications in wxPython.

Now my Python is reasonable, but what I'm struggling with is the hierachy and conceptual layout of the wx elements. It's not helped that the tutorials vary wildly in coding style.

As I understand it we have applications, which are running programs. Applications contain frames, which are windows. Finally we have panels, which are there to group widgets with related functionality.

So what items do you put inside others? Should your frame contain all panels that will be in it? Or do you make your own panel outside then call it inside the constructor for the frame? If you need to pass variables to new frames and panels (say you have very similar panels for 'New' and 'Edit' and you want to specify which mode it should open in, but re-use a lot of the controls) what's the best way to do it.

Apologies if that makes no sense, I suppose what I'd like to find is a 'best practice' for wxPython, or at least your thoughts on how to arrange wxPython code.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11129
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: wxPython: Struggling with some of the concepts

Postby Yakk » Wed Dec 15, 2010 5:48 pm UTC

You might want to step back and look at generic design patterns for a moment.

The UI should hold very little in the form of persistent application state. Most of its state should be a reflection of the applications actual state, rather than storing it.

The model-view-controller pattern, and more generally data-driven design, might be of help. MVC is famous and easy to research, but be aware that MVC is the pattern, many people will talk about MVC in the context of a specific implementation, which isn't the important part.

Data-driven design involves reducing the amount of code, and instead having the behavior of your application be based off of "data". So instead of having code that says there is a button with the name foo, you have a description of where the button is, what the name of the button is, and what hook to call when it is pressed stored. Then you have a description of what the form that the button is in and how they are arranged stored somewhere. Then you invoke the creation of the panel, which loads the form and all of the buttons contained.

This is always a matter of degree -- a completely data-driven application is theoretically possible, but not usually ideal. Instead, make the parts that are uniform be data-driven -- instead of using copy/paste, you have one set of code that loads the differences.

Other useful patterns are observable -- when you have a slider, attach it to some observable variable in the application. Now any changes to that variable can be observed by the slider (without custom slider code per variable) and reflected, automatically. Reflection is somewhat useful -- the core applications interface to the UI may be string-based (or string and GUID based, where either is unique, but GUIDs are faster), where the UI asks for observable variables to twerk and/or functions to call via name or GUID. (names are more optimal, because they are programmer-readable, which helps with debugging -- if you hare performance issues, map the string to a UID of some kind, then store the UID and the name next to each other in a way that prevents them from becoming decoherent).

And then -- you create a window by naming a UID for the file. This UID causes your application to load the file, which describes the layout of the window, and loads the controls into it. This possibly recurses. Almost every control comes from a small set of base classes, with a custom control factory that loads the handful of exceptions.

But that might be overkill for your application. :)
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
sparkyb
Posts: 1091
Joined: Thu Sep 06, 2007 7:30 pm UTC
Location: Camberville proper!
Contact:

Re: wxPython: Struggling with some of the concepts

Postby sparkyb » Thu Dec 16, 2010 3:09 am UTC

I'll describe a couple different patterns that I use with wxPython. Spoilered because it is a frickin book.

Spoiler:
Like Yakk suggested with an MVC kind of framework, which my more polished apps I tend to write a class(es) that contain all the state and logic about the task (the Model). Individual operations would be methods. I design this object so that it can be used independent of any particular GUI (and often I do use it GUI-less from an interactive python shell when testing). Then I build a separate module which is the GUI (View-Controller). My main GUI class (more about what that is later) will then hold an instance of my core class (the Model). Sometimes I can have the GUI instantiate it (especially in the case of something document oriented that has a New or Open option), or when there is only one global core object that needs to precede the GUI, I'll construct it before the GUI in the main() function and pass it into the GUI. Then on the GUI class I create methods which are event handlers (for buttons or menu items or whatever) that wrap functions on the model object. For instance, if my model can be saved it probably has a .save(filename) that does all the actual work. Then the GUI class would probably have a save menu option and a method like .onSave(event) that creates and shows a file dialog to get a filename and then calls .save on the model with that filename, and turning the return value or any exceptions into other message boxes and things. Sometimes I don't really bother creating a separate model class and just have the main class be both the GUI and the holder of the data. I still usually try to have separate methods for the meat of the operation and event handler functions that wrap them, though.

Typically my main GUI class is a subclass of wx.Frame that represent my main window that there'll be a single instance of created in the main function. I don't really see much need for wxWidgets' App classes, at least not in python, so I don't usually subclass that. In fact my "main function" is usually the following snippet at the bottom of my main python file (where ProjectFoo is the name of my main window frame class, named after whatever the project is called):

Code: Select all

if (__name__=="__main__"):
    app = wx.PySimpleApp()
    win = ProjectFoo()
    win.Show()
    app.MainLoop()


It varies sometimes if I need to create an instance of the model class in there at the top and pass it to the window contructor, or if there needs to be command-line parsing or anything, but that's pretty close to most of my wxPython apps. When you asked about passing things to the wx class constructors, notice that I didn't pass anything to my main frame constructor. My wx.Frame subclass's constructor will first call the wx.Frame constructor. Usually I already know in my subclass the defaults I want to use, so I encapsulate that within my subclass's constructor. Sometimes the subclass constructor will take parameter, usually that define application specific settings, but sometimes are either direct passthroughs to the wx.Frame constructor, or have an effect on the defaults passed in. Sometimes I just make the constructor take a **kwargs that I pass straight through in case I ever want to pass extra arbitrary arguments to the wx.Frame constructor. After that the constructor goes about setting up its state and stuff like a usual class and then builds the rest of the GUI that it contains. Sometimes this is right in the constructor but most of the time I like to move this to a separate .setupGUI() method that the constructor calls.

As far as setting up the GUI, I rarely subclass anything else. If I use panels just for layout/containment then I just directly create a wx.Panel. The exception is if I have a particular collection of controls that perform a specific task and I need the same or similar is several place (multiple tabs perhaps?) then I might create it as a separate re-usable class that would subclass from wx.Panel. And of course I create separate subclass of wx.Frame/wx.Dialog for any other custom windows/dialogs I use. And even more rarely I'll subclass something like wx.ListCtrl or wx.Table or something if I have a type of list that I was really customized behavior, whether I need to use multiple of them or even if it is only a single one but I want to abstract it out of the main frame class.

My setupGUI function is generally broken down into two sections (sometimes separate methods) where I set up the contents of the window and setup the menus. I usually put the binding of event handlers right in line with creation of the controls that generate the events that they handle, but some more global kinds of event handlers are a third section at the end. Another thing I do is that I don't save every widget I create as a member variable on the class. Any one I'd need to access later like lists if I want to change their contents or ask which is selected, checkboxes or text boxes if I need to know their value, or buttons/menuitems that I might need to gray out or change the text of I would. But if I'm not ever planning on changing it and it only exists for layout or to trigger events I general do not save references to panels, sizers, menus/menuitems, buttons, or labels. I'll use local variables to create them, register an event handler, and add them to a sizer and then forget about them.

Because wxWidgets is actually a C++ library just wrapped for use as wxPython, it is typical to see some non-pythonic patterns that people have brought over from C++ wxWidgets, but wxPython is a really good set of wrappers so here's some things to stay away from if you want to keep your stuff pythonic. always pass wx.ID_ANY to every widget constructor and never used explicit IDs. Well, at least not any app-specific custom IDs. There are a few common IDs like wx.ID_OK that are good to use for things like the OK button of any custom dialogs you create. Also, don't use the macro style for registering event handlers or register the event handlers on the frame class using the ID for the child you care about. I always register the event handlers right on the widget itself even though the method that I use as the handler is a method of the frame class. For instance: refreshButton.Bind(wx.EVT_BUTTON,wx.onRefresh). The only exception to this method of registered event handlers is menu items where for some reason this method doesn't work. Instead you can pass the menu item to Bind. This is a simple form of what my menu section would look like:

Code: Select all

menubar = wx.MenuBar()

menu = wx.Menu()
mi = menu.Append(wx.ID_ANY, "&Save...")
self.Bind(wx.EVT_MENU,self.onSave,mi)
menubar.Append(menu,"&File")

self.SetMenuBar(menubar)


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 12 guests