聽說過使用python在網站爬蟲,或者數據清洗,或者人工智慧原型驗證,但是用在GUI開發上倒是頭一回。今兒就來瞧瞧怎麼個玩法! ...
以下內容為本人的學習筆記,如需要轉載,請聲明原文鏈接 微信公眾號「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 打包出來的目標文件體積過大,關於這個問題的解決思路是,工程開發(包括目標文件打包輸出)應該在配置好的單獨虛擬環境下進行,環境中不應該安裝任何不需要的第三方包!
全文到這裡算是結束了,歡迎你的留言!
工程代碼倉庫:[email protected]:ifi-leung/python_gui_wx.git