wxPython入门

第一个应用程序: "Hello, World"

作为常见,我们首先编写一个小的“Hello,world”应用程序。下面是代码:

   1 #!/usr/bin/env python
   2 import wx
   3 
   4 app = wx.App(False)  #创建一个新的应用程序对象,不能直接输出标准输出流/标准错误流到一个窗口。
   5 frame = wx.Frame(None, wx.ID_ANY, "Hello World") #一个框架作为顶级窗口。
   6 frame.Show(True)     #显示这个框架.
   7 app.MainLoop()

注释:

app = wx.App(False)

每个wxPython应用程序对象都是wx.App的一个实例。 对于大多数简单的应用程序可以使用wx.App。 当你需要一个更加复杂的应用时,可能需要扩展wx.App 类。 "False"意味着"不能直接输出标准输出流/标准错误流到一个窗口"。

wx.Frame(None,wx.ID_ANY,"Hello")

wx.Frame是一个顶级窗口。 语法是: x.Frame(Parent, Id, Title)。大部分都是这样的结构 (一个父对象,Id). 举个例子, 我们使用None 代表 "没有父窗口"和wx.ID_ANY由wxWidgets设定一个id .

frame.Show(True)

显示这个框架。

app.MainLoop()

最后,我们开始应用程序的MainLoop,其作用是处理事件。

: 你总是想使用wx.ID_ANY或者另一个标准ID为了wxWidgets. 你也可以生成自己的IDs, 但是没必要每次这样做。

运行这个,你会看到这样的效果:

窗口还是框架?

当人们谈论到图形界面时,通常会说到窗口、菜单和图标。 很自然, 你期望 wx.Window 意味着屏幕上的窗口。不幸的是,不是这样的.一个wx.Window是所有可见元素的基类(按钮, 菜单等),并且我们通常所认为程序窗口是一个wx.Frame。这个不一致会导致很多新的用户混乱。

构建一个简单的文本编辑器

在本教程中,我们将构建一个简单的文本编辑器.在这个过程中,我们将探讨几个组件,并了解事件和回调等特点。

第一步

第一步就是建立一个带文本框的框架。 一个文本框是一个 wx.TextCtrl 组件。 默认情况下,文本框是单行模式,但是你可以使用 wx.TE_MULTILINE参数允许输入多行文本。

   1 #!/usr/bin/env python
   2 import wx
   3 class MyFrame(wx.Frame):
   4     """ We simply derive a new class of Frame. """
   5     def __init__(self, parent, title):
   6         wx.Frame.__init__(self, parent, title=title, size=(200,100))
   7         self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
   8         self.Show(True)
   9 
  10 app = wx.App(False)
  11 frame = MyFrame(None, 'Small editor')
  12 app.MainLoop()

在这个例子中,我们从wx.Frame 派生和覆盖其 __init__ 方法。 在这里,我们声明一个新 wx.TextCtrl,一个简单的文本编辑框。请注意,由于MyFrame 已经运行了self.Show()__init__方法, 我们不再需要调用 frame.Show()

添加一个菜单栏

每个应用程序都应该有一个菜单栏和状态栏。将它们添加:

   1 import wx
   2 
   3 class MainWindow(wx.Frame):
   4     def __init__(self, parent, title):
   5         wx.Frame.__init__(self, parent, title=title, size=(200,100))
   6         self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
   7         self.CreateStatusBar() #窗口下创建一个状态栏
   8 
   9         #创建菜单
  10         filemenu= wx.Menu()
  11 
  12         #wx.ID_ABOUT和wx.ID_EXIT是由wxWidgets提供的标准ID
  13         filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
  14         filemenu.AppendSeparator()
  15         filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
  16 
  17         #创建菜单栏
  18         menuBar = wx.MenuBar()
  19         menuBar.Append(filemenu,"&File") #添加"filemenu"到菜单栏
  20         self.SetMenuBar(menuBar)  #添加菜单栏
  21         self.Show(True)
  22 
  23 app = wx.App(False)
  24 frame = MainWindow(None, "Sample editor")
  25 app.MainLoop()

提示: 注意wx.ID_ABOUTwx.ID_EXIT 标识. 这些是wxWidgets提供的标准标识(点击这里查看完整清单).如果一个可用的,使用标准的ID是个好习惯。这有助于wxWidgets知道如何在每个平台显示,使它看起来更加本地化。

事件处理

在wxPython中对事件做出反应称为事件处理。事件指一些事情发生在你的应用程序 (按钮单击, 文本输入, 鼠标移动, 等等)。 GUI编程的大部分是对事件作出响应。 绑定一个对象事件使用Bind()方法:

   1 class MainWindow(wx.Frame):
   2     def __init__(self, parent, title):
   3         wx.Frame.__init__(self,parent, title=title, size=(200,100))
   4         ...
   5         menuItem = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
   6         self.Bind(wx.EVT_MENU, self.OnAbout, menuItem)

这意味着,从现在起,当用户选择 "About"菜单项,方法self.OnAbout将被执行。 wx.EVT_MENU是"选择菜单项"事件。您可以了解更多的事件(查看完整列表)。 self.OnAbout方法的通用声明如下:

   1 def OnAbout(self, event):
   2         ...

在这里,event事件是wx.Event子类的实例。 举个例子,按钮单击事件 - wx.EVT_BUTTON - 是wx.Event的一个子类。

该方法是在事件发生时执行。 默认情况下,方法处理事件并在回调完成后停止。然而,你可以使用 event.Skip() "跳过"一个事件。这会导致事件通过这个层次的事件处理。举个例子:

   1 def OnButtonClick(self, event):
   2     if (some_condition):
   3         do_something()
   4     else:
   5         event.Skip()
   6 
   7 def OnEvent(self, event):
   8     ...

当一个按钮单击事件发生时,该方法OnButtonClick被调用。如果 some_condition是true,我们 do_something()否则,我们让该事件是由更普遍的事件处理程序处理。现在让我们看下程序:

   1 import os
   2 import wx
   3 
   4 
   5 class MainWindow(wx.Frame):
   6     def __init__(self, parent, title):
   7         wx.Frame.__init__(self, parent, title=title, size=(200,100))
   8         self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
   9         self.CreateStatusBar() #窗口下创建一个状态栏
  10 
  11         #创建菜单
  12         filemenu= wx.Menu()
  13 
  14         #wx.ID_ABOUT和wx.ID_EXIT是由wxWidgets提供的标准ID
  15         menuAbout = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
  16         menuExit = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
  17 
  18         #创建菜单栏
  19         menuBar = wx.MenuBar()
  20         menuBar.Append(filemenu,"&File") #添加"filemenu"到菜单栏
  21         self.SetMenuBar(menuBar)  #添加菜单栏
  22 
  23         #设置事件
  24         self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
  25         self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
  26 
  27         self.Show(True)
  28 
  29     def OnAbout(self,e):
  30         #OK按钮的一个消息对话框。 wx.OK是wxWidgets的一个标准ID。
  31         dlg = wx.MessageDialog( self, "A small text editor", "About Sample Editor", wx.OK)
  32         dlg.ShowModal() #显示他
  33         dlg.Destroy() #最后完成时销毁他
  34 
  35     def OnExit(self,e):
  36         self.Close(True)  #关闭框架
  37 
  38 app = wx.App(False)
  39 frame = MainWindow(None, "Sample editor")
  40 app.MainLoop()

   1 wx.MessageDialog( self, "A small editor in wxPython", "About Sample Editor", wx.OK)

我们可以省略ID。在这种情况下wxWidget自动生成一个ID(就像如果你指定的wx.ID_ANY):

   1 wx.MessageDialog( self, "A small editor in wxPython", "About Sample Editor")

对话框

当然如果一个编辑器不能保存或打开文档,那么是没用的。这就需要通用对话框了。常见的对话框由底层平台提供,以至于让应用程序看起来更加地本地化。这是一个OnOpen方法的使用在 MainWindow中:

   1     def OnOpen(self,e):
   2         """ Open a file"""
   3         self.dirname = ''
   4         dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", wx.OPEN)
   5         if dlg.ShowModal() == wx.ID_OK:
   6             self.filename = dlg.GetFilename()
   7             self.dirname = dlg.GetDirectory()
   8             f = open(os.path.join(self.dirname, self.filename), 'r')
   9             self.control.SetValue(f.read())
  10             f.close()
  11         dlg.Destroy()

说明:

您现在应该可以进入菜单中添加相应的条目,并把它连接到 OnOpen方法。 如果遇到了问题,可以向下滚动查看完整的源代码。

可能的扩展

当然,这个还远远没达到像样的编辑器。但是,加入其他功能应该不是非常的困难。你也许可以从wxPython示例中找到灵感:

窗口化工作

在这节,我们将要学习窗口和所包含内容的处理方式,包括构建输入表单和使用各个部件/控制。 我们将要编写一个计算报价的小程序。如果您已经是经验丰富的图形用户界面开发人员,这将是容易的,你可以继续前进到本页面Boa-Constructor的学习在高级主题章节中。

概述

布局可视元素

这个文档集中于wxSizers的使用,因为这些是我最熟悉的方案。

Sizers

sizers较常见的类型包括:

   1 import wx
   2 import os
   3 
   4 class MainWindow(wx.Frame):
   5     def __init__(self, parent, title):
   6         self.dirname=''
   7 
   8         # A "-1" in the size parameter instructs wxWidgets to use the default size.
   9         # In this case, we select 200px width and the default height.
  10         wx.Frame.__init__(self, parent, title=title, size=(200,-1))
  11         self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
  12         self.CreateStatusBar() # A Statusbar in the bottom of the window
  13 
  14         # Setting up the menu.
  15         filemenu= wx.Menu()
  16         menuOpen = filemenu.Append(wx.ID_OPEN, "&Open"," Open a file to edit")
  17         menuAbout= filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
  18         menuExit = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")
  19 
  20         # Creating the menubar.
  21         menuBar = wx.MenuBar()
  22         menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
  23         self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.
  24 
  25         # Events.
  26         self.Bind(wx.EVT_MENU, self.OnOpen, menuOpen)
  27         self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
  28         self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
  29 
  30         self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
  31         self.buttons = []
  32         for i in range(0, 6):
  33             self.buttons.append(wx.Button(self, -1, "Button &"+str(i)))
  34             self.sizer2.Add(self.buttons[i], 1, wx.EXPAND)
  35 
  36         # Use some sizers to see layout options
  37         self.sizer = wx.BoxSizer(wx.VERTICAL)
  38         self.sizer.Add(self.control, 1, wx.EXPAND)
  39         self.sizer.Add(self.sizer2, 0, wx.EXPAND)
  40 
  41         #Layout sizers
  42         self.SetSizer(self.sizer)
  43         self.SetAutoLayout(1)
  44         self.sizer.Fit(self)
  45         self.Show()
  46 
  47     def OnAbout(self,e):
  48         # Create a message dialog box
  49         dlg = wx.MessageDialog(self, " A sample editor \n in wxPython", "About Sample Editor", wx.OK)
  50         dlg.ShowModal() # Shows it
  51         dlg.Destroy() # finally destroy it when finished.
  52 
  53     def OnExit(self,e):
  54         self.Close(True)  # Close the frame.
  55 
  56     def OnOpen(self,e):
  57         """ Open a file"""
  58         dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", wx.OPEN)
  59         if dlg.ShowModal() == wx.ID_OK:
  60             self.filename = dlg.GetFilename()
  61             self.dirname = dlg.GetDirectory()
  62             f = open(os.path.join(self.dirname, self.filename), 'r')
  63             self.control.SetValue(f.read())
  64             f.close()
  65         dlg.Destroy()
  66 
  67 app = wx.App(False)
  68 frame = MainWindow(None, "Sample editor")
  69 app.MainLoop()

   1 window.SetSizer(sizer)
   2 window.SetAutoLayout(true)
   3 sizer.Fit(window)

Validators

A Working Example

Our first label within a panel

   1 import wx
   2 class ExampleFrame(wx.Frame):
   3     def __init__(self, parent):
   4         wx.Frame.__init__(self, parent)
   5         self.quote = wx.StaticText(self, label="Your quote :", pos=(20, 30), size=(200, -1))
   6         self.Show()
   7 
   8 app = wx.App(False)
   9 ExampleFrame(None)
  10 app.MainLoop()

   1         self.quote = wx.StaticText(self, label="Your quote :", pos=(20, 30))

[7] According to wxPython documentation:

[8] A label is used to display text that is not supposed to interact with the user.

Adding a few more controls

   1 clearButton = wx.Button(self, wx.ID_CLEAR, "Clear")
   2 self.Bind(wx.EVT_BUTTON, self.OnClear, clearButton)

   1 textField = wx.TextCtrl(self)
   2 self.Bind(wx.EVT_TEXT, self.OnChange, textField)
   3 self.Bind(wx.EVT_CHAR, self.OnKeyPress, textField)

   1 import wx
   2 class ExamplePanel(wx.Panel):
   3     def __init__(self, parent):
   4         wx.Panel.__init__(self, parent)
   5         self.quote = wx.StaticText(self, label="Your quote :", pos=(20, 30))
   6 
   7         # A multiline TextCtrl - This is here to show how the events work in this program, don't pay too much attention to it
   8         self.logger = wx.TextCtrl(self, pos=(300,20), size=(200,300), style=wx.TE_MULTILINE | wx.TE_READONLY)
   9 
  10         # A button
  11         self.button =wx.Button(self, label="Save", pos=(200, 325))
  12         self.Bind(wx.EVT_BUTTON, self.OnClick,self.button)
  13 
  14         # the edit control - one line version.
  15         self.lblname = wx.StaticText(self, label="Your name :", pos=(20,60))
  16         self.editname = wx.TextCtrl(self, value="Enter here your name", pos=(150, 60), size=(140,-1))
  17         self.Bind(wx.EVT_TEXT, self.EvtText, self.editname)
  18         self.Bind(wx.EVT_CHAR, self.EvtChar, self.editname)
  19 
  20         # the combobox Control
  21         self.sampleList = ['friends', 'advertising', 'web search', 'Yellow Pages']
  22         self.lblhear = wx.StaticText(self, label="How did you hear from us ?", pos=(20, 90))
  23         self.edithear = wx.ComboBox(self, pos=(150, 90), size=(95, -1), choices=self.sampleList, style=wx.CB_DROPDOWN)
  24         self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, self.edithear)
  25         self.Bind(wx.EVT_TEXT, self.EvtText,self.edithear)
  26 
  27         # Checkbox
  28         self.insure = wx.CheckBox(self, label="Do you want Insured Shipment ?", pos=(20,180))
  29         self.Bind(wx.EVT_CHECKBOX, self.EvtCheckBox, self.insure)
  30 
  31         # Radio Boxes
  32         radioList = ['blue', 'red', 'yellow', 'orange', 'green', 'purple', 'navy blue', 'black', 'gray']
  33         rb = wx.RadioBox(self, label="What color would you like ?", pos=(20, 210), choices=radioList,  majorDimension=3,
  34                          style=wx.RA_SPECIFY_COLS)
  35         self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, rb)
  36 
  37     def EvtRadioBox(self, event):
  38         self.logger.AppendText('EvtRadioBox: %d\n' % event.GetInt())
  39     def EvtComboBox(self, event):
  40         self.logger.AppendText('EvtComboBox: %s\n' % event.GetString())
  41     def OnClick(self,event):
  42         self.logger.AppendText(" Click on object with Id %d\n" %event.GetId())
  43     def EvtText(self, event):
  44         self.logger.AppendText('EvtText: %s\n' % event.GetString())
  45     def EvtChar(self, event):
  46         self.logger.AppendText('EvtChar: %d\n' % event.GetKeyCode())
  47         event.Skip()
  48     def EvtCheckBox(self, event):
  49         self.logger.AppendText('EvtCheckBox: %d\n' % event.Checked())
  50 
  51 
  52 app = wx.App(False)
  53 frame = wx.Frame(None)
  54 panel = ExamplePanel(frame)
  55 frame.Show()
  56 app.MainLoop()

The notebook

Note how ExamplePanel's parent is the Notebook. This is important.

   1 app = wx.App(False)
   2 frame = wx.Frame(None, title="Demo with Notebook")
   3 nb = wx.Notebook(frame)
   4 
   5 
   6 nb.AddPage(ExamplePanel(nb), "Absolute Positioning")
   7 nb.AddPage(ExamplePanel(nb), "Page Two")
   8 nb.AddPage(ExamplePanel(nb), "Page Three")
   9 frame.Show()
  10 app.MainLoop()

Improving the layout - Using Sizers

Here is the sample above re-written to use sizers:

   1 class ExamplePanel(wx.Panel):
   2     def __init__(self, parent):
   3         wx.Panel.__init__(self, parent)
   4 
   5         # create some sizers
   6         mainSizer = wx.BoxSizer(wx.VERTICAL)
   7         grid = wx.GridBagSizer(hgap=5, vgap=5)
   8         hSizer = wx.BoxSizer(wx.HORIZONTAL)
   9 
  10         self.quote = wx.StaticText(self, label="Your quote: ")
  11         grid.Add(self.quote, pos=(0,0))
  12 
  13         # A multiline TextCtrl - This is here to show how the events work in this program, don't pay too much attention to it
  14         self.logger = wx.TextCtrl(self, size=(200,300), style=wx.TE_MULTILINE | wx.TE_READONLY)
  15 
  16         # A button
  17         self.button =wx.Button(self, label="Save")
  18         self.Bind(wx.EVT_BUTTON, self.OnClick,self.button)
  19 
  20         # the edit control - one line version.
  21         self.lblname = wx.StaticText(self, label="Your name :")
  22         grid.Add(self.lblname, pos=(1,0))
  23         self.editname = wx.TextCtrl(self, value="Enter here your name", size=(140,-1))
  24         grid.Add(self.editname, pos=(1,1))
  25         self.Bind(wx.EVT_TEXT, self.EvtText, self.editname)
  26         self.Bind(wx.EVT_CHAR, self.EvtChar, self.editname)
  27 
  28         # the combobox Control
  29         self.sampleList = ['friends', 'advertising', 'web search', 'Yellow Pages']
  30         self.lblhear = wx.StaticText(self, label="How did you hear from us ?")
  31         grid.Add(self.lblhear, pos=(3,0))
  32         self.edithear = wx.ComboBox(self, size=(95, -1), choices=self.sampleList, style=wx.CB_DROPDOWN)
  33         grid.Add(self.edithear, pos=(3,1))
  34         self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, self.edithear)
  35         self.Bind(wx.EVT_TEXT, self.EvtText,self.edithear)
  36 
  37         # add a spacer to the sizer
  38         grid.Add((10, 40), pos=(2,0))
  39 
  40         # Checkbox
  41         self.insure = wx.CheckBox(self, label="Do you want Insured Shipment ?")
  42         grid.Add(self.insure, pos=(4,0), span=(1,2), flag=wx.BOTTOM, border=5)
  43         self.Bind(wx.EVT_CHECKBOX, self.EvtCheckBox, self.insure)
  44 
  45         # Radio Boxes
  46         radioList = ['blue', 'red', 'yellow', 'orange', 'green', 'purple', 'navy blue', 'black', 'gray']
  47         rb = wx.RadioBox(self, label="What color would you like ?", pos=(20, 210), choices=radioList,  majorDimension=3,
  48                          style=wx.RA_SPECIFY_COLS)
  49         grid.Add(rb, pos=(5,0), span=(1,2))
  50         self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, rb)
  51 
  52         hSizer.Add(grid, 0, wx.ALL, 5)
  53         hSizer.Add(self.logger)
  54         mainSizer.Add(hSizer, 0, wx.ALL, 5)
  55         mainSizer.Add(self.button, 0, wx.CENTER)
  56         self.SetSizerAndFit(mainSizer)

This example uses a GridBagSizer to place its items. The "pos" argument controls where in the grid the widget is placed, in (x, y) positions. For instance, (0, 0) is the top-left position, whereas (3, 5) would be the third row down, and the fifth column across. The span argument allows a widget to span over multiple rows or columns.

Responding to User Actions

Overview

A Working Example

Drawing

Overview

A Working Example

Using wxPython

Debugging Techniques

   1 class MyApp (wx.App):
   2 #...
   3 #...
   4 #...
   5 myapp = MyApp() # functions normally. Stdio is redirected to its own window
   6 myapp = MyApp(0) #does not redirect stdout. Tracebacks will show up at the console.
   7 myapp = MyApp(1, 'filespec') #redirects stdout to the file 'filespec'
   8 # NOTE: These are named parameters, so you can do this for improved readability:
   9 myapp = MyApp(redirect = 1, filename = 'filespec') # will redirect stdout to 'filespec'
  10 myapp = MyApp(redirect = 0) #stdio will stay at the console...

You can also use the Widget Inspection Tool to help debug most layout issues.

TODO: Discuss source debuggers, event loop conflicts, etc.

PyCrust interactive shell

wxPython distribution comes with the nice PyCrust shell. With it you can interactvely test your layout. Here is a screenshot with a sample code.

Deploying your wxPython Application

Next Steps

Events

So then "self.Bind(wx.EVT_SOMETHING, ACallable)" would mean:

When a SOMETHING event is delivered to this Window (self), and it comes from any child window or itself, then call ACallable, and self must be a class derived from a wxPython window (e.g. a Button, a Dialog, a Frame), and "ACallable" can be any function, though the usual choice by the programmer is to make it a function within the above-mentioned class.

The second version "self.Bind(wx.EVT_SOMETHING, ACallable, srcWin)" means:

When a SOMETHING event is generated by "srcWin", and it comes up through the window hierarchy to this Window (self), then call ACallable.

But some events can only be caught in the Window in which they are generated (which means the second form then won't do anything), so it is best to use the first form whenever possible, which would be basically everywhere except for menu items, which don't have a Bind() method.

Scintilla

Boa-constructor

multi threading

Managed/ Non Managed windows

Useful resources

As a conclusion- slithering our way to the future of GUI-apps

You have now covered the main aspects of wxPython programming and should be able to start writing wxPython applications. Do not hesitate to take part in the wxPython community by subscribing to the mailing lists and by posting questions or answers on those lists.

Contributors

Appendix

Small editor - Complete Source

Building Forms - Complete source

Drawing with wxPython - Complete Source

A rudimentary project organizer - Complete Source

Frequently Asked Questions

Comments

Comment Sample

Feel free to add any remarks or comments here. Content suggestions are welcome as well as corrections...

OS independent path concatenation

This code:

   1 import os
   2 f=open(os.path.join(self.dirname,self.filename),'r')

...can still cause problems on Windows in cases where you've used the common dialog to navigate to the root dir of the drive. In that case, the common dialog function will return self.dirname='c:' (you'd expect 'c:\' unless you've dealt with MS for many years). Unfortunately, os.path.join won't join as expected when the string contains a ':', and so an error occurs when opening 'c:file.py', since this is dependent on the cwd setting (assuming 'file.py' exists in 'c:\', but not in your cwd).

Solutions? I use:

   1 f=open(self.dirname+'/'+self.filename,'r')

without any problems. I presume that lower level functions like "open" probably call os.path.normpath() which fixes it on Windows. The advantage to this is that I can use the same method ('/') for directories & URL's, and the code runs cross-platform Win/Linux. I'm sure it will get me someday, though. A better option would probably be:

   1 f=open(os.path.normpath(os.path.join(self.dirname+os.sep,self.filename)),'r')

I think this works for all cases except where self.dirname is 'c:\' before you start (normpath won't strip extra separators on the 1st join arg). Or perhaps an elegant solution using os.path.isabs() would be better.

Use the sizer in one step

I always thought setting sizer must be shorter than three lines an indeed it is (at least for wxWidgets/wxPython 2.4), the used above

   1 window.SetSizer(sizer)
   2 window.SetAutoLayout(true)
   3 sizer.Fit(window)

might be shorten to one line

   1 window.SetSizerAndFit(sizer)

How to get tabs to work

It's beeen really hard to me getting tabs to work in non-dialog windows: the only hint in the whole documentation is in wxWindow style wxTAB_TRAVERSAL description, but it's not so clear. What I finally got was:

1)For the tabbing to work at all, the window or individual panel you plonk controls/wigits on has to have as part of its style flag the following: wxTAB_TRAVERSAL ie;

   1 class ContactsPanel(wx.Panel):
   2     def __init__(self, parent,id):
   3         wx.Panel.__init__(self, parent, id, wx.DefaultPosition,wx.DefaultSize,
   4                          wx.RAISED_BORDER|wx.TAB_TRAVERSAL)

2) The tab order is set by the order you add controls to the panel or frame.

(information comes from http://mail.gnu.org/archive/html/gnumed-devel/2002-07/msg00015.html)

3) Tabbing order also seems to be dependent in the order widgets are created. I assume this is due to widget ID numbers. Order of addition to sizers/panels did not seem to help me with #2. -- Keith Veleba

4) Here's a little idiom for setting tab order once you have the controls set up:

   1 order = (control1, control2, control3, ...)
   2 for i in xrange(len(order) - 1):
   3     order[i+1].MoveAfterInTabOrder(order[i])

(The list contains the actual control objects.) This makes it easy to change the order, add new controls, etc. -- Don Dwiggins

入门 (last edited 2011-01-06 12:20:24 by awu)