wxPython的基础教程

2023-11-07

写在前面的话:上个假期学习了python,发现它真的是一门很有趣的语言,所以这学期想学一些python的可视化编程,于是选择了wxPython。但是我在网上找中文教程找了好久都没有找到中文的教程(额,也许是我方法不对),无奈只好看英文的啦。于是在这个网站上看完了wxPython的基础教程,但是为了方便广大网友所以决定将这个网页中的内容翻译过来。花费了3个晚上的时间,终于把它翻译完了。但是我只是一个普通的大学生(还是属于四级没过的那种),能力有限可能有些地方翻译的不对或者不够准确,所以如果有更好的翻译方式的话请通过我的邮箱g975291783@163.com偷摸地告诉我,我会及时的改正(我将不胜感激),希望通过大家的帮助可以使这篇文章变得更加准确和完善。

大家在看的时候会发现有些地方会出现“残缺不全”的问题,这不是我偷懒,是原网页上真的没有写。对于这个问题如果以后有时间的话我可以试试用别的资料补上。大家当然也可以自己去找找资料。好了,文章有点长,大家可以记住网址以后慢慢看。

 

原文地址:http://wiki.wxpython.org/Getting%20Started

第一个应用程序:“Hello World

作为传统,我们首先将要写一个小的“Hello World”程序,下面是他的代码:

 

#!/usr/bin/env python
import wx

app = wx.App(False)  # Create a new app, don't redirect stdout/stderr to a window.
frame = wx.Frame(None, wx.ID_ANY, "Hello World") # A Frame is a top-level window.
frame.Show(True)     # Show the frame.
app.MainLoop()

解释:

App = wx.App(False)

每一个wxPython应用程序都是wx.App这个类的一个实例。对于大多数简单的应用程序来说,你可以直接使用wx.App这个类。当你需要更复杂的功能的时候,你也许就需要去扩展wx.App类。参数“False”,意味着不重定向标准输出和错误输出到窗口上。

 

wx.Frame(None, wx.ID_ANY, “Hello”)

wx.Frame类是一个顶层窗口。它的用法是wx.Frame(Parent, Id, Title)。对于大对数的构造函数来说,都有这种通用的形式(一个父窗口名,后面紧随着它的Id)。在这个例子当中,我们使用None ,来说明没用父窗口,并且使用ID_ANY,来拥有一个wxWidgets分配给我们的ID 号。

 

frame.Show(True)

我们使一个窗口可见,通过这个函数。如果将参数改为False,你会发现程序真的在运行,但是我们看不到。

 

app.MainLoop()

最后,我们开始应用程序的MainLoop函数,它用来处理各种事件。

 

Note你应该使用wx.ID_ANY或者wxWidgets提供的其他标准ID。你也可以使用自己创建的ID,但是没有理由那么做。

 

运行程序,然后你应该看到一个类似与这样的一个窗口:

hello.png

(在不同的系统平台下,这个窗口的样子可能差距很大)

 

窗口 还是 框架?

当人们谈论GUI 的时候,他们通常会说窗口,菜单和图标。然后自然而然地,你期望应该使用wx.Window来表示一个窗口。不幸的是,情况并不是这样的。wx.Window是一个基类,所有的可视化控件都是从这个类派生出来的(比如说按钮,菜单等等)。我们平时想到的程序窗口是一个wx.Frame类。对于许多初学者来说,这是一个不幸的矛盾。

 

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

在这个教程中,我们将要建立一个简单的文本编辑器。在这个过程中,我们将会探索许多的widgets知识,并且学习到关于事件处理和回调函数的一些知识。

 

第一步:

第一步先编写一个简单的框架,里面包含一个可编辑的文本框。文本框可以通过wx.TextCtrl类进行创建。默认情况下,文本框是单行的,但是使用wx.TE_MULTILINE参数可以允许你在文本框中输入多行文本。

#!/usr/bin/env python
import wx
class MyFrame(wx.Frame):
    """ We simply derive a new class of Frame. """
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title, size=(200,100))
        self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        self.Show(True)

app = wx.App(False)
frame = MyFrame(None, 'Small editor')
app.MainLoop()

在这个例子当中,我们派生了wx.Frame类,并且重写了它的__init__方法。在这个方法中,我们声明了一个新的wx.TextCtrl实例,它是一个简单的文本编辑控件。注意:因为MyFrame类运行了self.Show()在它的__init__方法中,所以我们不需要再显式地调用frame.Show()

 

添加一个菜单栏

每一个程序应该用一个菜单栏和一个状态栏。让我们添加它们到我们的程序当中:

import wx

class MainWindow(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title, size=(200,100))
        self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        self.CreateStatusBar() # A Statusbar in the bottom of the window

        # Setting up the menu.
        filemenu= wx.Menu()

        # wx.ID_ABOUT and wx.ID_EXIT are standard IDs provided by wxWidgets.
        filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
        filemenu.AppendSeparator()
        filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")

        # Creating the menubar.
        menuBar = wx.MenuBar()
        menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
        self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.
        self.Show(True)

app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()

提示:注意那个wx.ID_ABOUTwx.ID_EXIT。它们是wxWidgets提供的标准ID(查看全部的ID列表)。使用标准ID是一个好的习惯,如果它存在的话。这有助于让wxWidgets在不同的平台上使每一个控件的ID都看起来更加自然。

 

事件处理

wxPython中,对事件的响应,称作事件处理。事件就是指发生在你的程序当中的某些事情(一个按钮被按下,文本输入,鼠标移动等等)。GUI编程的很大一部分是由事件的响应组成的。你可以使用Bind()方法,将一个控件和事件绑定到一起。

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

这意味着从现在开始,当用户选择About菜单项的时候,self.OnAbout方法将会被执行。wx.EVT_MENU选择菜单项事件。wxWidgets也会处理许多其他的事件(查看全部列表)。self.OnAbout方法一般是这样定义的:

def OnAbout(self, event):
        ...

这里的 event 是从 wx.Event 派生出一个子类的实例。比如说按钮点击事件 -wx.EVT_BUTTON 就是 wx.Event 的一个子类。

 

当事件发生的时候,这个方法被执行。默认情况下,这个方法将会处理事件,并且在回调函数结束后,事件终止。但是,你可以跳过一个事件通过event.Skip()方法。这将会导致事件直接跨过事件用户处理层。比如说这样:

def OnButtonClick(self, event):
    if (some_condition):
        do_something()
    else:
        event.Skip()

def OnEvent(self, event):
    ...

当一个按钮点击事件发生时, OnButtonClick 方法被调用。如果 some_condition 是真,我们就执行 do_something() ,否则我们让这个事件用系统默认方式所处理。现在让我们看看我们的程序:

import os
import wx


class MainWindow(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title, size=(200,100))
        self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        self.CreateStatusBar() # A StatusBar in the bottom of the window

        # Setting up the menu.
        filemenu= wx.Menu()

        # wx.ID_ABOUT and wx.ID_EXIT are standard ids provided by wxWidgets.
        menuAbout = filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
        menuExit = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")

        # Creating the menubar.
        menuBar = wx.MenuBar()
        menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
        self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.

        # Set events.
        self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)
        self.Bind(wx.EVT_MENU, self.OnExit, menuExit)

        self.Show(True)

    def OnAbout(self,e):
        # A message dialog box with an OK button. wx.OK is a standard ID in wxWidgets.
        dlg = wx.MessageDialog( self, "A small text editor", "About Sample Editor", wx.OK)
        dlg.ShowModal() # Show it
        dlg.Destroy() # finally destroy it when finished.

    def OnExit(self,e):
        self.Close(True)  # Close the frame.

app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()

Note

 

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

在这个例子中,我们可以忽略IDwxWidget会自动使用一个默认的ID(就像我们指定了wx.ID_ANY一样)。

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

对话框

当然,如果一个编辑器没用打开和保存功能,那么它几乎是没用的。那就到了展现通用对话框的时候了。通用对话框是由底层平台提供的,通过它可以是你的程序看起来更像是一个完整的程序。这里实现了MainWindow中的OnOpen方法。

    def OnOpen(self,e):
        """ Open a file"""
        self.dirname = ''
        dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", wx.OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            self.filename = dlg.GetFilename()
            self.dirname = dlg.GetDirectory()
            f = open(os.path.join(self.dirname, self.filename), 'r')
            self.control.SetValue(f.read())
            f.close()
        dlg.Destroy()

解释:

首先,我们创建了对话框,通过调用合适的构造函数。

然后,我们调用了ShowModal。通过它,打开了对话框。“Modal(模式/模态)意味着在用户点击了确定按钮或者取消按钮之前,他不能在该程序中做任何事情。

ShowModal的返回值是被按下的按钮的ID。如果用户点击了确定按钮,我们就读文件。

 

你现在应该可以向菜单中添加相应的内容,并且将它和OnOpen方法链接起来。如果你有问题,可以翻到下面的附录,去查看完整的代码。

 

可能的扩展

当然,这个程序距离一个像样的编辑器还差的很远。但是添加其他的功能不会比我们已经完成的部分难。你也许会从这些和wxPython绑定的样例代码中得到灵感。

拖放

MDI

选项卡视图/多文件

查找/替换对话框

打印对话框(印刷)

Python中的宏命令(使用eval函数)

等等...

 

在窗口中工作

标题:

框架

窗口

控件/工具

布局管理

验证器

在这段中,我们将学习如何使用wxPython处理窗口和它们的组件,包括构建输入框和使用各种各种的控件。我们将创建一个小的应用程序,用来显示一个标签。如果你是一个有GUI编程经验的开发者,这将是很简单的,你可以直接查看后面高级章节的Boa-Constructor子段落。

 

总览

可视化控件的布局

在一个框架当中,你将会使用很多wxPython的子类去充实框架里面的内容。这里是一些你将会在你的框架中使用到的常见的控件。

wx.MenuBar,在你的框架的顶部放一个菜单栏。

wx.Statusbar,在你的框架底部设置一个区域,来显示状态信息等等。

wx.ToolBar,在你的框架中放置一个工具栏

wx.Control的子类,这里面提供了一些控件的用户接口(比如说用来显示数据或者用户输入的可视化控件),常见的wx.Control对象包括wx.Buttonwx.StaticTextwx.TextCtrlwx.ComboBox

wx.Panel,它是一个容器,可以用来包含你的许多wx.Control对象。将你的wx.Control对象放入一个wx.Panel中,意味着用户可以直接将一对控件从一个可视化器件移动到另一个可视化器件上。  

所有的可视化控件(wx.Window对象和它们的子类)可以包含许多子控件。比如说,wx.Frame可以包含很多个wx.Panel对象,wx.Panel对象中又可以包含多个wx.Buttonwx.StaticTextwx.TextCtrl对象,给大家一个完整的控件层次结构:HierarchyOfElements1注意:这个仅仅只是描述了可视化控件之间的相关关系,不是说明它们在框架中的布局情况。处理框架中的控件布局,你有以下几个选择:

  1. 你可以手动的指定每一个控件在父窗口中的像素坐标。由于字体大小的不同等等的问题,如果想要程序在不同的平台之间可以通用的话,这个选择一般不被考虑。
  2. 你可以使用wx.LayoutContains,它一般用起来很复杂。
  3. 你可以使用像Delphi一样的LayoutAnchors,它比wx.LayoutContains更加简单一点。
  4. 你可以使用一个wx.Sizer的子类。

在这个文档中将会集中使用wx.Sizers,因为是大家最常用的解决方案。

Sizer

Sizer用来解决在窗口和框架中可视化控件的放置问题。Sizer可以做下列事情:

为每个控件计算合适的大小。

通过一些包含规则放置每一个控件。

当一个框架的大小被改变后,自动的重新计算每个控件的大小并且改变其坐标。

一些常见的sizers类包括:

wx.BoxSizer,以水平或垂直的方式将控件放置在一条线上。

wx.GridSizer,将控件以网状结构放置。

wx.FlexGridSizer,它和wx.GridSizer相似,但是它允许以更加灵活的方式放置可视化控件。

可以向Sizer添加一组wx.Window对象,可以通过调用sizer.Add(window, options...),或者调用sizer.AddMany(...)这两个方法向sizer中添加控件。每一个Sizer只处理那些被添加进来的控件。Sizer可以被嵌套。也就是说,你可以将一个Sizer添加到另一个Sizer当中。举个例子来说有两排按钮(每一排按钮通过一个水平的wx.BoxSizer进行布局),它可以包含在另一个wx.BoxSizer中,将一排按钮放在另一排按钮的上面,就像这样:SizersExample1

注意:注意上面那个例子不是将6个按钮布局成23列,如果要那么做的话,那就应该使用wx.GridSizer

 

在下面这个例子当中,我们使用2个嵌套的sizer,主Sizer是垂直布局,嵌套的Sizer是水平布局:

import wx
import os

class MainWindow(wx.Frame):
    def __init__(self, parent, title):
        self.dirname=''

        # A "-1" in the size parameter instructs wxWidgets to use the default size.
        # In this case, we select 200px width and the default height.
        wx.Frame.__init__(self, parent, title=title, size=(200,-1))
        self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE)
        self.CreateStatusBar() # A Statusbar in the bottom of the window

        # Setting up the menu.
        filemenu= wx.Menu()
        menuOpen = filemenu.Append(wx.ID_OPEN, "&Open"," Open a file to edit")
        menuAbout= filemenu.Append(wx.ID_ABOUT, "&About"," Information about this program")
        menuExit = filemenu.Append(wx.ID_EXIT,"E&xit"," Terminate the program")

        # Creating the menubar.
        menuBar = wx.MenuBar()
        menuBar.Append(filemenu,"&File") # Adding the "filemenu" to the MenuBar
        self.SetMenuBar(menuBar)  # Adding the MenuBar to the Frame content.

        # Events.
        self.Bind(wx.EVT_MENU, self.OnOpen, menuOpen)
        self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
        self.Bind(wx.EVT_MENU, self.OnAbout, menuAbout)

        self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
        self.buttons = []
        for i in range(0, 6):
            self.buttons.append(wx.Button(self, -1, "Button &"+str(i)))
            self.sizer2.Add(self.buttons[i], 1, wx.EXPAND)

        # Use some sizers to see layout options
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.control, 1, wx.EXPAND)
        self.sizer.Add(self.sizer2, 0, wx.EXPAND)

        #Layout sizers
        self.SetSizer(self.sizer)
        self.SetAutoLayout(1)
        self.sizer.Fit(self)
        self.Show()

    def OnAbout(self,e):
        # Create a message dialog box
        dlg = wx.MessageDialog(self, " A sample editor \n in wxPython", "About Sample Editor", wx.OK)
        dlg.ShowModal() # Shows it
        dlg.Destroy() # finally destroy it when finished.

    def OnExit(self,e):
        self.Close(True)  # Close the frame.

    def OnOpen(self,e):
        """ Open a file"""
        dlg = wx.FileDialog(self, "Choose a file", self.dirname, "", "*.*", wx.OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            self.filename = dlg.GetFilename()
            self.dirname = dlg.GetDirectory()
            f = open(os.path.join(self.dirname, self.filename), 'r')
            self.control.SetValue(f.read())
            f.close()
        dlg.Destroy()

app = wx.App(False)
frame = MainWindow(None, "Sample editor")
app.MainLoop()

sizer.Add 这个方法有三个参数。第一个参数指定了放入 sizer 的控件名。第二个参数是一个宽度因子,它用来表示这个控件相对于其他控件的比例大小,比如说你有 3 个编辑框并且你希望它们的大小比例是 3 2 1, 那么当你添加这些控件的时候,你就可以将这些比例指定为这个参数。 0 意味着这个控件或者这个 sizer 将不会发生大小的变化。第 3 个参数通常使用 wx.GROW( wx.EXPAND 一样 ) ,它意味着这个控件当在需要的时候可以改变大小。如果你使用 wx.SHAPED 来替代 wx.GROW ,那么控件的纵横比将不会发生变化。

 

如果第二个参数是0,即控件将不会改变大小,第三个参数如果使用wx.ALIGN_CENTER_HORIZONTAL, wx.ALIGN_CENTER_VERICAL这两个参数替代wx.GROW或者wx.SHARPED,那么控件将会被定位在垂直或者水平方向的中间位置。如果使用wx.ALIGN_CENTER那么控件将会被定位在程序窗口的正中间。

 

你可以在wx.ALIGN_LEFT,wx.ALIGN_TOP,wx.ALIGN_RIGHTwx.ALIGN_BOTTOM中选择几个作为一个组合。默认的行为是wx.ALIGN_LEFT | wx.ALIGN_TOP

 

大家可能对wx.Sizer和它的子类存在关于sizer和父窗口之间的差别的困惑。当你创建了一个对象放到了sizer里面,你并没有指定sizer的父窗口。Sizer只是一种窗口布局的方式,它本身并不是一个窗口,所以不需要指定sizer的父窗口。在上面的例子当中,六个按钮被创建的时候指定的父窗口是框架--不是sizer。如果你尝试创建一个可视化控件并且用sizer作为它的父窗口,那么你的程序将会崩溃。

 

当你建立了你的可视化控件之后并将它们添加到sizer(或者嵌套的sizer)里面,下一步就是告诉你的框架或者窗口去调用这个sizer,你可以完成这个,用以下三步:

window.SetSizer(sizer)
window.SetAutoLayout(True)
sizer.Fit(window)

调用 SetSizer() 方法告诉你的窗口或框架去使用这个 sizer 。调用 SetAutoLayout() 方法告诉你的窗口使用 sizer 去为你的控件计算位置和大小。最后,调用 sizer.Fit() 告诉 sizer 去计算所有控件的初始位置和大小。如果你使用 sizer 进行布局,在第一次显示你窗口中所有的控件之前,这都是一个常见的步骤。

 

Validator/验证器

当你创建了一个对话框或者一个其他的输入窗体,你可以使用wx.Validator来载入数据到你的输入窗体,验证输入的数据,再次从窗体中提取数据。wx.Validator也可以被用来截获键盘按键和其他的发生在输入区域框中的事件。如果要使用一个Validator,你应该创建一个你自己的wx.Validator的子类(wx.TextValidatorwx.GenericValidator都没有被wxPython实现)。通过调用myInputField.SetValidator(myValidator)来使你的这个子类和你的文字输入框联系起来。

注意:你的wx.Validator子类必须实现wx.Validator.Clone()方法。

 

一个可以运行的例子

我们的第一个程序包含一个panel(面板),panel中包含一个label(标签)

让我们开始一个例子。我们的程序将有一个框架,它有一个panel包含一个label

import wx
class ExampleFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)
        panel = wx.Panel(self)
        self.quote = wx.StaticText(panel, label="Your quote: ", pos=(20, 30))
        self.Show()

app = wx.App(False)
ExampleFrame(None)
app.MainLoop()

这段代码应该是很清晰的,并且应该没有任何问题。

注意:这里应该使用sizer,来替代指定控件坐标的部分。

注意这一行:

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

使用了panel作为我们的wx.StaticText的父窗口参数。我们的静态文本将出现在我们刚刚创建的面板上。wx.Point被用来做位置参数。还有一个可选参数wx.Size但是如果它在这里使用,将不会被对齐。

根据wxPython文档中的描述:

面板(panel)是一个窗口,可以将其他的控件放到它的上面。它通常放置在框架中。在它父类(wx.Window)的基础上,它包含了最小的额外的功能。它的主要是用在功能和外观相似的对话框上,它要比任何的父窗口有更强的灵活性。,事实上,它是一个简单的窗口,被用来做其他对象的背景。它作为一个控件,这些一般都是被大家所知道的。

标签(label)用来显示一些不能被用户所改变的文本。

添加更多的控件

你可以在wxPython的样例代码和帮助文档中发现全部控件的列表,但是我们在这里只展示几个最常用的控件:

wx.Button,最基础的控件:一个按钮上面显示文本,你可以点击它。比如说这里有一个“Clear”按钮:

 

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

wx.TextCtrl,文本框,这个控件可以让用户输入文本。它触发2个主要的事件。EVT_TEXT,只要文本被改变了,它就会被调用。EVT_CHAR,只要一个按键被按下,它就会被调用。

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

比如说:如果用户按下了“Clear”按钮并且文字区域被清空,将会触发EVT_TEXT事件,但是不会触发EVT_CHAR事件。

wx.ComboBox,  组合框和文本框很相似,但是它除了包含wx.TextCtrl会触发的2个事件,它还有EVT_COMBOBOX事件。

wx.CheckBox, 复选框是提供给用户选择真假的控件。

wx.RadioBox, 单选框可以让用户从一组选项中选择一个。

现在让我们看看这个Form1全部的代码:

import wx
class ExamplePanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.quote = wx.StaticText(self, label="Your quote :", pos=(20, 30))

        # A multiline TextCtrl - This is here to show how the events work in this program, don't pay too much attention to it
        self.logger = wx.TextCtrl(self, pos=(300,20), size=(200,300), style=wx.TE_MULTILINE | wx.TE_READONLY)

        # A button
        self.button =wx.Button(self, label="Save", pos=(200, 325))
        self.Bind(wx.EVT_BUTTON, self.OnClick,self.button)

        # the edit control - one line version.
        self.lblname = wx.StaticText(self, label="Your name :", pos=(20,60))
        self.editname = wx.TextCtrl(self, value="Enter here your name", pos=(150, 60), size=(140,-1))
        self.Bind(wx.EVT_TEXT, self.EvtText, self.editname)
        self.Bind(wx.EVT_CHAR, self.EvtChar, self.editname)

        # the combobox Control
        self.sampleList = ['friends', 'advertising', 'web search', 'Yellow Pages']
        self.lblhear = wx.StaticText(self, label="How did you hear from us ?", pos=(20, 90))
        self.edithear = wx.ComboBox(self, pos=(150, 90), size=(95, -1), choices=self.sampleList, style=wx.CB_DROPDOWN)
        self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, self.edithear)
        self.Bind(wx.EVT_TEXT, self.EvtText,self.edithear)

        # Checkbox
        self.insure = wx.CheckBox(self, label="Do you want Insured Shipment ?", pos=(20,180))
        self.Bind(wx.EVT_CHECKBOX, self.EvtCheckBox, self.insure)

        # Radio Boxes
        radioList = ['blue', 'red', 'yellow', 'orange', 'green', 'purple', 'navy blue', 'black', 'gray']
        rb = wx.RadioBox(self, label="What color would you like ?", pos=(20, 210), choices=radioList,  majorDimension=3,
                         style=wx.RA_SPECIFY_COLS)
        self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, rb)

    def EvtRadioBox(self, event):
        self.logger.AppendText('EvtRadioBox: %d\n' % event.GetInt())
    def EvtComboBox(self, event):
        self.logger.AppendText('EvtComboBox: %s\n' % event.GetString())
    def OnClick(self,event):
        self.logger.AppendText(" Click on object with Id %d\n" %event.GetId())
    def EvtText(self, event):
        self.logger.AppendText('EvtText: %s\n' % event.GetString())
    def EvtChar(self, event):
        self.logger.AppendText('EvtChar: %d\n' % event.GetKeyCode())
        event.Skip()
    def EvtCheckBox(self, event):
        self.logger.AppendText('EvtCheckBox: %d\n' % event.Checked())


app = wx.App(False)
frame = wx.Frame(None)
panel = ExamplePanel(frame)
frame.Show()
app.MainLoop()

我们的类现在变得更大了;它现在有了许多控件并且这些控件都是有响应的。我们添加了一个特别的 wx.TextCtrl 控件去显示控件发出的各种各样的事件。

 

标签页

有时候一个窗体变得太大了,以至于不能在一个单个的页面中显示。wx.NoteBook就是用来解决这个问题的:它允许用户通过点击相关的标签页,在多个页面中快速浏览。我们实现了这个,通过将窗体放入wx.NoteBook,而不是放入主框架中,并且通过使用AddPage方法将Form1添加到标签页中。

注意ExamplePanel的父窗口是NoteBook。这是很重要的。

app = wx.App(False)
frame = wx.Frame(None, title="Demo with Notebook")
nb = wx.Notebook(frame)


nb.AddPage(ExamplePanel(nb), "Absolute Positioning")
nb.AddPage(ExamplePanel(nb), "Page Two")
nb.AddPage(ExamplePanel(nb), "Page Three")
frame.Show()
app.MainLoop()

加强布局 - 使用 Sizer

使用一个绝对的坐标位置经常是不能让人满意的:如果窗口不是一个正确的大小,那么窗口最后的样子将会是很丑陋的。wxPython中有非常丰富的词汇来定义对象的位置。

wx.BoxSizer是一个最常用的并简单的布局对象,它只是将控件放在一个大概的位置。它的功能是很粗鲁地安排一系列的控件在一行或一列上,并且在需要的时候(比如说窗口的整体大小改变的时候)重新安排它们的位置。

wx.GridSizerwx.FlexGridSizer是两个非常重要的布局工具。它们安排控件以表格的形式布局。

这里将上面的代码简单的重写了一下:

class ExamplePanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        # create some sizers
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        grid = wx.GridBagSizer(hgap=5, vgap=5)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)

        self.quote = wx.StaticText(self, label="Your quote: ")
        grid.Add(self.quote, pos=(0,0))

        # A multiline TextCtrl - This is here to show how the events work in this program, don't pay too much attention to it
        self.logger = wx.TextCtrl(self, size=(200,300), style=wx.TE_MULTILINE | wx.TE_READONLY)

        # A button
        self.button =wx.Button(self, label="Save")
        self.Bind(wx.EVT_BUTTON, self.OnClick,self.button)

        # the edit control - one line version.
        self.lblname = wx.StaticText(self, label="Your name :")
        grid.Add(self.lblname, pos=(1,0))
        self.editname = wx.TextCtrl(self, value="Enter here your name", size=(140,-1))
        grid.Add(self.editname, pos=(1,1))
        self.Bind(wx.EVT_TEXT, self.EvtText, self.editname)
        self.Bind(wx.EVT_CHAR, self.EvtChar, self.editname)

        # the combobox Control
        self.sampleList = ['friends', 'advertising', 'web search', 'Yellow Pages']
        self.lblhear = wx.StaticText(self, label="How did you hear from us ?")
        grid.Add(self.lblhear, pos=(3,0))
        self.edithear = wx.ComboBox(self, size=(95, -1), choices=self.sampleList, style=wx.CB_DROPDOWN)
        grid.Add(self.edithear, pos=(3,1))
        self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, self.edithear)
        self.Bind(wx.EVT_TEXT, self.EvtText,self.edithear)

        # add a spacer to the sizer
        grid.Add((10, 40), pos=(2,0))

        # Checkbox
        self.insure = wx.CheckBox(self, label="Do you want Insured Shipment ?")
        grid.Add(self.insure, pos=(4,0), span=(1,2), flag=wx.BOTTOM, border=5)
        self.Bind(wx.EVT_CHECKBOX, self.EvtCheckBox, self.insure)

        # Radio Boxes
        radioList = ['blue', 'red', 'yellow', 'orange', 'green', 'purple', 'navy blue', 'black', 'gray']
        rb = wx.RadioBox(self, label="What color would you like ?", pos=(20, 210), choices=radioList,  majorDimension=3,
                         style=wx.RA_SPECIFY_COLS)
        grid.Add(rb, pos=(5,0), span=(1,2))
        self.Bind(wx.EVT_RADIOBOX, self.EvtRadioBox, rb)

        hSizer.Add(grid, 0, wx.ALL, 5)
        hSizer.Add(self.logger)
        mainSizer.Add(hSizer, 0, wx.ALL, 5)
        mainSizer.Add(self.button, 0, wx.CENTER)
        self.SetSizerAndFit(mainSizer)

 

在这个例子当中使用GridBagSizer去放置控件。Grid对象的“pos”参数决定控件在grid中的位置。在这个实例中(0,0)是左上角,(3,5)指的是第三排第五列,span参数允许控件被强行扩展成多行多列。

 

用户行为的响应

标题:

事件

弹出菜单

总览

概念

一个可以运行的例子

例子

绘图

标题

设备环境

字体

颜色

onPaint()方法

总览

在这段中,我们将介绍在窗口中画图的方法。我们也将会展示如何在主窗口中按下鼠标右键,出现弹出菜单。

一个可以运行的例子

使用wxPython

调试技术

当在你的程序中遇到一个不能被处理的异常时(bug!),程序被异常终止,那么使用追踪技术去定位问题代码是很有用的。wxPython程序也是一样的,但是它是扭曲的(twist)。wxPython中的追踪是重新定向标准输入输出。它是独立于你程序的GUI窗口的。如果一个异常发生在事件处理阶段,追踪信息会被显示出来,并且你的程序会尽力地继续执行。但是,如果异常发生在你程序的初始化阶段,追踪信息会被显示出来,然后你的程序会被异常终止,查看标准输入输出窗口(或者你重定向的位置),可以让读者尽快知道问题所在。你可以截获标准输入输出,通过wxPython提供的两个可选参数,在你实例化你的wx.App对象的时候,可以指定这两个参数。这个例子可以很好的说明:

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

你也可以使用 Widget Inspection Tool 来帮你调试大多数的布局问题。

这里讨论代码调试问题,事件循环问题,等等。

 

PyCrust交互控制台

wxPython发布一个漂亮的PyCrust控制台。使用它,你可以以交互的方式测试你的布局。这里有一段简单的代码。

部署你的wxPython应用

下一步

事件

事件处理是wxPython很关键的一部分。我们知道所有的GUI系统都是依赖于事件去在各种应用程序之间分发消息。GUI程序的任务是当接收到一个特定的消息的时候,自己决定去做什么,并且调用事件处理。在面向对象编程以前,想要处理事件就意味着必须有一个“switch”操作,根据事件的类型来决定去做什么事。现在在面向对象编程中这就不再是那么简单的事情了。现在有2种事件处理的方式:

一种方法(像java)是依赖于事件处理器。事件处理器和一个特定的对象链接并且和一个回调函数/方法绑定。当对象接收到一个特定类型的事件,事件处理器就会触发回调函数。

另一种方法是预先给一个方法起一个名字,假定用这个方法处理某一种特定的事件。使用这种方法的话,如果你想要改变某个类对某个事件的响应,那么你就必须在原有的类的基础上进行派生,并且重载响应的方法。

wxPython结合了这两种方法。你可以定义事件处理器与派生类去实现新的行为。


所以“self.Bind(wx.EVT_SOMETHING,ACallable)”的意思是:当EVT_SOMETHING事件被发送到窗口的时候,它将会调用ACallableACallable可以是任何函数,通常程序员的选择是让它实现上一个类的功能。


第二个版本是“self.Bind(wx.EVT_SOMETHING,ACallable,srcWin)”的意思是:当源窗口发出SOMETHING事件,这个事件到达窗口,然后调用ACallable


但是很多事件只能被发出事件的窗口所捕获(这意味着第二个方式不能做任何事情),所以最好无论在什么情况下都使用第一种方法,第一种方法可以用在任何地方,除了菜单项,因为它没有Bind()方法。

在这里:创建一个自定义的事件处理程序

 

Scintilla

Scintilla是一个基本的组件,被用在wx.StyleTextCtrl类中,它可以使我们在wxPython中实现语法高亮的功能。

Boa-constructor

Boa-constructor是wxPython的一个集成开发环境

多线程

在这里:

有用的资源

http://wxPython.org

从这里开始,一个非常著名的网站,而且你也可以看看wxPython包中的样例代码。它都是非常有用的代码样例并且它几乎包含所有你能想到的主题。想要运行样例程序的话可以通过以下方法:

windows中只需要选择开始菜单中的wxPython子菜单,然后只需要选择Run The Demo就可以了。

Linux中找到样例代码的目录然后运行“python demo.py”

http://wxwidgets.org

你也可以尝试在wxWidgets的网站找到一些信息。wxPython的文档包含了wxWidget的所有内容,所以如果你开始看wxPython的文档的话,你可能会没有头绪。

http://wxpython.org/maillist.php

wxPython的邮箱列表,在这里你可以找到你想要的信息。但是在你提问之前,请先搜索档案库。

http://www.python.org

Python社区的参考手册网站。

http://starship.python.net/crew/theller/py2exe

这是一个工具,通过这个工具你可以将python脚本或者wxpython脚本转换成独立的windows中的可执行文件。这可以使你的程序被更广泛的使用。

做出贡献的人

wxPython社区

Lucas Bruand

Rob CakeBread

Charlie Derr

Robin Dunn

Michael Roberts

Erik westra

我们也得感谢它们:

正是由于Andrew Kuchling的帮助和支持,才没有使永不间断的问题和回复变得得很无聊。

Robin North, J-P Syed, Armel Guenneugues, Pilar Rodriguez, Matteo Caligarisfor being supportive and not kidding too much around about calling me a geek.(这个没理解是什么意思)

附录

小编辑器   --完整代码

WxHowtoSmallEditor

创建窗体   --完整代码

WxHowtoBuildingForms

wxPython中绘图   --完整代码

WxHowtoDrawing

一个前期的工程项目  --完整代码

WxProject,它也展示了怎样使用wxPython风格的向导

常见的问题及解答

请看:FAQ


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

wxPython的基础教程 的相关文章

  • Psycopg / Postgres:连接随机挂出

    我正在使用 psycopg2 作为我当前正在开发的cherrypy 应用程序 并使用 cli 和 phpgadmin 来手动处理一些操作 这是Python代码 One connection per thread cherrypy threa
  • 来自 python 的 FQL 多重查询因 unicode 查询而失败

    使用 python 2 6 5 和 facebook sdk 0 3 2 import facebook api facebook GraphAPI token api fql example u SELECT uid2 FROM frie
  • 默认情况下在 Jupyter 笔记本中配置第一个单元

    有没有办法为 Jupyter 笔记本中的特定 python 内核配置默认的第一个单元 我同意默认的 python 导入违背了良好的编码实践 那么 我可以配置笔记本 使新的 python 笔记本的第一个单元始终是 import numpy a
  • 从终端调用时 uvicorn 不工作

    我尝试通过 pip3 在系统上安装 uvicorn 这有效 但是我无法从命令行运行相同的命令 有关如何解决此问题的任何指示 Requirement already satisfied uvicorn in home vhawk19 loca
  • 如何在 Linux 中显示进程状态(阻塞、非阻塞)

    有没有办法查询 Linux 进程表中进程的状态 以便能够演示执行查询时进程是正在运行还是被阻止 我的目标是从进程或程序的 外部 执行此操作 因为我希望从操作系统进程的角度来理解这一点 但欢迎任何想法 这是Python代码阻塞的过程 impo
  • cv2.face.mindistancepredictcollector() 错误

    我已经安装了带有额外模块的 opencv 3 1 0 但是当我尝试使用 gt gt gt s cv2 face MinDistancePredictCollector 它返回一个错误 Traceback most recent call l
  • 在 python + Flask + Gunicorn + nginx + Compute Engine 应用程序中从 Google Cloud Storage 读取文件失败

    在 python Flask Gunicorn nginx Compute Engine 应用程序中读取从 Google Cloud Storage 下载的文件失败 代码链接 https github com samuq CE test h
  • SMTPAuthenticationError: (535, b'5.7.8 用户名和密码在 Django 生产中不被接受?

    我在 Heroku 上部署了一个 Django 应用程序 在其中一节中 我使用 SMTP Gmail 设置向用户发送电子邮件 当我在本地运行项目时 电子邮件发送成功 但在 Heroku 上部署的项目上却发送失败 我在 Stackoverfl
  • WTForms 中的小数字段舍入

    我有一个包含价格小数字段的表单 如下所示 from flask ext wtf import Form import wtforms from wtforms validators import DataRequired from deci
  • 将列表传递给 PyCrypto 中的 AES 密钥生成器

    我尝试使用 Pycrypto 生成 AES 密钥 但收到以下错误 类型错误 列表 不支持缓冲区接口 对于以下声明 aescipher AES new mykey AES MODE ECB mykey 属于类型list并包含 18854347
  • 更改Python pylab玫瑰/极坐标图中图例标题的字体大小

    我正在尝试更改玫瑰图或 极地 图上现有图例标题的字体大小 大部分代码是由不在的其他人编写的 我已经添加 ax legend title legend title setp l get title fontsize 8 添加标题 legend
  • 图像堆栈的最大强度投影

    我正在尝试重新创建该功能 max array 3 来自 MatLab 它可以获取 N 个图像的 300x300px 图像堆栈 我在这里说 图像 因为我正在处理图像 实际上这只是一个大的双数组 300x300xN 并创建一个 300x300
  • 尝试修复我的功能

    我正在开发一个函数 我必须返回一个元组 其中第一个参数是最大数字的 str 第二个参数是 int 列表 这是示例以及我为该函数编写的内容 投票 G G N G C G 1 3 0 1 您必须将最大值的位置映射到正确的一方 parties N
  • Python unittest - 与assertRaises相反?

    我想编写一个测试来确定在给定情况下不会引发异常 测试是否有异常很简单is上调 sInvalidPath AlwaysSuppliesAnInvalidPath self assertRaises PathIsNotAValidOne MyO
  • import numpy 和 import numpy as np 之间的区别

    我明白 如果可能的话 应该使用 import numpy as np 这有助于避免由于命名空间引起的任何冲突 但我注意到虽然下面的命令有效 import numpy f2py as myf2py 以下不 import numpy as np
  • 在Python中引用不带换行符的长字符串

    我正在尝试在 Python 中编写一个长字符串 该字符串显示为 OptParser 选项的帮助项 在我的源代码 py 文件中 我想放置换行符 以便我的代码不会花费新行 但是 我不希望这些换行符影响代码运行时该字符串的显示方式 例如 我想写
  • Python 类方法的示例用例是什么?

    我读了Python 中的类方法有什么用 https stackoverflow com questions 38238 what are class methods in python for但那篇文章中的例子很复杂 我正在寻找 Pytho
  • sklearn 中带有词袋和附加情感特征的文本分类器

    我正在尝试构建一个分类器 除了词袋之外 还使用情绪或主题 LDA 结果 等特征 我有一个包含文本和标签的 pandas DataFrame 并且想添加情感值 5 到 5 之间的数字 和 LDA 分析结果 带有句子主题的字符串 我有一个工作词
  • Python列表问题

    我在使用 python 列表时遇到问题 简化版本是 mylist1 some items in a list mylist2 mylist1 mylist1 pop i mylist insert i item print mylist1
  • Pandas 替换特定列上的值

    我知道这两个类似的问题 熊猫替换值 https stackoverflow com questions 27117773 pandas replace values Pandas 替换数据框中的列值 https stackoverflow

随机推荐

  • PAT1033 旧键盘打字 (20 分)

    题目描述 旧键盘上坏了几个键 于是在敲一段文字的时候 对应的字符就不会出现 现在给出应该输入的一段文字 以及坏掉的那些键 打出的结果文字会是怎样 输入格式 输入在 2 行中分别给出坏掉的那些键 以及应该输入的文字 其中对应英文字母的坏键以大
  • chatgpt赋能python:Python学习:如何建立一个空集合?

    Python学习 如何建立一个空集合 作为一门流行的编程语言 Python在数据处理 机器学习和Web应用开发等领域广泛应用 其中 集合是Python中常用的数据类型之一 它是由不同元素组成的无序集合 其中每个元素都是唯一的 当需要处理一些
  • Git&TortoiseGit安装和配置

    前言 日常开发提交代码的过程中 Git工具是必不可少的 有些小伙伴喜欢可视化界面 TortoiseGit工具就是不二之选了 下面提供纯Git配置和Git TortoiseGit两种方式 1 纯Git安装与配置 下载 Git官网 下载安装完成
  • Python Email应用的中文乱码问题解决方法

    或者是对python的unicode理解得不透彻 时常遇到各种各样的中文乱码问题 今天 在email应用中 中文乱码又出来捣乱了 经过一段时间的误打误撞 搞出了一个解决方法 coding utf 8 Created on 2010 6 21
  • [docker]笔记-存储管理

    1 docker数据存储分为非永久性存储和永久性存储 非永久性存储 容器创建会默认创建非永久性存储 该存储从属于容器 生命周期与容器相同 会随着容器的关闭而消失 可理解为内存中数据 会随关机而消失 多用来保存不需要存储的数据 永久性存储 创
  • eclipse中注释模板的修改

    window gt preferences gt java gt code style gt code template gt comments code 手机扫一扫 关注程序员技能成长
  • Android 取主色逻辑

    Palette是Google官方提供的一个类 用于帮助开发者提取图片的主色 1 生成 Palette 根据bitmap生成 Palette 同步方法 应该在子线程中使用 Palette p Palette generate bitmap 异
  • date时间格式化 只想要年月日

    传进来的时间是String类型 DateTimeFormatter format DateTimeFormat forPattern yyyy MM dd HH mm ss DateTime paraDate DateTime parse
  • ssm(spring+springMVC+Mybatis)框架 集成Quartz(定时任务框架)

    1 ssm框架基础jar 包 aopalliance jar aspectjrt jar aspectjweaver jar commons beanutils 1 9 2 jar commons codec 1 9 jar commons
  • Python基础--入门基础和数据类型测试题(一)

    Made By Zly All Right Reversed 上一篇 篇三 Python 入门基础和数据类型测试题 一 1 在Python语言中 不能作为变量名的是 A P B Temp C 3p D fg 2 以下关于Python缩进的描
  • 【华为OD统一考试B卷

    华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为OD统一考试 A卷 和OD统一考试 B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是
  • Java 生成测试字符串的库:Java Faker

    一 背景 Java的单元测试经常需要构造各种测试数据 其中一项就是构造测试的字符串 如果手动创建非常麻烦 也有一些框架支持创建指定长度的随机字符串 但是并不是我们想要的效果 我们想要的是人名 地名等 那么有一个库叫java fake可以实现
  • 从MCU上电启动流程出发编写bootloader(上)

    配置开发板的BOOT 从EFLASH 即片内Flash存储器 启动 在KEIL中也需要进行相应的设置 将接下来要编写的bootloader烧写至EFLASH bootloader段需要设置从0x8000000开始 空间根据bootloade
  • 50个热门语义分割数据集免费、高速下载资源分享,涵盖通用视觉、遥感、自动驾驶、医疗等多种场景题

    小伙伴们期待已久的数据集资源盘点系列又来啦 本期将分享50个语义分割任务相关的热门公开数据集资源 粗略分了4类 通用视觉类 智慧遥感类 自动驾驶类 其他 快来看看有没有你想要的吧 如果觉得不错的话 记得收藏 一 通用视觉类 No 1 PAS
  • 【java筑基】IO流基础之文件的常见操作

    前 言 作者简介 半旧518 长跑型选手 立志坚持写10年博客 专注于java后端 专栏简介 深入 全面 系统的介绍java的基础知识 文章简介 本文将深入全面介绍IO流知识 建议收藏备用 创作不易 敬请三连哦 大厂真题 大厂面试真题大全
  • Linux 对函数库的理解

    一 前言 我们的C程序中 并没有定义 printf 的函数实现 且在预编译中包含的 stdio h 中也只有该函数的声明 而没有定义函数的实现 那么 是在哪里实 printf 函数的呢 最后的答案是 系统把这些函数实现都被做到名为 libc
  • Mybatis配置文件入门

    mybatis config xml
  • 基于SSM框架的《超市订单管理系统》Web项目开发(第二天)完成登录模块和用户退出模块

    超市订单管理系统 第二天 基于SSM框架的Web项目开发 昨天我们实现了登录功能 但是用的是模拟数据 今天我们要链接数据库整合Spirng Mybatis 读取数据库中的真实数据 用来跟我们输入的userCode和userPassword进
  • Java业内主流框架你知道吗?SSH和SSM有什么区别?

    大家好 我是威哥 今天我给大家分享Java的三大框架是什么 在SpringBoot出现之前 我们常说的三大框架是 Spring SpringMVC Mybatis SSM 而在SpringBoot出现之后 SpringBoot慢慢开始占据了
  • wxPython的基础教程

    写在前面的话 上个假期学习了python 发现它真的是一门很有趣的语言 所以这学期想学一些python的可视化编程 于是选择了wxPython 但是我在网上找中文教程找了好久都没有找到中文的教程 额 也许是我方法不对 无奈只好看英文的啦 于