For me the wxPython wx.lib.masked.NumCtrl
has some really odd quirks. Hence, I wanted to create my own NumberCtrl
derived from wx.TextCtrl
that simply skips over non-numeric input. Side note: the downside is that ctrl.SetValue()
only accepts strings.
The below class fails due to non-matching arguments (or number of arguments) in the call to the super class constructor.
class NumberCtrl(wx.TextCtrl):
def __init__(self, parent, id=wx.ID_ANY, label=None):
wx.TextCtrl.__init__(self, parent=parent, id=id, label=label)
self.Bind(wx.EVT_CHAR, lambda event: self.force_numeric(event))
def force_numeric(self, event):
raw_value = self.GetValue().strip()
keycode = event.GetKeyCode()
if keycode < 255:
if chr(keycode).isdigit() or chr(keycode) == '.' and '.' not in raw_value:
event.Skip()
However, using just the bare basic arguments works.
def __init__(self, parent):
wx.TextCtrl.__init__(self, parent=parent)
I don't understand what is going on here.
Try simply using the explicit init values for a TextCtrl.
I've replaced label
with value
and added allowing for `backspace´ (keycode 8 on my setup) in the events validation.
Edited to change the default value of value
to string not None
class NumberCtrl(wx.TextCtrl):
def __init__(self, parent, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, validator=wx.DefaultValidator, name=wx.TextCtrlNameStr):
wx.TextCtrl.__init__(self, parent=parent, id=id, value=value)
self.Bind(wx.EVT_CHAR, lambda event: self.force_numeric(event))
def force_numeric(self, event):
raw_value = self.GetValue().strip()
keycode = event.GetKeyCode()
print(keycode)
if keycode < 255:
if chr(keycode).isdigit() or keycode == 8 or chr(keycode) == '.' and '.' not in raw_value:
event.Skip()
Edit - Working code (on Linux wxpython 4.1.1)
import wx
class MyPanel(wx.Panel):
def __init__(self, parent, flags=0):
super().__init__(parent)
# Panel 1 (contains button that takes up entire panel)
self.pnl1 = wx.Panel(self)
self.btn1 = wx.Button(self.pnl1, label="Panel2", size=(250,75))
self.btn1.Bind(wx.EVT_BUTTON, self.show_pnl2)
vbox1 = wx.BoxSizer(wx.VERTICAL)
vbox1.Add(self.btn1)
self.pnl1.SetSizer(vbox1)
# Panel 2 (contains button that takes up entire panel)
self.pnl2 = wx.Panel(self)
self.btn2 = wx.Button(self.pnl2, label="Panel1", size=(75,250))
self.num = NumberCtrl(self.pnl2, wx.ID_ANY, value="1", size=(75,250))
self.btn2.Bind(wx.EVT_BUTTON, self.show_pnl1)
vbox2 = wx.BoxSizer(wx.VERTICAL)
vbox2.Add(self.btn2)
vbox2.Add(self.num)
self.pnl2.SetSizer(vbox2)
# Main panel (displays either Panel 1 or Panel 2)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.pnl1, flag=flags)
vbox.Add(self.pnl2, flag=flags)
self.SetSizer(vbox)
# Start off with panel 1 showing and panel 2 hidden
self.Show()
self.pnl1.Hide()
def show_pnl1(self, event):
self.pnl2.Hide()
self.pnl1.Show()
self.Layout()
self.Fit()
def show_pnl2(self, event):
self.pnl1.Hide()
self.pnl2.Show()
self.Layout()
self.Fit()
class NumberCtrl(wx.TextCtrl):
def __init__(self, parent, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, validator=wx.DefaultValidator, name=wx.TextCtrlNameStr):
wx.TextCtrl.__init__(self, parent=parent, id=id, value=value)
self.Bind(wx.EVT_CHAR, lambda event: self.force_numeric(event))
def force_numeric(self, event):
raw_value = self.GetValue().strip()
keycode = event.GetKeyCode()
print(keycode)
if keycode < 255:
if chr(keycode).isdigit() or keycode == 8 or chr(keycode) == '.' and '.' not in raw_value:
event.Skip()
class PanelSwitchExample(wx.Frame):
"""Represents application window that includes switchable panel"""
def __init__(self, flags=0):
super().__init__(None)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(MyPanel(self, flags))
self.SetSizer(vbox)
self.Show()
if __name__ == "__main__":
app = wx.App()
PanelSwitchExample()
app.MainLoop()