2026/10/17 10:05:55

wxPython 入门教程

wxPython 入门教程 前言wxPython 是 C 图形界面库 wxWidgets 的 Python 绑定层。它属于第三方库不在 Python 标准库里必须用pip install wxPython单独安装本机没有装、也装不了所以本文的代码只能逐行推演不能当场运行。它最大的特点是在不同平台上调用系统原生控件Windows 上按钮长得像 Windows 按钮macOS 上像 macOS 按钮因此观感比自绘界面的工具包更贴近系统。初学者最常见的误解有三个。第一以为wx.Frame一创建窗口就会显示出来——实际上要显式调用Show()。第二以为import wx之后必须自己写事件循环——实际上 wxPython 把事件循环封在App.MainLoop()里你只要继承wx.App并实现OnInit就行。第三以为可以把wx.Frame当成普通的 Python 对象随便跨线程操作——GUI 工具包几乎都要求只在主线程更新控件。本文讲清楚三件事wxPython 的整体结构、一个最小可运行程序的骨架、以及事件绑定与布局器sizer的基本用法。wxPython 的具体 API 与参数以该库官方文档为准本文只保证结构和方法名与文档一致不保证每个可选参数的默认值。一、整体结构App、Frame、Panel 三层wxPython 程序的骨架固定是三层wx.App ── 应用程序对象拥有事件循环一个进程一般只有一个└── wx.Frame ── 顶层窗口有标题栏、边框、可最小化└── wx.Panel ── 容器面板是控件的父窗口└── 各种控件Button、StaticText、TextCtrl ...这样分层的原因和 wxWidgets 的底层模型有关控件需要一个「父窗口」才能确定自己的坐标参照系和生命周期。wx.Panel还承担一个实际职责——把 Tab 键的遍历顺序限制在面板内部并且在某些平台上让背景色正确刷新。二、最小可运行骨架下面这个例子展示了一个带标签和按钮的窗口。它的写法遵循官方文档给出的推荐结构继承wx.App、实现OnInit、在OnInit里创建窗口。# 适用于 Python 3.8需要先 pip install wxPython属第三方库import wxclass MainFrame(wx.Frame):def __init__(self):# parent 传 None 表示这是顶层窗口super().__init__(parentNone, titlewxPython 示例)panel wx.Panel(self)sizer wx.BoxSizer(wx.VERTICAL)self.label wx.StaticText(panel, label你好wxPython)button wx.Button(panel, label点我)# Bind 把「事件类型 处理函数」登记到控件上button.Bind(wx.EVT_BUTTON, self.on_click)# Add(控件, proportion, flag, border)sizer.Add(self.label, 0, wx.ALL | wx.EXPAND, 8)sizer.Add(button, 0, wx.ALL | wx.EXPAND, 8)panel.SetSizer(sizer)def on_click(self, event):self.label.SetLabel(按钮被点击了)class MyApp(wx.App):def OnInit(self):frame MainFrame()frame.Show() # 不调用 Show()窗口不会出现self.SetTopWindow(frame)return True # 返回 False 会让程序立刻退出if __name__ __main__:app MyApp()app.MainLoop() # 进入事件循环阻塞在这里逐行推演几个关键点super().__init__(parentNone, title...)调用的是wx.Frame的构造函数官方文档里的签名是Frame(parent, idID_ANY, title, posDefaultPosition, ...)id有默认值所以可以省略。wx.BoxSizer(orient)的orient取wx.VERTICAL或wx.HORIZONTAL文档明确写了这两个常量名。Sizer.Add的文档签名是Add(window, proportion0, flag0, border0, userDataNone)其中proportion是「是否吸收多余空间」的权重flag是位掩码wx.ALL、wx.EXPAND等border是边距像素数。OnInit必须返回True否则MainLoop不会启动。三、事件绑定Bind 与事件对象wxPython 的事件模型是「事件类型 处理函数」通过Bind登记# 适用于 Python 3.8需先安装 wxPython# 文档签名Bind(self, event, handler, sourceNone, idwx.ID_ANY, id2wx.ID_ANY)self.Bind(wx.EVT_BUTTON, self.on_click, idwx.ID_EXIT)source参数用来指定事件源控件id用来按控件 ID 过滤。官方文档给出的另一种写法是wx.EVT_MENU配合idwx.ID_EXIT# 适用于 Python 3.8需先安装 wxPythonself.Bind(wx.EVT_MENU, self.OnExit, idwx.ID_EXIT)处理函数的第一个参数是事件对象习惯上叫event里面带事件的来源、位置等信息事件对象一般还要调用它的Skip()把事件继续传递下去具体方法名以官方文档为准。四、布局sizer 而不是绝对坐标wxPython 用「布局器」sizer来自动排布控件。官方文档把wx.BoxSizer定位为最基本的一种控件沿一个方向排成一行或一列proportion决定谁吸收剩余空间。除此之外还有网格布局器wx.GridSizer/wx.FlexGridSizer、以及wx.GridBagSizer这种支持跨行列的复杂布局器。布局方式特点适用场景wx.BoxSizer单方向线性排列proportion分配剩余空间表单、工具栏、简单对话框wx.FlexGridSizer网格可让某些行或列随窗口伸缩设置面板、参数表wx.GridBagSizer网格 跨行列 精细定位复杂自适应界面绝对坐标SetPosition位置写死不随窗口变化一般不推荐只有动画类场景例外用 sizer 而不是绝对坐标的理由和所有 GUI 工具包一样字体大小、DPI 缩放、系统主题一变写死的像素坐标就全部错位sizer 会重新协商尺寸。嵌套 sizer 是常见做法外层wx.BoxSizer(wx.VERTICAL)分成「内容区 按钮条」按钮条内部再放一个wx.BoxSizer(wx.HORIZONTAL)横向排列两个按钮。五、样式、高分屏与打包控件的外观通过构造时的style位掩码控制例如给wx.Frame传wx.DEFAULT_FRAME_STYLE、给文本框传只读样式等具体常量名以官方文档为准。窗口级别的属性标题、尺寸、最小尺寸、状态栏文本由wx.Frame及其基类wx.TopLevelWindow提供常见的有SetTitle、SetSize、SetMinSize、CreateStatusBar/SetStatusText。高分屏是一个真实且高频的问题。Windows 在 125% / 150% 缩放下如果程序没有声明自己是 DPI 感知的系统会先把界面渲染再整体放大结果就是文字和图标发虚。wxPython 与 wxWidgets 提供了 DPI 感知的相关选项但具体开关名与生效范围以官方文档为准不要凭印象写。稳妥的做法是不要写死像素尺寸字体用系统默认字号让 sizer 去分配空间。「打包成 exe」是另一个独立话题。用 PyInstaller 这类工具打包时它会把 Python 解释器、标准库和 wxPython 依赖的运行时一起塞进包里因此体积通常很大、首次启动偏慢而且被杀毒软件误报的概率不低。能做的缓解手段是选用单文件还是目录模式、排除无用模块、给可执行文件签名但没有任何办法能保证「一定不被误报」。这些属于打包工具的配置项具体参数以其官方文档为准。常见坑点❌ 创建了wx.Frame就直接等窗口出现。✅ 必须显式调用frame.Show()否则窗口对象存在但不可见。这是官方文档里点名的早期错误。❌ 把耗时任务直接写在按钮回调里。✅ 回调里只更新界面耗时活儿交给工作线程再把结果用线程安全的方式交回主线程。写在回调里会让界面「假死」因为事件循环被你占住了。❌ 在子线程里直接调用SetLabel、Show等控件方法。✅ 大多数 GUI 工具包要求只在主线程更新控件子线程里改控件行为未定义或直接崩溃。跨线程通信用队列或工具包提供的事件投递机制。❌ 用绝对坐标逐像素摆放控件。✅ 用 sizer让布局随窗口大小、字体、DPI 自动调整否则换台机器就错位。❌OnInit忘记return True。✅OnInit返回值决定事件循环是否启动返回假值程序会直接退出且往往没有任何报错提示非常难查。❌ 忘记调用app.MainLoop()。✅ 没有MainLoop窗口创建完程序就结束了事件循环必须在脚本最后启动。❌ 安装时把包名写错或指望它是标准库。✅ 它是第三方库装的时候叫wxPython导入的时候叫wx两者名字不一样。总结要点结论库的性质第三方库绑定 C 的 wxWidgets使用原生控件程序骨架wx.App子类 OnInitwx.Framewx.PanelMainLoop事件模型Bind(事件类型, 处理函数)处理函数收一个 event 对象布局用wx.BoxSizer等布局器不用绝对坐标线程规则只在主线程更新控件文档具体 API 与参数以 wxPython 官方文档为准wxPython 的价值在于「原生观感 成熟的布局器体系」代价是必须额外安装、打包后体积偏大。入门时先把 App / Frame / Panel / Sizer / Bind 这五件事的分工记牢比背控件清单有用得多。