HI all,
Robin just reminded us of the wxPython Coding Guidelines page in the Wiki. As it happens, I had just started a little style guide with my ideas about how to write modern pythonic wxPython code.
The Coding Guidelines currently in the Wiki cover the usual stuff about how to style your code, but nothing wxPython specific. As wxPython has evolved over the years, a number of changes have been introduced that make the code cleaner, clearer, and more pythonic. Unfortunately, as the various demo code out there has been written over the years, it's not always clear to new users (or old users) what the best style is.
My goal is for us to come to a consensus (with Robin the ultimate decision maker) about what is good wxPython style, then ideally, we can all work together to go back and edit the demo, wiki, etc, to make it consistent with this style. That's a bit ambitious, but perhaps we can at least get new code in the wiki, etc to be a consistent style.
I'll kick this off my submitting my first thoughts on good wxPython style. Please add your suggestions and discussion of mine, where you might disagree.
-Chris
A Style Guide for wxPython
--Chris Barker (Chris.Barker@noaa.gov)
This is a little style guide for using wxPython. It's not the be-all and
end-all of how wxPython code should be written, but I what I've tried to
capture is away to write wxPython code that is clear and Pythonic.
1) Use "import wx" NEVER use "from wxPython.wx import *".
Don't use "import *" for other libs either.
2) Use keyword arguments in constructors:
MainFrame = wx.Frame(None, title = "A Title", size = (500, 400))
This lets you avoid putting in a bunch of unneeded defaults, like:
wx.DefaultSize, wx.DefaultPosition, wx.ID_ANY, etc.
3) Don't use IDs. There is very rarely a good reason to use them.
a) Most Widget constructors will fill in a default ID for you, so you
don't have to specify one at all. Other arguments can be specified as
key word arguments (see above):
MyFrame = wx.Frame(None, title="A Title", size = (400,400))
AButton = wx.Button(self, label="Push Me")
b) If the id is a required argument, use wx.ID_ANY. wx.ID_ANY == -1,
but I like to use it because it makes the code very clear. And who
knows, maybe that magic value will change one day.
4) Use the Bind() method to bind events:
a)
AButton = wx.Button(self, label="Push Me")
AButton.Bind(wx.EVT_BUTTON, self.OnButtonPush)
b) You can use Bind() for menus too, even though they don't have a
Bind() method, in this way:
FileMenu = wx.Menu()
item = wx.MenuItem(FileMenu, wx.ID_ANY, "&Quit")
FileMenu.AppendItem(item)
self.Bind(wx.EVT_MENU, self.OnQuit, item)
(where self is a wx.Frame)
5) wx.App() now has the same built in functionality as wx.PySimpleApp(),
so there is no need for the later.
7) Use Sizers!
6) Use separate, custom classes rather than nesting lots of wx.Panels in
one class. If you find yourself doing this in an __init__:
self.MainPanel = wx.Panel(self, ...
self.SubPanel1 = wx.Panel(self.MainPanel, ..)
self.SubPanel2 = wx.Panel(self.SubPanel1, ...)
MyButton = wx.Button(self.SubPanel2, ....)
Then you are creating an ugly, hard to maintain mess! Instead, create
custom classes for the stuff that all is working together in a panel:
class MainPanel(wx.Panel):
....
class SubPanel1(wx.Panel):
....
etc.
You'll also find that by doing this, you're less likely to break the
"Don't Repeat Yourself" (DRY) principle. Often you'll find that you
have group of widgets that are common to various parts of your
app. If you put them on a custom panel, you'll be able to re-use that
code.
7) Here's a very small sample demonstrating this style:
#!/usr/bin/env python2.4
import wx
class DemoFrame(wx.Frame):
""" This window displays a button """
def __init__(self, title = "Micro App"):
wx.Frame.__init__(self, None , -1, title)
MenuBar = wx.MenuBar()
FileMenu = wx.Menu()
item = wx.MenuItem(FileMenu, text = "&Quit")
FileMenu.AppendItem(item)
self.Bind(wx.EVT_MENU, self.OnQuit, item)
MenuBar.Append(FileMenu, "&File")
self.SetMenuBar(MenuBar)
btn = wx.Button(self, label = "Quit")
btn.Bind(wx.EVT_BUTTON, self.OnQuit )
self.Bind(wx.EVT_CLOSE, self.OnQuit)
self.Fit()
def OnQuit(self,Event):
self.Destroy()
app = wx.App()
frame = DemoFrame()
frame.Show()
app.MainLoop()
···
--
Christopher Barker, Ph.D.
Oceanographer
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception