界面开发
- wxPython桌面GUI实战两篇|抽奖器 + 简易计算器,新手友好的桌面小工具合集
- 一、升级版抽奖器:修复频闪Bug,自带中奖弹窗
- 源码
- 分段解答
- 1. 模块导入与窗口类定义
- 2. __init__ 初始化窗口与控件
- 3. 开始抽奖回调 onclick 与定时器逻辑
- 4. 名字刷新函数 update_name
- 5. 结束抽奖回调 stop
- 6. 程序入口与GUI主循环
- 7. 本案例注意事项
- 二、简易计算器:Sizer网格自适应布局实战
- 源码
- 分段解答
- 1. 模块导入与计算器窗口类定义
- 2. 初始化布局容器与输入框
- 3. GridSizer网格布局、批量生成按钮
- 4. 统一回调函数 on_btn_click
- 5. try-except异常捕获与eval计算
- 6. 程序入口和主循环
- 7. 本案例注意事项
- 三、打包为独立exe程序
- 源码(打包命令)
- 分段解答
- 1. 命令参数说明
- 2. 注意事项
- 四、总结
wxPython桌面GUI实战两篇|抽奖器 + 简易计算器,新手友好的桌面小工具合集
哈喽各位小伙伴!今天我们一起来上手wxPython,亲手打造两个实用的桌面小程序。一个是带滚动效果的抽奖器,一个是经典的四则运算计算器。
两个案例各有侧重,覆盖了GUI开发里非常核心的知识点:组件摆放、事件绑定、定时器动画、Sizer自适应布局。不需要前端知识,纯Python代码就能生成可以直接在电脑上运行的窗口程序,非常适合GUI入门练习。
wxPython 是 Python 生态中老牌的桌面GUI库,上手简单,适合快速开发自用小工具和功能原型。
运行前先安装依赖:
pipinstallwxPython一、升级版抽奖器:修复频闪Bug,自带中奖弹窗
源码
# 导入随机模块,用来随机抽取名单中的名字importrandom# 导入wxPython核心库,所有GUI组件都来自wximportwx# 自定义窗口类,继承wx.Frame(Frame代表一个独立窗口)classMyFrame(wx.Frame):# 抽奖名单,在这里修改参与抽奖的人员名称NameList=['mia','tom','jack']def__init__(self):# 调用父类Frame的构造方法# None:没有父窗口;title:窗口标题;size:窗口宽高 800*600wx.Frame.__init__(self,None,title='抽奖器',size=(800,600))# 创建Panel面板,Panel是窗口内部承载按钮、文字等控件的画布self.pl=wx.Panel(self,size=(800,600),pos=(100,100))# 设置窗口背景颜色为白色self.SetBackgroundColour('white')# 定义定时器变量,初始为None,用来控制名字滚动self.timer=None# 创建静态文本控件,用来展示滚动的名字# label:初始展示随机选取的一个名字;pos:控件在面板上的坐标位置self.staticText=wx.StaticText(self.pl,label=random.choice(self.NameList),pos=(300,150))# 创建字体对象:字号40,无衬线字体,正常样式,加粗font=wx.Font(40,wx.FONTFAMILY_SWISS,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_BOLD)# 将字体应用到静态文本上self.staticText.SetFont(font)# 创建【开始抽奖】按钮self.btn1=wx.Button(self.pl,label='开始抽奖',pos=(100,10))# 创建【结束抽奖】按钮self.btn2=wx.Button(self.pl,label='结束抽奖',pos=(200,10))# 绑定按钮点击事件:点击btn1时,执行onclick函数self.Bind(wx.EVT_BUTTON,self.onclick,self.btn1)# 绑定按钮点击事件:点击btn2时,执行stop函数self.Bind(wx.EVT_BUTTON,self.stop,self.btn2)# 【开始抽奖】按钮对应的回调函数defonclick(self,_event):# 判断:如果定时器已经存在并且正在运行,直接return,禁止重复开启定时器# 解决连续点击开始,多个定时器叠加,名字越闪越快的bugifself.timerisnotNoneandself.timer.IsRunning():return# 实例化定时器对象,绑定到当前窗口self.timer=wx.Timer(self)# 绑定定时器事件:定时器触发时执行update_name函数self.Bind(wx.EVT_TIMER,self.update_name,self.timer)# 启动定时器,参数单位是毫秒,100=0.1秒触发一次刷新self.timer.Start(100)# 定时器触发的回调函数:更新显示的名字defupdate_name(self,_event):# 从名单中随机挑选一个名字,设置给文本控件self.staticText.SetLabelText(random.choice(self.NameList))# 【结束抽奖】按钮对应的回调函数defstop(self,_event):# 先判断定时器是否有效且正在运行ifself.timerisnotNoneandself.timer.IsRunning():# 停止定时器,名字停止滚动self.timer.Stop()# 获取当前文本控件上显示的名字,即为中奖者winner=self.staticText.GetLabelText()# 弹出消息弹窗,展示中奖结果# wx.OK:弹窗带确定按钮;wx.ICON_INFORMATION:弹窗自带信息图标wx.MessageBox(f"🎉 恭喜{winner}中奖!","抽奖结果",wx.OK|wx.ICON_INFORMATION)# 程序入口,当直接运行该py文件时,执行下方代码if__name__=='__main__':# 创建wx应用程序实例,一个GUI程序有且仅有一个App对象app=wx.App()# 实例化我们自定义的窗口frm=MyFrame()# 显示窗口frm.Show()# 启动GUI主消息循环,持续监听点击、定时器等事件,保持窗口打开app.MainLoop()分段解答
1. 模块导入与窗口类定义
首先导入两个依赖模块,random负责随机选取名字,wx是wxPython的核心库。
我们新建类MyFrame并继承wx.Frame。wx.Frame就是一个标准窗口模板,继承之后我们就可以在里面添加文字、按钮、定时器等控件。
静态属性NameList存放所有参与抽奖的名字,想要增减人员,直接修改这个列表即可。
2.init初始化窗口与控件
__init__是类的构造函数,创建窗口对象的时候会自动执行这一段代码。
- 先调用父类构造,设置窗口标题、窗口尺寸;
wx.Panel相当于一块画布,所有按钮、文字都需要放在Panel上;wx.StaticText静态文本,专门用来展示文字,这里用来显示滚动的姓名;wx.Font自定义字体样式,放大文字,投屏抽奖的时候看得更清楚;wx.Button创建两个功能按钮;Bind()是事件绑定方法,作用是:当按钮被点击时,自动执行我们指定的回调函数。
3. 开始抽奖回调 onclick 与定时器逻辑
这是本程序最核心的部分,滚动效果依靠wx.Timer定时器实现。
增加状态判断,检测定时器是否正在运行。如果已经在运行,直接退出函数,避免多次点击生成多个定时器,出现名字越闪越快的bug。timer.Start(100)代表每100毫秒触发一次定时器事件,单位是毫秒。
4. 名字刷新函数 update_name
定时器每一次触发,都会执行这个函数。
调用random.choice在名单里随机挑选一个名字,然后使用SetLabelText更新界面上显示的文字,营造滚动摇号的视觉效果。
5. 结束抽奖回调 stop
点击结束按钮后,先判断定时器状态,确认正在运行才执行停止操作。
定时器停止,文字就不再刷新。读取当前界面上的文字作为中奖人,调用wx.MessageBox弹出弹窗展示中奖信息。
6. 程序入口与GUI主循环
if __name__ == '__main__'是Python脚本入口判断,只有直接运行这个文件,里面代码才会执行。
每一个wxPython程序必须有且仅有一个 wx.App。app.MainLoop()开启GUI主循环,持续监听鼠标点击、定时器事件,窗口才能保持打开状态。
7. 本案例注意事项
- 这个案例使用固定坐标
pos=(x,y)摆放控件,写法简单直观;缺点是拖动窗口放大缩小时,控件位置不会自动跟着适配。 - GUI界面中严禁使用while死循环刷新画面,死循环会抢占主线程,阻塞主循环,窗口直接卡死无响应,定时刷新优先使用wx.Timer。
wx.MessageBox属于模态弹窗,弹窗弹出之后,无法操作主窗口,必须点击确定关闭弹窗之后,才能继续操作。- 名单里面的文本使用英文逗号分隔,名字可以是中文、英文。
二、简易计算器:Sizer网格自适应布局实战
源码
# 导入wxPython核心库importwx# 自定义计算器窗口类,继承wx.FrameclassCalcFrame(wx.Frame):def__init__(self):# 父类初始化:无父窗口,窗口标题,窗口初始宽高 300*420super().__init__(None,title="简易计算器 wxPython",size=(300,420))# 创建面板,作为所有按钮、输入框的容器panel=wx.Panel(self)# 创建垂直布局盒子,控件从上到下依次排列vbox=wx.BoxSizer(wx.VERTICAL)# 创建文本输入框# wx.TE_RIGHT:文字右对齐;wx.TE_READONLY:输入框只读,不能键盘直接输入,只能点按钮self.display=wx.TextCtrl(panel,style=wx.TE_RIGHT|wx.TE_READONLY)# 设置输入框字体:字号24,默认字体族,正常样式,加粗self.display.SetFont(wx.Font(24,wx.FONTFAMILY_DEFAULT,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_BOLD))# 将输入框加入垂直布局# wx.EXPAND:控件自动拉伸填充;wx.ALL:四周都设置内边距;border:边距大小vbox.Add(self.display,flag=wx.EXPAND|wx.ALL,border=10)# 创建网格布局:4行4列,控件横向间距5px,纵向间距5pxgrid=wx.GridSizer(4,4,5,5)# 定义所有按钮上显示的文字,顺序对应4×4网格排布buttons=["7","8","9","/","4","5","6","*","1","2","3","-","0","C","=","+"]# 循环遍历按钮文字列表,批量创建按钮fortxtinbuttons:# 创建按钮,文字为循环取出的txtbtn=wx.Button(panel,label=txt)# 所有按钮统一绑定同一个点击回调函数btn.Bind(wx.EVT_BUTTON,self.on_btn_click)# 将按钮加入网格布局,wx.EXPAND让按钮自动填满格子grid.Add(btn,flag=wx.EXPAND)# 将整个按钮网格,添加到外层垂直布局中vbox.Add(grid,flag=wx.EXPAND|wx.ALL,border=10)# 给面板设置布局管理器,布局才会生效panel.SetSizer(vbox)# 窗口在屏幕居中显示self.Centre()# 所有按钮共用的点击回调函数defon_btn_click(self,event):# 获取被点击按钮上的文字标签label=event.GetEventObject().GetLabel()# 获取当前输入框里面已经存在的文本text=self.display.GetValue()# 如果点击的是 C 按钮:清空输入框iflabel=="C":self.display.SetValue("")# 如果点击的是 = 按钮:执行计算eliflabel=="=":# try-except捕获计算过程中的异常,防止程序崩溃try:# eval函数把字符串表达式转为代码执行,计算结果转为字符串res=str(eval(text))# 将计算结果放回输入框self.display.SetValue(res)exceptException:# 表达式非法(除0、语法错误等),输入框显示Errorself.display.SetValue("Error")# 其余数字、运算符按钮:把字符追加到输入框末尾else:self.display.SetValue(text+label)# 程序入口if__name__=="__main__":# 创建应用实例app=wx.App()# 实例化计算器窗口frame=CalcFrame()# 显示窗口frame.Show()# 开启GUI主循环,持续监听事件app.MainLoop()分段解答
1. 模块导入与计算器窗口类定义
只需要导入wx库,新建CalcFrame类继承wx.Frame。
这个案例重点学习Sizer自动布局,不再使用固定坐标定位控件,是wxPython项目里推荐的标准写法。
2. 初始化布局容器与输入框
wx.BoxSizer(wx.VERTICAL)垂直盒子布局,控件自上而下摆放。wx.TextCtrl文本输入框,通过style参数设置两个特性:wx.TE_RIGHT文字靠右(符合计算器习惯),wx.TE_READONLY只读,禁止键盘直接输入。
使用vbox.Add()把控件加入布局,flag参数控制填充方式与边距。wx.EXPAND代表控件会自动拉伸填满可用空间。
3. GridSizer网格布局、批量生成按钮
wx.GridSizer(4,4,5,5)创建4行4列网格,后两个参数是格子横向、纵向间隙。
我们把所有按钮文字放到列表,通过for循环批量创建按钮、绑定事件、加入网格布局。
所有按钮绑定同一个回调函数,减少大量重复代码,是GUI开发很经典的写法。
网格布局添加完成之后,把网格整体放进外层垂直盒子,最后调用panel.SetSizer(vbox)布局才会生效,这一步很容易被小白漏掉。
4. 统一回调函数 on_btn_click
event.GetEventObject()获取触发事件的控件对象,再调用GetLabel()拿到按钮文字,以此区分不同按钮的功能:
- 点击
C:清空输入框; - 点击
=:执行计算; - 数字和运算符:直接追加到输入框字符串末尾。
5. try-except异常捕获与eval计算
eval可以直接把数学字符串表达式运算出结果。
外面包裹try except,用来捕获除零错误、表达式语法错误等异常。一旦出错,界面展示Error而不是直接闪退。
6. 程序入口和主循环
和抽奖器结构保持一致:创建App、创建窗口、显示窗口、启动主循环。
wxPython所有GUI程序都遵循这套固定模板。
7. 本案例注意事项
- Sizer布局和固定pos布局不要混用,布局逻辑容易混乱;正式开发优先选择Sizer。
eval存在极大安全风险,它可以执行任意Python代码,仅限学习Demo使用,线上、正式项目禁止直接接收外部输入交给eval执行。- 只读输入框只是限制键盘输入,代码依然可以调用
SetValue修改里面的文字,二者不冲突。 - 计算连续运算符、除以0等非法表达式都会触发异常,依靠try-except实现容错。
三、打包为独立exe程序
源码(打包命令)
# 抽奖器打包pyinstaller-F-wlottery.py# 计算器打包pyinstaller-F-wcalc.py分段解答
1. 命令参数说明
-F:打包为单个独立exe文件,方便拷贝分发;-w:运行程序时,隐藏黑色控制台小黑窗。
2. 注意事项
- 需要提前安装pyinstaller:
pip install pyinstaller; - 打包完成后,exe文件会生成在dist文件夹内;
- 如果代码中加载外部图片、txt资源,打包时需要额外配置资源路径。
四、总结
这两个小案例刚好对应wxPython入门里两类典型场景:
抽奖器侧重定时器动画、动态文字刷新和弹窗交互;计算器侧重Sizer自适应布局、统一事件分发。
GUI开发的核心就是三件事:界面组件、事件监听、布局管理。从这种注释详尽的小Demo入手,理解窗口主循环的运行逻辑,是非常轻松的入门路线。