GUI

Table of Contents

Hits #+HTML_HEAD_EXTRA:

1. Streamlit

Streamlit 是一個開源的 Python 庫,專為數據科學家、機器學習工程師和開發者設計,能夠快速構建美觀且功能強大的網頁應用程式。它最大的優勢在於不需要複雜的前端開發技術,只需用簡單的 Python 代碼即可生成互動式的網頁應用。Streamlit 常被用於展示數據分析結果、機器學習模型或交互式儀表板。

1.1. 特點:

  • 簡單易用:只需要使用 Python 代碼,無需學習 HTML、CSS 或 JavaScript 等前端技術。
  • 快速開發:用戶可以在幾分鐘內從數據分析轉變成一個全功能的網頁應用,適合快速開發與迭代。
  • 實時互動:用戶可以通過滑桿、下拉選單等組件與數據進行交互,實時更新視覺化結果。
  • 即時更新:任何對代碼的變更在保存後會自動刷新網頁,允許用戶快速查看變化。

1.2. 適用場景:

  • 數據展示與可視化:簡單地展示數據分析結果,通過圖表和文字解說進行報告。
  • 機器學習應用展示:使用 Streamlit 可以輕鬆將機器學習模型包裝成一個可操作的網頁工具,讓非技術人員也能進行模型預測。
  • 交互式儀表板:創建類似於 BI(商業智能)的儀表板,用於動態數據的展示與控制。

1.3. 安裝套件

1.3.1. 1. 使用前先安裝套件

1: pip3 install streamlit

1.3.2. 2. 利用pycharm或其他編輯器新增一個python程式碼,檔名先叫main.py,輸入下列程式、存檔

1: import streamlit as st
2: 
3: st.title('測試python的web玩具')

1.3.3. 3. 執行

在pycharm、visual code裡的終端機輸入以下指令

1: streamlit run main.py

1.3.4. 結果

2024-07-24_13-58-48_2024-07-24_13-58-19.png

Figure 1: Streamlit 執行畫面

1.4. 在 Google Colab 中執行 Streamlit

1.4.1. 安裝streamlit

1: !pip install streamlit -q

1.4.2. 取得外部IP

The second line (!wget -q -O - ipv4.icanhazip.com) retrieves your external IP address using the wget command.

1: !wget -q -O - ipv4.icanhazip.com

1.4.3. 生成app.py

以下程式碼使用 %%writefile app.py 將 Streamlit 應用程式寫入名為 app.py 的檔案。這個 Streamlit 應用是一個簡單的計算機,輸入兩個數字後自動加總並顯示結果。

這裡用到了 st.number_input() 來讓使用者輸入數字:

st.number_input(label, min_value=None, max_value=None, value=0, step=1)

參數 說明
label 顯示在輸入框上方的文字
min_value 最小值限制(預設 None 不限制)
max_value 最大值限制(預設 None 不限制)
value 預設值(預設為 0)
step 每次增減的步長(整數預設 1、浮點數預設 0.01)
1: %%writefile app.py
2: import streamlit as st
3: st.write('#Streamlit計算機')
4: num1 = st.number_input('數字1')
5: num2 = st.number_input('數字2')
6: num3 = num1 + num2
7: st.write('#答案', num3)
8: 

1.4.4. 執行app.py

!streamlit run app.py & 會在背景執行 Streamlit 應用,末尾的 & 讓 Colab 可以繼續執行其他 cell。

!npx localtunnel --port 8501 使用 localtunnel 將本機的 Streamlit 應用(port 8501)暴露到網路上,提供一個公開的 URL 讓你可以從瀏覽器存取。

1: !streamlit run app.py & npx localtunnel --port 8501
  • & 是一個 Linux/Unix Shell 的符號,表示在後台運行該命令。在這裡,它將 streamlit run app.py 放到後台執行,使得後續的指令可以繼續在同一個終端中執行,而不需要等待這個 Streamlit 應用結束。
  • npx:npx 是 Node.js 的一個工具,用來執行 Node.js 的命令。
  • localtunnel:localtunnel 是一個開源工具,允許你將本地運行的服務通過臨時的公共 URL 發布到互聯網上。它經常用來在開發環境中分享本地服務,或者測試時讓遠程用戶訪問。
  • –port 8501:這裡指定 localtunnel 將本地的 8501 端口開放到網路上。因為 streamlit run app.py 預設運行在 8501 端口上,所以 localtunnel 將這個端口的服務映射到一個臨時的公共 URL。

1.5. 秀資料

學生成績

 1: import streamlit as st
 2: import pandas as pd
 3: 
 4: st.title('學生成績')
 5: 
 6: df = pd.DataFrame({
 7:     '國文': [98, 76, 100, 38, 40],
 8:     '數學': [80, 46, 80, 67, 66]
 9: })
10: df
11: 
12: # 畫圖
13: st.line_chart(df)

1.6. 側邊欄 (Sidebar)

1.6.1. 測試一下

 1: import streamlit as st
 2: import pandas as pd
 3: 
 4: st.title('學生成績')
 5: st.write('lalala')
 6: # 數據
 7: df = pd.DataFrame({
 8:     '國文': [98, 76, 100, 38, 40],
 9:     '數學': [80, 46, 80, 67, 66]
10: })
11: 
12: st.sidebar.write('選項1')
13: st.sidebar.markdown('選項2')
14: with st.sidebar:
15:     st.write('選項3')
16: 
17: df

1.6.2. 練習

  • 請自行在sidebar和main area裡加入一些文字
  • 可以發現sidebar是可以接受markdown語法的,那麼,你能否應用markdown語法在sidebar裡放一個google的連結呢?

1.7. 選單

1.7.1. 連結式選單

 1: import streamlit as st
 2: import pandas as pd
 3: 
 4: st.title('學生成績')
 5: 
 6: # 數據
 7: df = pd.DataFrame({
 8:     '國文': [98, 76, 100, 38, 40],
 9:     '數學': [80, 46, 80, 67, 66]
10: })
11: 
12: # 側邊欄連結
13: st.sidebar.markdown("[查看數據](?page=table)")
14: st.sidebar.markdown("[畫圖](?page=chart)")
15: 
16: # 獲取當前URL參數
17: query_params = st.query_params
18: page = query_params.get("page", "table")
19: 
20: # 根據URL參數顯示相應的內容
21: if page == 'table':
22:     st.write('學生成績數據表')
23:     st.dataframe(df)
24: elif page == 'chart':
25:     st.write('學生成績折線圖')
26:     st.line_chart(df)

1.7.2. 下拉式選單

 1: import streamlit as st
 2: import pandas as pd
 3: import matplotlib.pyplot as plt
 4: 
 5: st.title('學生成績')
 6: 
 7: # 數據
 8: df = pd.DataFrame({
 9:     '國文': [98, 76, 100, 38, 40],
10:     '數學': [80, 46, 80, 67, 66]
11: })
12: 
13: # 側邊欄選擇
14: st.sidebar.write('選單')
15: option = st.sidebar.selectbox(
16:     '請選擇操作',
17:     ['查看成績', '畫折線圖']
18: )
19: 
20: # 根據選擇顯示相應的內容
21: if option == '查看成績':
22:     st.write('學生成績表')
23:     st.dataframe(df)
24: elif option == '畫折線圖':
25:     st.write('學生成績折線圖')
26:     fig, ax = plt.subplots()
27:     ax.plot(df['國文'])
28:     ax.plot(df['數學'])
29:     ax.set_xticks(range(5))
30:     ax.set_xticklabels(range(1, len(df) + 1))
31:     st.pyplot(fig)

1.7.3. 課堂練習

請參考Google Classroom中的[實作練習7]更專業的資料處理工具: Pandas,新增三種選單功能

  • 查看全班成績
  • 國文科成績長條圖
  • 各科Box圖
  • 請解決畫圖時中文出現亂碼的問題
  • 請善用ChatGPT

完成結果參考下圖:

2024-08-08_16-05-46_2024-08-08_15-09-45 (1).gif

1.8. 地圖

1.8.1. 單一地標

 1: import streamlit as st
 2: import pandas as pd
 3: 
 4: st.title('雲林智慧教育中心')
 5: 
 6: data = pd.DataFrame({
 7:     'lat': 23.67,
 8:     'lon': 120.39,
 9:     'name': ['雲林智慧教育中心']
10: })
11: 
12: st.map(data)

1.8.2. 兩個或以上地標

 1: import streamlit as st
 2: import pandas as pd
 3: 
 4: st.title('雲林')
 5: # 定義不同地點的數據
 6: locs = {
 7:     '雲林智慧教育中心': {'lat': 23.67, 'lon': 120.39},
 8:     '雲林高鐵站': {'lat': 23.738, 'lon': 120.428}
 9: }
10: 
11: data = pd.DataFrame([
12:     {'lat': locs['雲林智慧教育中心']['lat'], 'lon': locs['雲林智慧教育中心']['lon'], 'name': '雲林智慧教育中心'},
13:     {'lat': locs['雲林高鐵站']['lat'], 'lon': locs['雲林高鐵站']['lon'], 'name': '雲林高鐵站'}
14: ])
15: 
16: st.map(data)
17: 

1.8.3. 加入選單

 1: import streamlit as st
 2: import pandas as pd
 3: 
 4: st.title('雲林智慧教育中心')
 5: # 定義不同地點的數據
 6: locations = {
 7:     '雲林智慧教育中心': {'lat': 23.67, 'lon': 120.39},
 8:     '雲林高鐵站': {'lat': 23.738, 'lon': 120.428}
 9: }
10: 
11: # 在側邊欄中選擇地點
12: st.sidebar.write('選單')
13: option = st.sidebar.selectbox(
14:     '請選擇地點',
15:     ['雲林智慧教育中心', '雲林高鐵站'])
16: 
17: # 根據選擇的地點更新地圖數據
18: selected_location = locations[option]
19: data = pd.DataFrame({
20:     'lat': [selected_location['lat']],
21:     'lon': [selected_location['lon']],
22:     'name': [option]
23: })
24: selected_location['lat']
25: selected_location['lon']
26: option
27: 
28: st.map(data)

1.8.4. 課堂練習

  • 請加入你家的地圖
  • 請加入和你學校的地圖

1.9. 照相

 1: import streamlit as st
 2: 
 3: # 取得由相機輸入的影像緩衝資料
 4: imgBuffer = st.camera_input("Take a picture")
 5: 
 6: if imgBuffer is not None:
 7:     # 將緩衝資料轉換為二進位資料
 8:     imgData = imgBuffer.getvalue()
 9: 
10:     with open("/Users/letranger/Desktop/captured_image.png", "wb") as f:
11:         f.write(imgData)
12: 
13:     st.success("影像成功儲存!")

1.10. 部署應用程式 (Deploy your app)

1.10.1. 上傳至 GitHub

1.10.2. 部署到 streamlit.io

1.11. 研習用範例

1.11.1. 需要先在本機端安裝的套件

1: pip3 install pandas
2: pip3 install streamlit
3: pip3 install plotly
4: pip3 install scikit-learn

1.11.2. 範例程式

 1: import pandas as pd
 2: import streamlit as st
 3: import plotly.express as px
 4: from sklearn.cluster import KMeans
 5: 
 6: # 讀取資料
 7: test = pd.read_csv("https://letranger.github.io/working/scores.csv")
 8: 
 9: # Streamlit 應用程式標題
10: st.title("分數分佈")
11: 
12: # 設定側邊欄選單
13: st.sidebar.write('選單')
14: 
15: # 科目選擇下拉選單
16: option = st.sidebar.selectbox(
17:     '請選擇科目',
18:     ['國文', '英文', '數學', '物理']
19: )
20: 
21: # 顯示資料框
22: st.write("### 資料", test)
23: 
24: # 將分數依每10分分一組
25: test['binned'] = pd.cut(test[option], bins=range(0, 101, 10))
26: 
27: # 計算每個區間的頻率
28: frequency = test['binned'].value_counts().sort_index()
29: # 轉換為資料框格式,方便 Streamlit 顯示
30: frequency_df = pd.DataFrame({'分數區間': frequency.index.astype(str), '頻率': frequency.values})
31: 
32: # 使用 Streamlit 顯示長條圖
33: st.bar_chart(frequency_df.set_index('分數區間'))
34: 
35: # KMeans 聚類
36: # 使用滑桿選擇 k 值(聚類數量)
37: k = st.slider("選擇聚類的數量 k", 2, 10, 3)
38: kmeans = KMeans(n_clusters=k)  # 預設分為 k 群
39: kmeans.fit(test[['國文', '英文']])  # 使用 '國文' 和 '英文' 進行聚類
40: clusters = kmeans.predict(test[['國文', '英文']])
41: 
42: # 將聚類結果添加到資料框
43: test['聚類'] = clusters
44: 
45: # 使用 Plotly 繪製聚類結果的散點圖
46: fig2 = px.scatter(test, x='國文', y='英文', color='聚類', title='國文與英文分數的聚類結果')
47: st.plotly_chart(fig2)

1.12. 執行結果

你也可以把你開發好的程式透過Github發佈到Streamlit的免費雲端平台像這樣

Streamlit-Demo.gif

Figure 2: Streamlit執行範例

2. Gradio

讓只有command line I/O的python程式搖身一變成為web service。

2.1. 安裝套件

1: pip3 install gradio

2.2. Hello world1

gr.Interface 是 Gradio 最核心的類別,把一個 Python 函式包裝成網頁介面:

gr.Interface(fn, inputs, outputs, title=None, description=None)

參數 說明
fn 被 UI 裝飾的函式
inputs 輸入元件,可用字串簡寫如 "text", "image", "audio"
outputs 輸出元件,可用字串簡寫如 "text", "image", "label"
title 網頁標題(顯示在最上方)
description 標題下方的說明文字

多個輸入/輸出用 list 傳入,例如 inputs[“text”, “number”]=。Gradio 支援 20 多種不同的元件型別,其中大部分都可以作為輸入/輸出元件,詳見官網文件gradio Docs

1: import gradio as gr
2: 
3: def greet(name):
4:     return "Hello " + name + "!!"
5: 
6: demo = gr.Interface(fn=greet, inputs="text", outputs="text")
7: 
8: demo.launch()

2024-03-07_16-09-49_2024-03-07_16-09-26.png

Figure 3: 執行畫面

2.3. 數字 I/O

這裡用 gr.Slider 當作數字輸入元件,比起手動打數字方便多了:

gr.Slider(minimum=0, maximum=100, value=50, step=1, label=None)

參數 說明
minimum 滑桿最小值(預設 0)
maximum 滑桿最大值(預設 100)
value 預設值(預設 50)
step 每次滑動的步長(預設 1)
label 顯示在滑桿上方的標籤文字
 1: import gradio as gr
 2: 
 3: def BMI(h, w):
 4:   h /= 100
 5:   return f'BMI值: {w/(h*h)}'
 6: 
 7: ui = gr.Interface(
 8:     fn=BMI, inputs=[gr.Slider(100, 240, label="身高(cm)"), gr.Slider(40, 200, label="體重(kg)")],
 9:     outputs=["text"]
10: )
11: ui.launch(share=True)

2024-03-07_16-41-11_2024-03-07_16-40-52.png

Figure 4: 執行畫面

2.3.1. [課堂練習]三角形面積   TNFSH

輸入三角形三邊長(1..100),輸出面積、周長

2.4. 文字 I/O

 1: import gradio as gr
 2: 
 3: def greet(name):
 4:     return "Hello " + name + "!"
 5: 
 6: demo = gr.Interface(
 7:     fn=greet,
 8:     inputs=gr.Textbox(lines=2, placeholder="Name Here..."),
 9:     outputs="text",
10: )
11: demo.launch(share=True)

2024-03-07_16-44-41_2024-03-07_16-44-28.png

Figure 5: 執行畫面

2.5. 多資料 I/O

 1: import gradio as gr
 2: 
 3: def greet(name, is_morning, temperature):
 4:     salutation = "Good morning" if is_morning else "Good evening"
 5:     greeting = f"{salutation} {name}. It is {temperature} degrees today"
 6:     celsius = (temperature - 32) * 5 / 9
 7:     return greeting, round(celsius, 2)
 8: 
 9: demo = gr.Interface(
10:     fn=greet,
11:     inputs=["text", "checkbox", gr.Slider(0, 100)],
12:     outputs=["text", "number"],
13: )
14: demo.launch(share=True)

2024-03-07_16-46-19_2024-03-07_16-46-06.png

Figure 6: 執行畫面

2.6. 圖形 I/O

 1: import gradio as gr
 2: import matplotlib.pyplot as plt
 3: import numpy as np
 4: def curve(a, b, c):
 5:     x = np.arange(-3, 3, 0.3)
 6:     y = a*x**2 + b*x + c
 7:     fig = plt.figure()
 8:     plt.plot(x, y)
 9:     print(x)
10:     print(y)
11:     return fig
12: inputs = [gr.Slider(0, 10, 5), gr.Slider(0, 10, 5), gr.Slider(0, 10, 5)]
13: outputs = gr.Plot()
14: demo = gr.Interface(
15:     fn=curve,
16:     inputs=inputs,
17:     outputs=outputs,
18:     cache_examples=True,)
19: demo.launch(share=True)

2024-03-07_16-51-43_2024-03-07_16-51-32.png

Figure 7: 執行畫面

2.7. 按鈕 (Button)

前面的 gr.Interface 適合「一進一出」的簡單情境,如果需要自訂版面配置(例如自己決定元件排列方式),就要用 =gr.Blocks=:

with gr.Blocks() as demo:

  • with 區塊裡面放元件,就會按順序排列
  • 搭配 gr.Row(), gr.Column() 可以做水平/垂直排列
  • btn.click(fn=函式, inputs…, outputs=…)= 綁定按鈕事件
 1: import gradio as gr
 2: 
 3: def greet(name):
 4:     return "Hello " + name + "!"
 5: 
 6: with gr.Blocks() as demo:
 7:     name = gr.Textbox(label="Name")
 8:     output = gr.Textbox(label="Output Box")
 9:     greet_btn = gr.Button("Greet")
10:     greet_btn.click(fn=greet, inputs=name, outputs=output)
11: 
12: demo.launch(share=True)

2024-03-11_20-11-32_2024-03-11_20-10-15.gif

Figure 8: 執行畫面

2.8. Radio

 1: import gradio as gr
 2: 
 3: def greet(gender, name):
 4:     if gender == "Male":
 5:         return "Hello, Mr. " + name + "!"
 6:     else:
 7:         return "Hello, Ms. " + name + "!"
 8: 
 9: with gr.Blocks() as demo:
10:     input = [gr.Radio(["Male", "Female"]), gr.Textbox(label="Name")]
11:     output = gr.Textbox(label="Output Box")
12:     greet_btn = gr.Button("Greet")
13:     greet_btn.click(fn=greet, inputs=input, outputs=output)
14: 
15: demo.launch(share=True)

2024-03-11_20-13-52_2024-03-11_20-13-00.gif

Figure 9: 執行畫面

2.9. Tab(分頁)

適合多功能的程式(靜態的多重輸入)

 1: import gradio as gr
 2: 
 3: def tri(a, b, c):
 4:     import math
 5:     # 使用海龍公式計算半周長
 6:     s = (a + b + c) / 2
 7:     # 使用半周長計算面積
 8:     area = math.sqrt(s * (s - a) * (s - b) * (s - c))
 9:     return round(area,2)
10: 
11: def rect(a, b):
12:     return round(a*b,2)
13: 
14: area1 = gr.Interface(
15:     tri,
16:     [gr.Slider(label="長"), gr.Slider(label="寬"), gr.Slider(label="高") ],
17:     gr.Number(label="面積"),
18: )
19: 
20: area2 = gr.Interface(
21:     rect,
22:     [ gr.Slider(label="長"), gr.Slider(label="寬") ],
23:     gr.Number(label="面積"),
24: )
25: 
26: gr.TabbedInterface(
27:     [area1, area2], ["三角形", "長方形"]
28: ).launch()

2024-04-16_16-05-40_gradio-tabs.gif

Figure 10: 分頁式的Gradio

3. Flask

3.1. 按鈕 (Button)

Flask 用 @app.route() 裝飾器把 URL 路徑對應到 Python 函式:

@app.route(rule, methods[’GET’])=

參數 說明
rule URL 路徑,如 "/"=、“/page”=
methods 允許的 HTTP 方法,如 ['GET', 'POST']

rule 中可用 <variable> 接收 URL 參數,例如 @app.route("/user/<name>") 會把網址中的值傳給函式參數 =name=。

 1: from flask import Flask
 2: app = Flask(__name__)
 3: 
 4: @app.route("/")
 5: def hello():
 6:     title = "<title>Advanced Materials of Python</title>"
 7:     h1 = "<h1>Python based web</h1>"
 8:     p1 = "<p>這是用Python開發的網站</p>"
 9:     return title+h1+p1
10: 
11: if __name__ == "__main__":
12:     app.run()

FlashSite.jpg

Figure 11: Flask Web Site

3.2. 輸入表單 (Input form)2

2024-03-11_21-44-56_2024-03-11_21-43-18.gif

Figure 12: 執行畫面

3.2.1. Python

這個範例用到了 render_template() 來載入 HTML 模板:

render_template(template_name, **context)

參數 說明
template_name 模板檔名,如 "index.html"
**context 要傳給模板的變數,用關鍵字參數傳入

注意事項:

  • 模板檔案須放在 templates/ 資料夾裡(Flask 預設路徑)
  • 傳入的變數可在模板中用 =​{{ variable }}= 存取
 1: # Flask網站前後端互動 09 - 超連結與圖片
 2: # 載入Flask、Request、render_template
 3: from flask import Flask, request, render_template
 4: 
 5: app = Flask(__name__, template_folder='templates',
 6:             static_folder="public",
 7:             static_url_path="/")
 8: 
 9: # 處理路徑 / 的對應函式
10: @app.route("/")
11: 
12: def main():
13:     return render_template("index.html")
14: 
15: @app.route("/page")
16: 
17: def page():
18:     #從main.html頁面中讀取姓名,存在name中
19:     name = request.args.get("nameis")
20:     # 將name的資料轉給page.html中的變數namepage
21:     return render_template("page.html", namepage=name)
22: # 啟動Server
23: 
24: if __name__ == '__main__':
25:     app.debug = True
26:     app.run()

3.2.2. templates

  1. 建立public資料夾,放入一個head.png圖檔
  2. 建立templates資料夾
  3. 以下兩個html檔要放在templates資料夾中
3.2.2.1. index.html
 1: <!DOCTYPE html>
 2: <html lang="en">
 3: 
 4: <head>
 5:     <meta charset="UTF-8">
 6:     <meta http-equiv="X-UA-Compatible" content="IE=edge">
 7:     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 8:     <title>這是標題</title>
 9: </head>
10: 
11: <body>
12:     <h3>網頁的主畫面</h3>
13:     <form action="/page">
14:         名字:<input type="text" name="nameis">
15:         <button>點擊送出</button>
16:     </form>
17: </body>
18: 
19: </html>
3.2.2.2. page.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>page1</title>

</head>

<body>
    <h1>我的名字叫 {{namepage}}</h1>
</body>

</html>

3.3. [課堂練習]Flask 自我介紹網站   TNFSH

參考上面的 Input form 範例,製作一個簡易的自我介紹網站:

  1. 首頁(index.html)包含一個表單,讓使用者輸入:
    • 姓名(文字輸入)
    • 班級(文字輸入)
    • 興趣(文字輸入)
  2. 點擊送出後,跳到結果頁(result.html),顯示使用者輸入的資料
  3. 結果頁使用 HTML 標題和段落排版

提示:

  • 使用 request.args.get() 取得表單資料
  • render_template() 中傳入多個變數
  • HTML 表單的 action 屬性指向 Flask 的路由

4. tkinter

4.1. 安裝

4.1.1. MacOS

於PyCharm的terminal下輸入brew install python-tk@Python版本
如果系統的預設python版本為3.9,則輸入

1: brew install python-tk@3.9
2: pip install tk

4.2. 範例 #1

tkinter 的 tk.Button 用來建立按鈕,最重要的是 command 參數:

tk.Button(parent, text’…’, command=callback)=

參數 說明
parent 父元件(通常是 root 主視窗)
text 按鈕上顯示的文字
command 按下按鈕時要執行的函式(不加括號!)

command 只能綁定「沒有參數的函式」,如果需要傳參數,用 lambda 包一層:

command=lambda: calculate('+')
 1: # -*- coding: utf-8 -*-
 2: import tkinter as tk
 3: from tkinter import messagebox
 4: # 按完button要做什麼
 5: def test():
 6:     tk.messagebox.showinfo('測試', 'Hi')
 7: 
 8: # main window
 9: root = tk.Tk()
10: root.title('my window')
11: root.geometry('200x150')
12: 
13: root.configure(background='white')
14: 
15: myentry = tk.Entry(root)
16: myentry.pack()
17: 
18: # Button
19: myButton = tk.Button(root, text='Button', command=test)
20: myButton.pack()
21: 
22: # Label
23: resultLabel = tk.Label(root, text='這是Label')
24: resultLabel.pack()
25: 
26: root.mainloop()

2024-03-12_10-24-30_2024-03-12_10-24-05.png

Figure 13: 執行畫面

4.3. 範例 #2

 1: # -*- coding: utf-8 -*-
 2: import tkinter as tk
 3: from tkinter import messagebox
 4: 
 5: def button_event():
 6:     #print(var.get())
 7:     if var.get() == '':
 8:         tk.messagebox.showerror('message', '未輸入答案')
 9:     elif var.get() == '2':
10:         tk.messagebox.showinfo('message', '答對了!')
11:     else:
12:         tk.messagebox.showerror('message', '答錯')
13: 
14: root = tk.Tk()
15: root.title('my window')
16: 
17: # label
18: mylabel = tk.Label(root, text='1+1=')
19: mylabel.grid(row=0, column=0)
20: 
21: var = tk.StringVar()
22: myentry = tk.Entry(root, textvariable=var)
23: myentry.grid(row=0, column=1)
24: 
25: mybutton = tk.Button(root, text='完成', command=button_event)
26: mybutton.grid(row=1, column=1)
27: 
28: root.mainloop()

2024-03-12_10-27-00_2024-03-12_10-25-57.gif

Figure 14: 執行畫面

4.4. [課堂練習]簡易計算機   TNFSH

參考範例 #1 和 #2,用 tkinter 製作一個簡易計算機,需包含:

  1. 兩個輸入欄位(Entry)讓使用者輸入數字
  2. 四個按鈕(+、-、×、÷)
  3. 一個 Label 顯示計算結果
  4. 使用 grid 排版

提示:

  • tk.Entry 取得輸入值,透過 .get() 讀取並用 float() 轉型
  • tk.Button(root, text’+’, command=函式名)= 建立按鈕
  • resultLabel.config(text=結果) 更新 Label 文字

完成畫面參考:

  • 第一列:「數字1」Label + Entry
  • 第二列:「數字2」Label + Entry
  • 第三列:四個運算按鈕(+、-、×、÷)
  • 第四列:「結果」Label

5. PyInstaller

5.1. 安裝pyinstaller

1: pip3 install pyinstaller

5.2. 準備要打包的檔案

5.2.1. 找一個喜歡的icon

此處我下載了一個icns,命名為imac.icns,與底下的403qq.py存於同一資料夾裡,如果你懶到不想自己找,也可以按這裡下載我的icns圖示檔

5.2.2. 以前述tkinter demo#2為例,將下列程式命名為403qq.py

 1: import tkinter as tk
 2: from tkinter import messagebox
 3: 
 4: def button_event():
 5:     #print(var.get())
 6:     if var.get() == '':
 7:         tk.messagebox.showerror('message', '未輸入答案')
 8:     elif var.get() == '2':
 9:         tk.messagebox.showinfo('message', '答對了!')
10:     else:
11:         tk.messagebox.showerror('message', '答錯')
12: 
13: root = tk.Tk()
14: root.title('my window')
15: 
16: # label
17: mylabel = tk.Label(root, text='1+1=')
18: mylabel.grid(row=0, column=0)
19: 
20: var = tk.StringVar()
21: myentry = tk.Entry(root, textvariable=var)
22: myentry.grid(row=0, column=1)
23: 
24: mybutton = tk.Button(root, text='完成', command=button_event)
25: mybutton.grid(row=1, column=1)
26: 
27: root.mainloop()

5.3. 打包

1: pyinstaller --icon imac.icns --noconsole -n 403QQ 403qq.py

成功執行後,會在同一資料夾中看到一個dist的資料夾,裡面有一支403QQ的應用程式,試著執行看看

2024-03-12_12-56-19_2024-03-12_12-55-49.png

Figure 15: 編譯後的應用程式: 403QQ

6. Flet

7. TODO Kivy

8. PyQt

1: pip3 install PyQt6
 1: import sys
 2: from PyQt6.QtWidgets import QApplication, QWidget
 3: from PyQt6.QtGui import QIcon
 4: 
 5: class Window(QWidget):
 6:     def __init__(self):
 7:         super().__init__()
 8:         self.setWindowTitle("PyQT")
 9:         self.setFixedHeight(200)
10:         self.setFixedWidth(200)
11:         self.setGeometry(500, 300, 400, 300)
12:         stylesheet = self.setStyleSheet('background-color:Gray')
13: 
14: app = QApplication([])
15: window = Window()
16: 
17: window.show()
18: sys.exit(app.exec())

9. Resources

Footnotes:

Author: Yung-Chin Yen

Created: 2026-02-13 Fri 15:33