如何从TextCtrl派生wxPython自定义控件

How to derive wxPython custom control from TextCtrl

对我来说,wxPython wx.lib.masked.NumCtrl 有一些非常奇怪的怪癖。因此,我想创建我自己的 NumberCtrl 派生自 wx.TextCtrl,它只是跳过非数字输入。旁注:缺点是 ctrl.SetValue() 只接受字符串。

下面的 class 由于在调用超级 class 构造函数时参数不匹配(或参数数量)而失败。

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()

但是,仅使用最基本的参数就可以了。

def __init__(self, parent):
    wx.TextCtrl.__init__(self, parent=parent)

我不明白这是怎么回事。

尝试简单地使用 TextCtrl 的显式初始值。

我已将 label 替换为 value 并在事件验证中添加了允许“退格键”(我的设置中的键码 8)。

编辑以将 value 的默认值更改为字符串 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()