作者:月小水長
前言
TextCtrl 是 wxPython 框架里一個非常實用的文本輸入控件,我們經常需要對 TextCtrl 做這樣一個輸入上的約束:只允許輸入數字(比如允許 1.2、4.5、100 這些輸入而禁止諸如 .8、4t等輸入,方便我們在將輸入的 str 類型轉成 int、float 等其他類型時不需要額外加判斷來防止產生異常。今天我查閱 wxPython 的 API,發現了 Validator 這個類可以滿足我們的需求。事實上,不僅是滿足只允許輸入數字這個需要,掌握了 Validator 這個類的使用,我們可以滿足任何定制化的需求。
編碼
首先必須新建類繼承自 wx.Validator
import wx class MyNumberValidator(wx.Validator):# 創建驗證器子類 def __init__(self): wx.Validator.__init__(self) def Clone(self): return MyNumberValidator() def Validate(self,win):#1 使用驗證器方法 return True def TransferToWindow(self): return True def TransferFromWindow(self): return True
同時,必須重寫 init()(完成初始化)、Clone()(返回當前類的一個實例)等五個方法,你只要把它們當成一個模板就行,然后在此基礎上加上我們自己的邏輯。
根據需求加入實現邏輯
import wx # import wx.lib.imagebrowser class MyNumberValidator(wx.Validator):# 創建驗證器子類 def __init__(self): wx.Validator.__init__(self) self.ValidInput = ['.','0','1','2','3','4','5','6','7','8','9'] self.StringLength = 0 self.Bind(wx.EVT_CHAR,self.OnCharChanged) # 綁定字符改變事件 def OnCharChanged(self,event): # 得到輸入字符的 ASCII 碼 keycode = event.GetKeyCode() # 退格(ASCII 碼 為8),刪除一個字符。 if keycode == 8: self.StringLength -= 1 #事件繼續傳遞 event.Skip() return # 把 ASII 碼 轉成字符 InputChar = chr(keycode) if InputChar in self.ValidInput: # 第一個字符為 .,非法,攔截該事件,不會成功輸入 if InputChar == '.' and self.StringLength == 0: return False # 在允許輸入的范圍,繼續傳遞該事件。 else: event.Skip() self.StringLength += 1 return True return False def Clone(self): return MyNumberValidator() def Validate(self,win):#1 使用驗證器方法 return True def TransferToWindow(self): return True def TransferFromWindow(self): return True
注意這行代碼,self.Bind(wx.EVT_CHAR,self.OnCharChanged) # 綁定字符輸入事件,它的意思是,一旦用戶輸入了字符,該事件會首先交給OnCharChanged方法處理,在OnCharChanged中,特別注意event.Skip()這行代碼,它的意思是,過了合法性檢驗這一關,該事件可以繼續傳遞了,可以交給將該字符加入 textctrl.value 並顯示出來的函數處理;否則,事件就在OnCharChanged中被攔截了,不會顯示出來,輸入失敗。
給控件 textctrl 綁定驗證器子類。
很簡單,只需要在創建 textctrl 的時候加指定關鍵詞參數
wx.TextCtrl(self,validator=MyNumberValidator(),style=wx.TE_CENTER)
style=wx.TE_CENTER
使輸入字符居中
再說 Validate() 方法
我們發現,Validator
類中和類名最相似的方法Validate()
一直沒有使用,其實這個方法是一般使用流程如下,先在 Validate()
方法中加上業務邏輯。
def Validate(self,win):#1 使用驗證器方法 print(111) textCtrl = self.GetWindow() text = textCtrl.GetValue() valid_text = '' for i in text: if i in self.ValidInput: valid_text += i textCtrl.SetValue(valid_text) return True
然后在外部調用它驗證合法性
self.n1.GetValidator().Validate(self.n1)
Validate()也可以驗證合法性,只不過它不能處理輸入事件,必須在輸入完成后才能驗證合法性,相當於“馬后炮”的作用。
完整例子:
# -*- coding: utf-8 -*- # author: inspurer(月小水長) # pc_type lenovo # create_time: 2019/4/12 13:37 # file_name: validator.py # github https://github.com/inspurer # qq郵箱 2391527690@qq.com # 微信公眾號 月小水長(ID: inspurer) import wx # import wx.lib.imagebrowser class MyNumberValidator(wx.Validator):# 創建驗證器子類 def __init__(self): wx.Validator.__init__(self) self.ValidInput = ['.','0','1','2','3','4','5','6','7','8','9'] self.StringLength = 0 self.Bind(wx.EVT_CHAR,self.OnCharChanged) # 綁定字符輸入事件 def OnCharChanged(self, event): # 得到輸入字符的 ASCII 碼 keycode = event.GetKeyCode() # 退格(ASCII 碼 為8),刪除一個字符。 if keycode == 8: self.StringLength -= 1 # 事件繼續傳遞 event.Skip() return # 把 ASII 碼 轉成字符 InputChar = chr(keycode) if InputChar in self.ValidInput: # 第一個字符為 .,非法,攔截該事件,不會成功輸入 if InputChar == '.' and self.StringLength == 0: return False # 在允許輸入的范圍,繼續傳遞該事件。 else: event.Skip() self.StringLength += 1 return True return False def Clone(self): return MyNumberValidator() def Validate(self,win):#1 使用驗證器方法 print(111) textCtrl = self.GetWindow() text = textCtrl.GetValue() valid_text = '' for i in text: if i in self.ValidInput: valid_text += i textCtrl.SetValue(valid_text) return True def TransferToWindow(self): return True def TransferFromWindow(self): return True class GUI(wx.Frame): def __init__(self,parent): wx.Frame.__init__(self, parent=parent, title="wxPython開發實戰",size=(400,300),style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX ) self.Center() self.SetBackgroundColour('white') font = wx.Font(16,wx.DECORATIVE, wx.NORMAL, wx.NORMAL) self.n1 = wx.TextCtrl(self,validator=MyNumberValidator(),pos=(100,30),size=(180,45),style=wx.TE_CENTER) self.n1.SetFont(font) self.n1.SetBackgroundColour('#95ec69') self.n1.SetHint('請輸入第一個數字') self.n2 = wx.TextCtrl(self,validator=MyNumberValidator(),pos=(100,100),size=(180,45),style=wx.TE_CENTER) self.n2.SetFont(font) self.n2.SetBackgroundColour('#95ec69') self.n2.SetHint('請輸入第二個數字') self.config = wx.Button(self,label="驗證輸入框一的合法性",pos=(115,170),size=(150,40)) self.config.SetBackgroundColour('#95ec69') self.Bind(wx.EVT_BUTTON,self.configClicked,self.config) def configClicked(self,event): self.n1.GetValidator().Validate(self.n1) pass if __name__ == "__main__": app = wx.App() GUI(None).Show() app.MainLoop()