以下内容为本人的学习笔记,如需要转载,请声明原文链接微信公众号「englyf」https://mp.weixin.qq.com/s/3Yb_YAKiMte_f5HanetXiA


本文大概 3617 个字,阅读需花 10 分钟
内容不多,但也花了一些精力
如要交流,欢迎评论区留言
谢谢你的点赞收藏分享

如果你接触过桌面 GUI 软件开发,那么你一定会对 MFC、WPF、Qt 等有或多或少的了解。

那么什么是 GUI 软件呢?GUI 软件是带有用户交互界面的软件,有按钮,有窗口,还有很多其它用于和用户交互的小部件。常见的例子是,比如各个厂家推出的浏览器,上面有标签,有按钮,有网址输入栏,有网页内容展示窗口和状态栏等等。

不过,这里打算为大家介绍一下使用 Python 怎么去做 GUI 开发,因为会 Python 的人更多,相信其中也有很多人对开发界面软件颇有兴趣。好,看下文。

Python 的 GUI 开发框架有好几种,比较主流的有 wxPython、PyQt、Tkinter,这三种都是跨平台方案。

wxPython 是 Python 的第三方库,代码实现基于 C++ 的 wxWidgets 库封装,呈现的界面风格和系统本地风格一致。其许可协议规定如果直接引用 wxPython 的二进制库文件,则可以随便使用。

可以看看在 linux 系统下基于 wxPython 的界面程序是什么样子

PyQt 是 Qt 平台的 Python 版本,自绘的界面风格,许可协议对商业应用不太友好。虽然和原生风格已经很接近了,但还是有人能挑出刺来。

可以看看在 linux 系统下基于 PyQt 的界面程序是什么样子

Tkinter 是 Python 自带的 GUI 开发框架,但也是自绘的界面风格。

可以看看在 linux 系统下基于 Tkinter 的界面程序是什么样子

值得一提的是,wxPython 也是 Python 作者主推的 GUI 开发框架。

今天先讲 wxPython,至于其它方案,会在后边的其它推文讲解,敬请关注。

环境配置

由于 VSCODE 的开放性和插件生态极其丰富的原因,推荐基于 VSCODE 来搭建开发环境。

本文以下内容基于 windows 10 和 Python3.

开发 Python 工程之前,先配置一个虚拟环境。

在 VSCODE 里打开选好的工程存放目录,然后点击 VSCODE 顶部菜单栏 Terminal -> New Terminal,弹出命令行终端,输入

python -m venv .venv

启动虚拟环境

.venv\Scripts\activate.bat

安装 wxPython

先查看一下当前环境里已经预装了哪些工具包

pip list

Output:

Package    Version
---------- -------
pip 21.1.1
setuptools 56.0.0
WARNING: You are using pip version 21.1.1; however, version 22.3.1 is available.
You should consider upgrading via the '.\.venv\scripts\python.exe -m pip install --upgrade pip' command.

建议升级一下 pip

python -m pip install --upgrade pip

Output:

Requirement already satisfied: pip in .\.venv\lib\site-packages (21.1.1)
Collecting pip
Using cached pip-22.3.1-py3-none-any.whl (2.1 MB)
Installing collected packages: pip
Attempting uninstall: pip
Found existing installation: pip 21.1.1
Uninstalling pip-21.1.1:
Successfully uninstalled pip-21.1.1
Successfully installed pip-22.3.1

安装 wxPython 包

pip install wxpython

Output:

Collecting wxpython
Using cached wxPython-4.2.0-cp38-cp38-win_amd64.whl (18.0 MB)
Collecting pillow
Using cached Pillow-9.3.0-cp38-cp38-win_amd64.whl (2.5 MB)
Collecting numpy
Using cached numpy-1.23.5-cp38-cp38-win_amd64.whl (14.7 MB)
Collecting six
Using cached six-1.16.0-py2.py3-none-any.whl (11 kB)
Installing collected packages: six, pillow, numpy, wxpython
Successfully installed numpy-1.23.5 pillow-9.3.0 six-1.16.0 wxpython-4.2.0

再查看一下实际都安装了哪些工具包

Package    Version
---------- -------
numpy 1.23.5
Pillow 9.3.0
pip 22.3.1
setuptools 56.0.0
six 1.16.0
wxPython 4.2.0

看到,wxpython 其实还依赖了 numpy、Pillow、six 这几个包。

基本程序

先来看一看一个基本的 wxPython GUI 程序骨架,新建文件 main.py,输入以下内容并保存。

// main.py

import wx

app = wx.App()
window = wx.Frame(parent=None, title="hello Python GUI APP!")
window.Show()
app.MainLoop()

导入 wx 库,也就是 wxPython 库,首先实例化类 wx.App

wx.App 类代表着整个应用,被用于引导 wxPython 系统,初始化底层的 gui 工具包,设置或者获取应用级别的属性,实现本机窗口系统的主消息或者事件环,以及分派事件到各个窗口实例等。每个 wx 应用都必须有且仅有一个 wx.App 类实例,而且为了确保 gui 平台和小部件都被初始化完成,所有 UI 对象的创建必须在 wx.App 类实例创建之后。为了实现更复杂的初始化过程,可以对 wx.App 类执行派生,然后重写方法 OnInit,窗口初始化完成时会调用方法 OnInit

然后,创建 wx.Frame 类实例,初始化时,设置父窗口为空和设置标题为 hello Python GUI APP!

wx.Frame 是用户可以改变大小和位置的窗口类,调用 Show() 显示窗口。

wxPython 提供了非常丰富的窗口部件(widget)和辅助控件来简化复杂 GUI 的开发过程。基本的 widget 比如有输入框 TextCtrl,按键 Button,静态文本 StaticText,菜单栏 MenuBar,列表 ListCtrl等。简单的辅助控件比如有布局器 BoxSizer,菜单 Menu等。为了显示丰富的内容,除了可以直接使用框架提供的窗口部件,还可以对这些部件派生以添加更多功能。

最后,调用类 wx.App 对象的 MainLoop() 方法来启动主界面的事件环,这时候用户和界面交互才会有反应。

输入指令以执行程序

python main.py

通过显示的程序窗口,可看到整个程序只做了很基本的 UI 窗口显示,但是代码结构也相对简单。如果需要实现更复杂的窗口界面呢?往下看。

使用 wxFormBuilder 设计复杂 UI

从上一节的 demo 代码中,看到要实现显示一个窗口是很简单的,但是如果窗口内部包含了很多其它部件呢?

继续手堆代码吗?不是不可以,但是这样子的做法对工程或者项目后期维护是很不利的。

那么怎么办?很巧,wxWidgets 框架提供了一个 GUI 构建器 wxFormBuilder。

打开 wxFormBuilder,可以看到提供了非常多的工具,而且自动创建一个空白工程。为了设计需要的界面,开发者可以通过手动点击组件面板(Component Palette)中不同种类的各种控件来布局界面,同时在编辑器(Editor)中生成设计图和各种开发语言的代码,支持 C++, Python, XRC, Lua 和 PHP 等。设计完成后,开发者只需要把对应页面的目标语言代码拷贝到自己的工程源文件中即可直接使用。

如需要安装 wxFormBuilder,建议前往官方下载页面获取对应平台安装包

https://github.com/wxFormBuilder/wxFormBuilder/releases

下面就使用 wxFormBuilder 来设计一个简单的界面。

一般界面都以 Frame 为底,所以在空白工程基础上添加一个 Frame 窗口。点击工作空间顶部的组件面板(Component Palette) -> Forms 类型 -> Frame 控件,如图

先在左边的控件树(Object Tree)面板里点击刚添加的 Frame 控件,然后在右边的对象属性(Object Properties)面板中就可以修改这个控件的属性了。这里修改控件的属性 name 为 w_frame_xrc,title 为 hello Python GUI APP!。最终输出代码会以一个窗口类的形式输出,而最底层窗口的 name 属性会决定这个窗口类的类名。

打算设计一个简单的名单录,为演示简单起见,只添加人员名称,而不编辑或者删除。按照元素控件的层次逐个添加,设计图最终效果图如下

如需要获取本工程所有源文件,包括设计文件等,可查看文末的链接。

设计界面完成后,点击编辑器(Editor)中的 Python 标签,复制窗口内所有内容,粘贴保存到新建的源文件 w_frame_xrc.py 中。

// w_frame_xrc.py

# -*- coding: utf-8 -*-

###########################################################################
## Python code generated with wxFormBuilder (version Oct 26 2018)
## http://www.wxformbuilder.org/
##
## PLEASE DO *NOT* EDIT THIS FILE!
########################################################################### import wx
import wx.xrc ###########################################################################
## Class w_frame_xrc
########################################################################### class w_frame_xrc ( wx.Frame ): def __init__( self, parent ):
wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"hello Python GUI APP!", pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) bSizer1 = wx.BoxSizer( wx.VERTICAL ) bSizer2 = wx.BoxSizer( wx.VERTICAL ) sbSizer1 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"输入信息" ), wx.VERTICAL ) bSizer3 = wx.BoxSizer( wx.HORIZONTAL ) self.m_staticText_name = wx.StaticText( sbSizer1.GetStaticBox(), wx.ID_ANY, u"名字:", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText_name.Wrap( -1 ) bSizer3.Add( self.m_staticText_name, 0, wx.ALL, 5 ) self.m_textCtrl_name = wx.TextCtrl( sbSizer1.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer3.Add( self.m_textCtrl_name, 1, wx.ALL, 5 ) sbSizer1.Add( bSizer3, 1, wx.EXPAND, 5 ) bSizer2.Add( sbSizer1, 1, wx.EXPAND, 5 ) bSizer4 = wx.BoxSizer( wx.VERTICAL ) bSizer4.Add( ( 0, 0), 1, wx.EXPAND, 5 ) self.m_button_add = wx.Button( self, wx.ID_ANY, u"添加", wx.DefaultPosition, wx.DefaultSize, 0 )
bSizer4.Add( self.m_button_add, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) bSizer4.Add( ( 0, 0), 1, wx.EXPAND, 5 ) bSizer2.Add( bSizer4, 1, wx.EXPAND, 5 ) bSizer1.Add( bSizer2, 0, wx.EXPAND, 5 ) self.m_staticText2 = wx.StaticText( self, wx.ID_ANY, u"列表:", wx.DefaultPosition, wx.DefaultSize, 0 )
self.m_staticText2.Wrap( -1 ) bSizer1.Add( self.m_staticText2, 0, wx.ALL, 5 ) bSizer5 = wx.BoxSizer( wx.VERTICAL ) self.m_listCtrl_info = wx.ListCtrl( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT )
bSizer5.Add( self.m_listCtrl_info, 1, wx.ALL|wx.EXPAND, 5 ) bSizer1.Add( bSizer5, 1, wx.EXPAND, 5 ) self.SetSizer( bSizer1 )
self.Layout() self.Centre( wx.BOTH ) def __del__( self ):
pass

为了使用 wxFormBuilder 构建器生成的代码,可以简单修改前面的代码,如下

// main.py

import wx
import w_frame_xrc app = wx.App()
window = w_frame_xrc.w_frame_xrc(parent=None)
window.Show()
app.MainLoop()

执行程序

python main.py

现在界面看起来有点内容了!

实现逻辑

前面的界面还不具备任何的实际功能,为了让其执行设计的功能,添加额外的逻辑在所难免。

比如,点击按钮 添加,就触发动作把输入框中的名字添加到下方的列表中,并清空输入框,因此需要在原来的窗口类 w_frame_xrc.w_frame_xrc 基础上添加一些逻辑功能代码。

不过,wxFormBuilder 构建出来的代码一般不建议直接修改,所以先对原来的窗口类 w_frame_xrc.w_frame_xrc 进行派生,再在派生类中补充逻辑功能代码。派生类的代码存在单独的源文件 w_frame.py 中。

// w_frame.py

import wx
import w_frame_xrc class w_frame(w_frame_xrc.w_frame_xrc):
def __init__(self, parent):
super(w_frame, self).__init__(parent) self.m_listCtrl_info.ClearAll()
self.m_listCtrl_info.InsertColumn(0, u'名字', width=140) self.m_button_add.Bind(wx.EVT_BUTTON, self.on_button_add) def on_button_add(self, event):
value = self.m_textCtrl_name.GetValue()
if not value:
print("You didn't enter anything!")
else:
self.m_listCtrl_info.InsertItem(self.m_listCtrl_info.GetItemCount(), value)
self.m_textCtrl_name.Clear()

可以看到,当控件有特定的事件需要绑定连接到处理句柄时,可以通过 Bind() 方法,传入 EVT_xxx 事件类型和处理句柄(可调用对象,比如,函数等)。如果需要将已绑定的某个事件断开连接,可以将处理句柄位置参数设为 None 即可。

然后,main.py 也需要稍作修改,如下

// main.py

import wx
import w_frame app = wx.App()
window = w_frame.w_frame(parent=None)
window.Show()
app.MainLoop()

好了,现在再来测试一下刚添加的逻辑,程序启动后往里添加几个名单看看吧。

python main.py

部署发布

目前来看,工程里都是一些以源码文件形式存在的脚本,但是在最终用户使用时,都是习惯于直接双击一个 exe 文件来启动软件进程。

下面就介绍一种对 Python 脚本工程打包的工具,目标是最终输出一个可执行的 exe 文件。

这个工具就是 pyinstaller,使用之前需要确认一下自己的环境里是否已经安装有这个第三方包,还用指令 pip list 即可查看。

如果确认过没有,那么用下面的指令可以安装

pip install pyinstaller

假设已经安装完毕,直接打包。选项 -F 后边输入启动脚本文件

pyinstaller -F main.py

启动打包过程之后,工程目录下面会自动生成一个新目录 dist 用于存放输出的目标文件。由于上面的打包指令没有指明输出的目标文件名,所以默认输出为脚本文件同名,如 main.exe。

如果需要指明输出的目标文件名,可以加上选项 -n。比如要输出目标为 demo.exe,可以这样

pyinstaller -F main.py -n demo

也许有的同学喜欢让打包的输出文件带上图标,那么可以加上选项 -i。比如工程目录里有一份图标文件 logo.ico,需要让打包后输出文件带上这个图标,可以这样

pyinstaller -F main.py -n demo -i logo.ico

打包完毕,双击程序 demo.exe,可能会发现在运行起来的软件背景里,老是有个命令行的窗口,这样子真的很碍眼!

怎样把终端窗口给隐藏掉呢?打包的时候带上选项 -w,这样

pyinstaller -F main.py -n demo -i logo.ico -w

网上有些同学喜欢吐槽 pyinstaller 打包出来的目标文件体积过大,关于这个问题的解决思路是,工程开发(包括目标文件打包输出)应该在配置好的单独虚拟环境下进行,环境中不应该安装任何不需要的第三方包!

全文到这里算是结束了,欢迎你的留言!

​工程代码仓库:​git@github.com:ifi-leung/python_gui_wx.git

Python:界面开发,wx入门篇的更多相关文章

  1. Mr.聂 带你成为web开发大牛——入门篇(上)

    作为一名IT届的后生,当初也经历过懵懂无知的实习期,对那种无力感深有体会.在这,希望能用我这几年的开发经验,让各位即将踏入或者刚刚踏入web开发领域的新人们少走些弯路.鉴于这是入门篇,下面我就从零为大 ...

  2. 学习笔记之盘一盘 Python 系列 1 & 2 - 入门篇

    盘一盘 Python 系列 1 & 2 - 入门篇 https://mp.weixin.qq.com/s?__biz=MzIzMjY0MjE1MA==&mid=2247486473&a ...

  3. python学习之路入门篇

    本文是up学习python过程中遇到的一些问题及总结归纳,本小节是入门篇. python基本语法 循环.分支不多赘述. 模块:一个.py文件就是一个模块. 文件和异常 模式 含义解释 “r” 读模式 ...

  4. 小白的 Python 修炼手册:入门篇

    Life is short, you need Python.(人生苦短,我用 Python.) --Bruce Eckel 前言 听说现在是全民 Python 的时代,虽然不知道事实如何,但学会 P ...

  5. 微信小程序开发之入门篇(熟悉开发工具)

    个人的每一篇博文都谈不上有什么技术含量,只是为了帮助不熟悉微信小程序开发的自己及他人提供一下思路.谢谢,下面开始! PS: 因为本人没有小程序的内测资格,所以所有的开发及Demo都是无AppId的,如 ...

  6. 学习python之路_入门篇A

    偶尔经同事的介绍进入了金角大王的博客里,看到大王编写的文章都是关于python编程的,由于自己一直也是做软件测试方面的工作,也一直想往自动化测试方面发展,了解到利用python可以进行自动化测试操作, ...

  7. 26-ESP8266 SDK开发基础入门篇--编写WIFI模块 SmartConfig/Airkiss 一键配网

    https://www.cnblogs.com/yangfengwu/p/11427504.html SmartConfig/Airkiss 配网需要APP/微信公众号,这节大家先使用我做好的APP/ ...

  8. 1-ESP8266 SDK开发基础入门篇--开发环境搭建

    因为今天终于做好了自己的另一块工控板,所以我就开始写基础公开篇的内容,希望自己小小的努力能够帮到大家 自己做的另一块板子 https://www.cnblogs.com/yangfengwu/cate ...

  9. 5-ESP8266 SDK开发基础入门篇--了解一下操作系统

    对于操作系统不知道有没有害怕接触的... 先说一下操作系统是什么意思,其实咱的电脑就运行了操作系统,手机,等等... 操作系统和任务分不开,所谓任务就是一个一个的执行各个功能的函数,,,操作系统呢就是 ...

  10. Android 开发基础入门篇: android studio安装教程

    下载地址 http://www.android-studio.org/ 注意: 安装主要分两种情况,下载的自带SDK和不带SDK两种 然后又分为安装版,就是.exe和解压版 两种的区别...解压版,, ...

随机推荐

  1. Grafana的基本概念

    Grafana基本概念 首先Grafana是一个通用的可视化工具.'通用'意味着Grafana不仅仅适用于展示Prometheus下的监控数据,也同样适用于一些其他的数据可视化需求.在开始使用Graf ...

  2. Service中spec.type 字段的值:ClusterIP和NodePort理解

    ClusterIP(默认) 在群集中的内部IP上公布服务,这种方式的 Service(服务)只在集群内部可以访问到 [root@master ~]# kubectl get service -n te ...

  3. .Net WebApi 中的 FromBody FromForm FromQuery FromHeader FromRoute

    在日常后端Api开发中,我们跟前端的沟通中,通常需要协商好入参的数据类型,和参数是通过什么方式存在于请求中的,是表单(form).请求体(body).地址栏参数(query).还是说通过请求头(hea ...

  4. HQL中出现XXX is not mapped的错误

    我的代码如下 @Test public void testCollection(){ String hql = "from Order where orderItems is not emp ...

  5. 绝杀processOn,这款UML画图神器,阿里字节都用疯了,你还不知道?

    大家好,我是陶朱公Boy,又和大家见面了. 前言 在文章开始前,想先问大家一个问题,大家平时在项目需求评审完后,是直接开始编码了呢?还是会先写详细设计文档,后再开始进行编码开发? ☆现实 这个时候可能 ...

  6. Kubeadm部署高可用K8S集群

    一 基础环境 1.1 资源 节点名称 ip地址 VIP 192.168.12.150 master01 192.168.12.48 master02 192.168.12.242 master03 1 ...

  7. Linux基础_2_bash功能

    查看当前shell:echo  $SHELL 查看可用shell:cat  /etc/shells 命令行编辑 光标跳到行首:Ctrl+a 光标跳到行尾:Ctrl+e 以单词为单位快速跳转光标:Ctr ...

  8. DevOps|1024程序员节怎么做?介绍下我的思路

    1024,祝每个程序员小哥哥小姐姐节日快乐. 因为在研发效能部门,我支持过几次 1024 程序员节的活动,所以经常有朋友问我1024 程序员节怎么做,本篇就是简单介绍下我的思路,希望对你有用. 102 ...

  9. 文本挖掘与NLP笔记——代码向:分词

    分词:jieba.cut words = jieba.cut("我来到北京大学",cut_all=True) print('全模式:'+'/'.join([w for w in w ...

  10. CSS 属性选择器 ~=, |=, ^=, $=, *= 的区别

    CSS 属性选择器 ~=, |=, ^=, $=, *= 的区别 总结: "value 是完整单词" 类型的比较符号: ~=, |= "拼接字符串" 类型的比较 ...