我想学些python GUI的编程,希望通过例子了解一下wxpython,于是就根据官方文档 https://wiki.wxpython.org/Getting Started 模仿写了下面的例子。如果还想查看更多官方文档,可以参考:https://wiki.wxpython.org/。
安装wxpython
:: windows 命令行下执行下面的命令,其中 U 表示安装并升级
pip install -U wxpython
检查wx的版本
import wx
wx.version()
我的环境:
- win7
- 4.0.6 msw (phoenix) wxWidgets 3.0.5’
- python 3.7.1
第一个例子
什么都不做,就是展示一个空窗口
# coding = utf-8
# https://wiki.wxpython.org/Getting%20Started
import wx
if __name__=="__main__":
# 所有用 wxpython 编写的小程序都需要一个wx.App作为载体。
myApp = wx.App(False)
# The "False" parameter means "don't redirect stdout and stderr to a window".这个是什么意思,还要试一试。
# 这里创建一个顶层(top-level)的框架,作为应用显示的框架。一般要自己根据自己需要的功能,添加组件实现一个Frame
mainFrame = wx.Frame(parent= None, id = wx.ID_ANY, title="测试小程序")
# 这里需要Show一下,是因为 Frame 里面没东西,默认不显示。
mainFrame.Show(True) # mainFrame.Show() 也可以,
# 启动APP的事件循环系统,等待处理事件
myApp.MainLoop()
官方文档说5句话就可以写一个完整的GUI小应用:
1、import wx :导入wxpython
2、myApp = wx.App(False) : 新建一个应用
3、mainFrame = wx.Frame(…):建立一个顶层框架
4、mainFrame.Show():展示这个顶层框架
5、myApp.MainLoop():进入应用循环,等待处理事件
第二个例子
第一个例子的应用太单调,给它加点料:菜单栏、状态栏,官方文档中还有加一个文本框,用来显示文本文件的内容,这里就跟着官方的节奏走下去。
先让它看起来像个应用
这个例子和第一个例子有一个很大的不同,我们自己定义一个继承自Frame的框架MyFrame类,并在框架中添加一些组件。
# coding = utf-8
# https://wiki.wxpython.org/Getting%20Started
import wx
class MyFrame(wx.Frame):
# MyFrame 继承 wx.Frame类,传进来的参数,需要传递给父类进行初始化。
def __init__(self, *args,**kw):
super().__init__(*args,**kw)
self.Show(True)
# 留一个框架的引用,方便后续使用(CreateMenuBar里就用到了)
self.theFrame = self
# 创建一个菜单栏
self.menuBar = self.CreateMenuBar()
# 创建一个文本编辑区
self.textContent = wx.TextCtrl(self,style = wx.TE_MULTILINE) # 文本控制窗口,多行模式
# 创建一个状态栏
self.statusBar = self.CreateStatusBar()
pass
def CreateMenuBar(self):
# 创建一个菜单栏
menuBar = wx.MenuBar()
# 创建一个“文件”菜单
fileMenu = wx.Menu()
fileMenu.Append(wx.ID_ANY, "打开", "打开文件")
fileMenu.Append(wx.ID_ANY, "保存", "保存文件")
fileMenu.AppendSeparator()
exitItem = fileMenu.Append(wx.ID_EXIT, "退出", "退出")
self.theFrame.Bind(wx.EVT_MENU, self.OnExit, exitItem)
# 上面这里:Bind 将一个对象和一个事件绑定在一起,当 exitItem 这个对象
# 发生 wx.EVT_MENU 这个事件时,就会调用 self.OnExit 这个动作来响应。
# 添加一条分割线
fileMenu.AppendSeparator()
## 在“文件”菜单中创建一个二级菜单
showMenu= wx.Menu()
showMenu.Append(wx.ID_ANY,"新功能")
fileMenu.AppendMenu(wx.ID_ANY,"二级菜单",showMenu)
# 创建一个“帮助”菜单
helpMenu = wx.Menu()
helpItem = helpMenu.Append(wx.ID_ANY, "帮助信息", "")
helpMenu.AppendSeparator()
aboutItem = helpMenu.Append(wx.ID_ABOUT, "应用信息", "")
self.theFrame.Bind(wx.EVT_MENU,self.OnAbout,aboutItem)
# 将两个菜单添加到菜单栏
menuBar.Append(fileMenu, "文件")
menuBar.Append(helpMenu, "帮助")
# 这里用到了程序刚开始定义的顶层框架自身的引用。
self.theFrame.SetMenuBar(menuBar)
return menuBar
pass
def OnAbout(self,event):
# 从文件读出软件信息,并显示
message = '软件名称:测试软件\n联系方式: somebody@somesite.com\n版本:0.0.1'
msgBox = wx.MessageBox(message,"关于此软件")
pass
def OnExit(self,event):
self.theFrame.Close()
if __name__=="__main__":
# 所有用 wxpython 编写的小程序都需要一个wx.App作为载体。
myApp = wx.App(False)
# 实例化一个自己的框架,窗口名称是“测试小程序”,窗口大小是600x400
mainFrame = MyFrame(parent= None, title="测试小程序",size=(600,400))
# 启动APP的事件循环系统,等待处理事件
myApp.MainLoop()
上面的例子中创建了一个菜单栏、一个文本框,一个状态栏。菜单栏中创建了两个菜单、若干菜单项、将菜单项和响应事件函数进行了绑定、创建了一个二级菜单、将菜单对象和响应事件的函数进行了绑定、定义了响应函数。这个应用只是个样子,啥功能都没,继续扩充它的功能。
给菜单中的“打开”、“保存”添加响应函数
首先添加一个响应“打开”菜单的函数:OnOpen()
def OnOpen(self,event):
self.dirname = ""
dlg = wx.FileDialog(self,"选择文件", self.dirname,"","*.*",wx.FD_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',encoding="utf-8")
self.textContent.SetValue(f.read())
f.close()
self.theFrame.Title = self.AppName +" "+self.filename
dlg.Destroy()
这里用到了一个 wx.FileDialog 对话框,可以比较方便选择文件。类似的,添加一个OnSave()函数
def OnSave(self,event):
if self.filename == "":
# 这里是还没有打开文件,直接要保存。提示选择文件,并保存
dlg = wx.FileDialog(self, "选择文件", self.dirname, "", "*.*", wx.FD_SAVE)
if dlg.ShowModal() == wx.ID_OK:
self.filename = dlg.GetFilename()
self.dirname = dlg.GetDirectory()
f = open(os.path.join(self.dirname, self.filename), 'w', encoding="utf-8")
f.write(self.textContent.GetValue())
f.close()
self.theFrame.Title = self.AppName + " " + self.filename
dlg.Destroy()
else:
# 已经打开了文件,提示是否保存
dlg = wx.MessageDialog(self,"是否保存文件到 "+os.path.join(self.dirname,self.filename)+" ?", \
"保存文件", style= wx.CANCEL|wx.OK)
if dlg.ShowModal() == wx.OK:
f = open(os.path.join(self.dirname,self.filename),'w',encoding="utf-8")
f.write(self.textContent.GetValue())
f.close()
dlg.Destroy()
由于要保存的文件并不是一个已经打开的文件,对于这种情况,就让用户选择(创建)要保存的文件,再进行报存。如果是已经打开的文件,则提示用户是否确认要保存。对于第二种情况,如果能知道文件内容是否有修改,如果有修改在提示保存,这样更好。(看看有啥办法可以知道文本内容和文本编辑区的内容是否一致?)
因为要保存文件的时候要分情况判断,要使用到一个 self.filename 成员变量,所以再添加一个初始化类似参数的函数:
def __initMembers(self):
self.dirname = ""
self.filename = ""
self.AppName=self.Title
# 留一个框架的索引,方便后续引用,也可以不用这样,直接用self
self.theFrame = self
其他类似“新建”,“另存为”的功能和“打开”,“保存”类似。下面这段参考代码的意义不大:
# coding = gbk
# https://wiki.wxpython.org/Getting%20Started
import wx
import os
class MyFrame(wx.Frame):
# MyFrame 继承 wx.Frame类,传进来的参数,需要传递给父类进行初始化。
def __init__(self, *args,**kw):
super().__init__(*args,**kw) # 窗口大小600x400,
self.Show(True)
# 初始化一些成员变量
self.__initMembers()
# 创建一个菜单栏
self.menuBar = self.CreateMenuBar()
# 创建一个文本编辑区
self.textContent = wx.TextCtrl(self,style = wx.TE_MULTILINE) # 文本控制窗口,多行模式
# 创建一个状态栏
self.statusBar = self.CreateStatusBar()
def __initMembers(self):
self.dirname = ""
self.filename = ""
self.AppName=self.Title
# 留一个框架的索引,方便后续引用,也可以不用这样,直接用self
self.theFrame = self
def CreateMenuBar(self):
menuBar = wx.MenuBar()
# 创建一个菜单
fileMenu = wx.Menu()
newfileItem = fileMenu.Append(wx.ID_ANY,"新建","新建文件")
self.Bind(wx.EVT_MENU,self.OnNewFile,newfileItem)
openItem = fileMenu.Append(wx.ID_ANY, "打开", "打开文件")
self.theFrame.Bind(wx.EVT_MENU,self.OnOpen,openItem)
saveItem = fileMenu.Append(wx.ID_ANY, "保存", "保存文件")
self.theFrame.Bind(wx.EVT_MENU, self.OnSave, saveItem)
saveasItem = fileMenu.Append(wx.ID_ANY, "另存为", "另存文件")
self.theFrame.Bind(wx.EVT_MENU, self.OnSaveAs, saveasItem)
fileMenu.AppendSeparator()
exitItem = fileMenu.Append(wx.ID_EXIT, "退出", "退出")
self.theFrame.Bind(wx.EVT_MENU, self.OnExit, exitItem)
fileMenu.AppendSeparator()
## 在“文件”菜单中创建一个二级菜单
showMenu= wx.Menu()
showMenu.Append(wx.ID_ANY,"新功能")
fileMenu.AppendMenu(wx.ID_ANY,"二级菜单",showMenu) # Todo:这有一个警告,要看一下怎么解决。
helpMenu = wx.Menu()
helpItem = helpMenu.Append(wx.ID_ANY, "帮助信息", "")
helpMenu.AppendSeparator()
aboutItem = helpMenu.Append(wx.ID_ABOUT, "应用信息", "")
self.theFrame.Bind(wx.EVT_MENU,self.OnAbout,aboutItem)
menuBar.Append(fileMenu, "文件")
menuBar.Append(helpMenu, "帮助")
self.theFrame.SetMenuBar(menuBar)
return menuBar
pass
def OnAbout(self,event):
# 从文件读出软件信息,并显示
message = '软件名称:测试软件\n联系方式: somebody@somesite.com\n版本:0.0.1'
msgBox = wx.MessageBox(message,"关于此软件")
def OnExit(self,event):
self.theFrame.Close()
def OnNewFile(self,event):
# 文件名清空,因为还没保存
self.filename=""
# 编辑区清空
self.textContent.SetValue("")
self.theFrame.Title = self.AppName
def OnOpen(self,event):
self.dirname = ""
dlg = wx.FileDialog(self,"选择文件", self.dirname,"","*.*",wx.FD_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',encoding="utf-8")
self.textContent.SetValue(f.read())
f.close()
self.theFrame.Title = self.AppName +" "+self.filename
dlg.Destroy()
def OnSave(self,event):
if self.filename == "":
# 这里是还没有打开文件,直接要保存。提示选择文件,并保存
dlg = wx.FileDialog(self, "选择文件", self.dirname, "", "*.*", wx.FD_SAVE)
if dlg.ShowModal() == wx.ID_OK:
self.filename = dlg.GetFilename()
self.dirname = dlg.GetDirectory()
f = open(os.path.join(self.dirname, self.filename), 'w', encoding="utf-8")
f.write(self.textContent.GetValue())
f.close()
self.theFrame.Title = self.AppName + " " + self.filename
dlg.Destroy()
else:
# 已经打开了文件,提示是否保存
dlg = wx.MessageDialog(self,"是否保存文件到 "+os.path.join(self.dirname,self.filename)+" ?", \
"保存文件", style= wx.CANCEL|wx.OK)
if dlg.ShowModal() == wx.OK:
f = open(os.path.join(self.dirname,self.filename),'w',encoding="utf-8")
f.write(self.textContent.GetValue())
f.close()
dlg.Destroy()
def OnSaveAs(self,event):
# 清空文件名,调用
self.filename= ""
self.OnSave(event=None)
if __name__=="__main__":
# 所有用 wxpython 编写的小程序都需要一个wx.App作为载体。
myApp = wx.App(False) # The "False" parameter means "don't redirect stdout and stderr to a window". 这个是什么意思,还要试一试。
# 实例化一个自己的框架
mainFrame = MyFrame(parent= None, title="测试小程序",size=(600,400))
# 启动APP的事件循环系统,等待处理事件
myApp.MainLoop()