wxPython 是成熟的跨平台 GUI 工具包,適合快速開發桌面應用。本文將一步步帶領讀者建立一個具備「開啟檔案、編輯、儲存」核心功能的記事本,並說明關鍵元件(Frame、Panel、TextCtrl、FileDialog、BoxSizer)的協作方式。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import wx
class NotepadFrame(wx.Frame):
def __init__(self):
super().__init__(None, title='Simple Notepad', size=(700, 500))
self.current_file = None
self._build_ui()
self.Centre()
self.Show()
def _build_ui(self):
panel = wx.Panel(self)
# 工具列區域
self.filename_ctrl = wx.TextCtrl(panel, style=wx.TE_READONLY)
open_btn = wx.Button(panel, label='開啟')
save_btn = wx.Button(panel, label='儲存')
save_as_btn = wx.Button(panel, label='另存新檔')
open_btn.Bind(wx.EVT_BUTTON, self.on_open)
save_btn.Bind(wx.EVT_BUTTON, self.on_save)
save_as_btn.Bind(wx.EVT_BUTTON, lambda e: self.on_save(as_new=True))
# 編輯區
self.text_ctrl = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.TE_RICH2)
# 版面配置
tool_sizer = wx.BoxSizer(wx.HORIZONTAL)
tool_sizer.Add(wx.StaticText(panel, label='檔案:'), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
tool_sizer.Add(self.filename_ctrl, 1, wx.EXPAND | wx.RIGHT, 5)
tool_sizer.Add(open_btn, 0, wx.RIGHT, 5)
tool_sizer.Add(save_btn, 0, wx.RIGHT, 5)
tool_sizer.Add(save_as_btn, 0)
main_sizer = wx.BoxSizer(wx.VERTICAL)
main_sizer.Add(tool_sizer, 0, wx.EXPAND | wx.ALL, 8)
main_sizer.Add(self.text_ctrl, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 8)
panel.SetSizer(main_sizer)
# 選單列(加分項)
self._build_menubar()
def _build_menubar(self):
menubar = wx.MenuBar()
file_menu = wx.Menu()
file_menu.Append(wx.ID_OPEN, '開啟\tCtrl+O')
file_menu.Append(wx.ID_SAVE, '儲存\tCtrl+S')
file_menu.Append(wx.ID_SAVEAS, '另存新檔\tCtrl+Shift+S')
file_menu.AppendSeparator()
file_menu.Append(wx.ID_EXIT, '離開\tCtrl+Q')
menubar.Append(file_menu, '檔案(&F)')
self.SetMenuBar(menubar)
self.Bind(wx.EVT_MENU, self.on_open, id=wx.ID_OPEN)
self.Bind(wx.EVT_MENU, self.on_save, id=wx.ID_SAVE)
self.Bind(wx.EVT_MENU, lambda e: self.on_save(as_new=True), id=wx.ID_SAVEAS)
self.Bind(wx.EVT_MENU, lambda e: self.Close(), id=wx.ID_EXIT)
# --- 事件處理 ---
def on_open(self, event):
with wx.FileDialog(self, '開啟文字檔', wildcard='文字檔 (*.txt)|*.txt|所有檔案 (*.*)|*.*',
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as dlg:
if dlg.ShowModal() == wx.ID_CANCEL:
return
path = dlg.GetPath()
try:
with open(path, 'r', encoding='utf-8') as f:
self.text_ctrl.SetValue(f.read())
self.current_file = path
self.filename_ctrl.SetValue(path)
self.SetTitle(f'Simple Notepad - {path}')
except Exception as e:
wx.MessageBox(f'讀取失敗:{e}', '錯誤', wx.OK | wx.ICON_ERROR)
def on_save(self, event, as_new=False):
if as_new or self.current_file is None:
with wx.FileDialog(self, '儲存文字檔', wildcard='文字檔 (*.txt)|*.txt',
style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as dlg:
if dlg.ShowModal() == wx.ID_CANCEL:
return
self.current_file = dlg.GetPath()
try:
with open(self.current_file, 'w', encoding='utf-8') as f:
f.write(self.text_ctrl.GetValue())
self.filename_ctrl.SetValue(self.current_file)
self.SetTitle(f'Simple Notepad - {self.current_file}')
wx.MessageBox('儲存成功', '提示', wx.OK | wx.ICON_INFORMATION)
except Exception as e:
wx.MessageBox(f'儲存失敗:{e}', '錯誤', wx.OK | wx.ICON_ERROR)
if __name__ == '__main__':
app = wx.App()
NotepadFrame()
app.MainLoop()
| 元件 | 用途 | 關鍵參數/方法 |
|---|---|---|
| wx.Frame | 主視窗容器 | title, size, Centre(), Show() |
| wx.Panel | 承載子元件、支援 Tab 鍵導航 | SetSizer() |
| wx.TextCtrl | 文字編輯區 | style=TE_MULTILINE|TE_RICH2, SetValue/GetValue() |
| wx.FileDialog | 開啟/儲存檔案對話框 | wildcard, style=FD_OPEN/FD_SAVE, GetPath() |
| wx.BoxSizer | 盒狀版面管理(水平/垂直) | Add(widget, proportion, flag, border) |
| wx.MenuBar | 選單列 | Append(menu, title), SetMenuBar() |
proportion:剩餘空間分配比例。0 表示固定大小,>0 按比例分配。flag:對齊與邊框。常用組合:
wx.EXPAND:填滿分配空間wx.ALL | wx.EXPAND:四周留白並填滿wx.ALIGN_CENTER_VERTICAL:垂直置中border:邊距像素。chardet 或 charset_normalizer。wx.stc.StyledTextCtrl(Scintilla 封裝)。wx.Notebook 或 wx.AuiNotebook。TextCtrl 內建支援 Undo()/Redo()。PyInstaller 產生單一執行檔(--noconsole --icon=app.ico)。wxPython 4.1+ 並使用 pythonw 執行,否則可能無法顯示視窗。wx.CallAfter 更新介面。os.replace,防止斷電導致檔案損壞。這個範例雖然簡單,但涵蓋了 wxPython 開發的核心流程:建立視窗 → 佈局元件 → 綁定事件 → 處理邏輯。以此為基礎,即可逐步擴充為功能完整的桌面應用程式。