PySimpleGUI:快速開始

好,我們的第一個 GUI 程序用大家一般在 Python 學習階段都用過的一個簡單的模擬登錄。

  • 預設賬號和密碼
  • 輸入賬號
  • 輸入密碼
  • 點擊提交
  • 判斷賬號和密碼是否匹配,都匹配則返回登錄成功,否則登錄失敗。

基本邏輯如下:

user = input('請輸入賬號:')
password = input('請輸入密碼:')
if user == 'admin' and password == '123':
	print('登錄成功!')
else:
	print('登錄失敗!')

相信很多同學都在控制檯上做過這個練習。接下來呢,我們用 GUI 實現。

寫一個 GUI 程序有以下步驟:

  1. 理清楚需求
  2. 畫出原型圖
  3. 分解原型圖中的元素,構造佈局生成窗口
  4. 獲取窗口事件和數據
  5. 運行事件和數據處理邏輯
  6. 反饋結果
  7. 重複 4~6 直到點擊退出
  8. 退出並銷燬窗口

定義需求

定義需求:
用戶在界面上輸入賬號和密碼,當點擊提交時判斷賬號和密碼是否匹配,匹配則彈出登錄成功,失敗則彈出紅字標識的登錄失敗。

畫原型圖

好,接下來我們畫一個原型圖,畫原型圖大家可以使用一些需求工具:

  • Axure:很強大的原型圖工具,但是用起來學習成本也比較高
  • Balsamiq Mockups:草圖工具,需要購買才能使用
  • MockPlus:國產的原型圖工具,畫原型圖免費,但是要導出需要收費

還有其他很多相關的原型圖工具,請大家自行下載,當然你也可以直接在紙上畫:

醜是醜了點,意思到了就行。簡單的我們就直接畫了,如果複雜的界面還是需要藉助工具。

分解原型圖並構建佈局

我們來看一下原型圖中可能會用到的界面元素(也有地方叫做控件、部件、組件等,我們這裏統一叫做元素好了)。

  1. 窗口標題,還有窗口上的 -縮小、適應屏幕、x關閉。除了標題,其他自動構建;
  2. 賬號輸入框,這裏有兩個元素:一個文本,顯示字符串“賬號”,一個輸入框
  3. 密碼輸入框,這裏也有兩個元素:一個文本,顯示字符串“密碼”,一個密碼輸入框
  4. 一個提交按鈕
  5. 兩個彈出窗口,一個彈出“登錄成功”,另一個彈出紅色文字的“登錄失敗”。

在PySimpleGUI 中佈局是按行來處理的。每個元素都處於佈局中的某一行。其中第 1 點中的標題欄不屬於佈局中的內容,第 5 點的彈出窗口屬於特殊元素,不用加入佈局。

我們來看看在 PySimpleGUI 中如何通過行來管理的:

  • 整個佈局是一個大的列表list
  • 每一行是這個大列表中的子列表;
  • 每一行中的元素是這個列表的元素。

比如第 2 點的賬號輸入框,有兩個元素,我們需要構建一個列表:

[文本, 輸入框]

整個佈局應該是這樣:

[
	[文本, 輸入框],
	[文本, 輸入框],
	[按鈕]
]

如果更復雜的佈局,就在這個列表中添加,比如你想在賬號輸入框後面添加一個複選框 checkbox 用來勾選是否保存賬號,那麼佈局會變成這樣:

[
	[文本, 輸入框, 複選框],
	[文本, 輸入框],
	[按鈕]
]

是不是很簡單?

好了,我們還是回到我們這個小程序。

這裏我們用到了三個元素,文本、輸入框和按鈕:

  • 文本: Text 或者 T,它接收的第一個參數是要顯示的文本;
  • 輸入框:InputText 或 Input 或 In,可以不用輸入參數,是用來接收輸入的單行文本。如果要用作密碼輸入框,那麼用password_char參數來指定替換的字符串,比如你要用*作爲屏蔽字符,就寫爲password_char='*'
  • 按鈕: Button 或 Btn 或 B,接收的第一個參數是按鈕顯示的文本。

這裏我們可以看到作者給出了很多簡寫,方便你使用但其實也增加了一些記憶負擔,你可以在可讀性的基礎上記住其中一個就行。

好,那麼接下來,我們就把這些元素替換到我們佈局的列表中去:

import PySimpleGUI as sg # 引入PySimpleGUI,注意大小寫

# 創建佈局
layout = [
    [sg.Text('賬號'), sg.Input(key='_USER_')],
    [sg.Text('密碼'), sg.Input(password_char='*', key='_PWD_')],
    [sg.Btn('提交', key='_LOGIN_')]
]

這裏的一些元素加上了 key 參數,這有什麼用?是不是都要加?

key有什麼用? key 參數主要用於後面接收事件和獲取用戶輸入。
是不是都要加? 像文本元素這種後續基本不會再處理的,可以不加。但是後續你如果要接收它的事件(比如按鈕被點擊),獲取用戶輸入(輸入框中錄入的數據),改變元素(比如某種情況觸發需要改變字體顏色等),那麼這些被操作的元素一定要用 key 參數指定一個唯一的標識。
如何加? 作者建議,使用大寫字母,前後加下劃線的方式增加標識的可讀性和可識別性。

好了,一個佈局就生成了,是不是很簡單。

接下來把佈局列表作爲參數傳入窗口實例就可以了:

# 創建窗口,生成窗口實例
window = sg.Window('登錄', layout=layout, finalize=True)

Window 第一個參數是窗口標題,第二個參數我們傳入了前面定義的佈局列表,第三個參數finalize不是必須的,用來定型窗口,這主要有一些操作必須在窗口定型後才能執行。

接下來你運行一下就可以看到一個窗口閃了一下。

《震驚!這樣寫代碼居然會讓窗口閃一下就沒了!》

好吧,如果你不加 finalize 參數,甚至閃都不會閃。我們還是把過程寫完吧,這本來就是半成品。

事件循環

事件循環包含三個我們需要處理的過程:

  • 獲取窗口事件和數據
  • 運行事件和數據處理邏輯
  • 反饋結果

爲什麼要用循環?任何 GUI 窗口都是利用循環機制,不斷循環接收用戶輸入和用戶操作事件,然後處理,直到用戶點擊退出爲止。如果不循環,窗口運行一次就會關閉。

在循環過程中,我們需要使用 WindowRead 方法來接收用戶的事件和輸入數據:

event, value =  window.Read()  
# event, value 的值分別是 _LOGIN_ {'_USER_': 'admin', '_PWD_': '123'}

返回的第一個值 event,其結果是接收界面的事件,這裏是接收按鈕點擊事件。如果按鈕被點擊,event 的值將是被點擊元素的 key 參數設置的標識符,比如我們這裏的提交按鈕的 key 設置爲 _LOGIN_,那麼當提交按鈕被點擊時,event 的值就是 _LOGIN_

如果沒有設置 key,event 的值將會是按鈕文本,那這裏就會是中文的提交

第二個返回值是 value,這是一個以字典形式記錄接收到界面上的用戶輸入。我們之前在元素中設置的 key 參數將作爲字典的鍵,比如_USER__PWD_,可以通過這些 key 來取對應的輸入值。

如果你沒有爲輸入元素設置 key,那麼字典的鍵爲按照元素在 layout 中的順序,用下標 0, 1, 2… 來記錄用戶輸入的值。你就需要用下標按順序去讀取,如果元素很多的情況下,你很難保證正確性,因此我 建議一定要在元素定義的時候加上 key 參數,並且爲元素設置一個唯一的標識。

好了,接下來我們就對事件進行處理。

if event == '_LOGIN_':  # 當獲取到事件是提交按鈕被點擊時,處理賬號密碼判斷
    user = value['_USER_']   # 從返回值字典中提取賬號輸入框的值
    password = value['_PWD_'] # 從返回值字典中提取密碼輸入框的值
    if user == 'admin' and password == '123':
        sg.popup('登錄成功!')  # 彈出框
    else:
        sg.popup('登錄失敗!', text_color='red')  # 彈出框的字體設置爲紅色

popup() 是 PySimpleGUI 中提供的彈出框,用text_color參數設置字體顏色。

如果你用的是 Pycharm,直接 Ctrl+鼠標懸停 在類名、函數名上都會看到其參數及返回值。你在調用時,也可以看到有哪些參數,大部分參數命名都能見名知義,用的時候多留心。大致你想要的絕大部分功能都能實現。

代碼敲完,運行一下,是不是就可以進行輸入了。但是輸入一次窗口就關閉了,因爲上面說過,窗口值運行一次就結束。要想持續保持窗口運行狀態就需要加入循環,在特定情況(比如右上角的x和定義的元素事件)下退出循環,關閉窗口。

while True:  # 設置一個循環
	# 在特定條件下
	break

那麼什麼特定條件下退出呢?比如某個按鈕被點擊或者右上角的x被點擊。特定按鈕(比如界面上定義了一個按鈕,其 key 爲 _EXIT_)的話,你直接寫爲:

while True:
	if event == '_EXIT_':
		break

對於右上角的x被點擊,window.Read() 會接收到一個事件None。對,沒錯就是你熟悉的那個 None。那我們要響應右上角的x退出怎麼寫呢?

while True:
	if event is None:
		break

或者你想兩個地方(退出按鈕被點擊,右上角x被點擊)接收到事件都退出,那麼寫爲:

while True:
	if event in ['_EXIT_', None]:
		break

然後在循環退出後,銷燬窗口。

其實你也發現了,就算我們不做任何處理,窗口也會自動關閉。爲什麼還要多寫一句呢?這是因爲在某些系統中會出現異常,因此保持良好的習慣,在循環後加上退出代碼。

window.close()

是不是比你用 input 寫命令行復雜不了多少呢?

這裏獲取本節完整代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章