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

如何从TextCtrl派生wxPython自定义控件,python,python-3.x,wxpython,Python,Python 3.x,Wxpython,对我来说,wxPythonwx.lib.masked.NumCtrl有一些非常奇怪的特性。因此,我想创建我自己的NumberCtrl,它从wx.TextCtrl派生而来,只跳过非数字输入。旁注:缺点是ctrl.SetValue()只接受字符串 由于调用超类构造函数时参数不匹配(或参数数量不匹配),下面的类失败 class NumberCtrl(wx.TextCtrl): def __init__(self, parent, id=wx.ID_ANY, label=None):

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

由于调用超类构造函数时参数不匹配(或参数数量不匹配),下面的类失败

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

我不明白这里发生了什么。

尝试简单地使用TextCtrl的显式init值

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

编辑以将
value
的默认值更改为stringnot
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()
classnumberctrl(wx.TextCtrl):
def u u init_uuuu(self,parent,id=wx.id_uany,value=”“,pos=wx.DefaultPosition,size=wx.DefaultSize,style=0,validator=wx.DefaultValidator,name=wx.TextCtrlNameStr):
wx.TextCtrl.\uuuuu init\uuuuuu(self,parent=parent,id=id,value=value)
self.Bind(wx.EVT_CHAR,lambda事件:self.force_numeric(事件))
def force_数字(自身、事件):
原始值=self.GetValue().strip()
keycode=event.GetKeyCode()
打印(键码)
如果键代码<255:
如果chr(keycode).isdigit()或keycode==8或chr(keycode)='.'和'.'不在原始值中:
event.Skip()
编辑-工作代码(在Linux wxpython 4.1.1上)

导入wx
类别MyPanel(wx.Panel):
def u u init u;(self,parent,flags=0):
super()。\uuuu init\uuuu(父级)
#面板1(包含占据整个面板的按钮)
self.pnl1=wx.面板(self)
self.btn1=wx.按钮(self.pnl1,label=“Panel2”,大小=(250,75))
self.btn1.Bind(wx.EVT_按钮,self.show_pnl2)
vbox1=wx.BoxSizer(wx.VERTICAL)
vbox1.Add(self.btn1)
self.pnl1.setizer(vbox1)
#面板2(包含占据整个面板的按钮)
self.pnl2=wx.面板(self)
self.btn2=wx.Button(self.pnl2,label=“Panel1”,size=(75250))
self.num=NumberCtrl(self.pnl2,wx.ID_ANY,value=“1”,size=(75250))
self.btn2.Bind(wx.EVT_按钮,self.show_pnl1)
vbox2=wx.BoxSizer(wx.VERTICAL)
vbox2.Add(self.btn2)
vbox2.Add(self.num)
self.pnl2.setizer(vbox2)
#主面板(显示面板1或面板2)
vbox=wx.BoxSizer(wx.VERTICAL)
vbox.Add(self.pnl1,flag=flags)
vbox.Add(self.pnl2,flag=flags)
自我设置器(vbox)
#从显示面板1和隐藏面板2开始
self.Show()
self.pnl1.Hide()
def显示pnl1(自身、事件):
self.pnl2.Hide()
self.pnl1.Show()
self.Layout()
self.Fit()
def show_pnl2(自身、事件):
self.pnl1.Hide()
self.pnl2.Show()
self.Layout()
self.Fit()
类别编号Ctrl(wx.TextCtrl):
def u u init_uuuu(self,parent,id=wx.id_uany,value=”“,pos=wx.DefaultPosition,size=wx.DefaultSize,style=0,validator=wx.DefaultValidator,name=wx.TextCtrlNameStr):
wx.TextCtrl.\uuuuu init\uuuuuu(self,parent=parent,id=id,value=value)
self.Bind(wx.EVT_CHAR,lambda事件:self.force_numeric(事件))
def force_数字(自身、事件):
原始值=self.GetValue().strip()
keycode=event.GetKeyCode()
打印(键码)
如果键代码<255:
如果chr(keycode).isdigit()或keycode==8或chr(keycode)='.'和'.'不在原始值中:
event.Skip()
类PanelSwitchExample(wx.Frame):
“”“表示包含可切换面板的应用程序窗口”“”
定义初始化(self,标志=0):
super()。\uuuu init\uuuu(无)
vbox=wx.BoxSizer(wx.VERTICAL)
添加(MyPanel(self,flags))
自我设置器(vbox)
self.Show()
如果名称=“\uuuuu main\uuuuuuuu”:
app=wx.app()
PanelSwitchExample()
app.MainLoop()

这是我尝试过的众多变体之一。由于
TextCtrl():参数与任何重载调用不匹配:\n重载1:“parent”不是有效的关键字参数\n重载2:参数“value”具有意外的类型“NoneType”
。此外,为什么我的控件的构造函数要接受它没有传递给超类构造函数的参数(
pos
size
,等等)?@MarcelStör我能告诉你什么,它对我有效。我已将我的测试代码添加到答案中。它对您有效,因为您的
NumberCtrl(self.pnl2…**value=“1”**…
。wxPython
TextCtrl
只是不接受
None
作为值。错误消息实际上非常清楚地暗示了这一点-我只是没有得到它。谢谢你的帮助。@MarcelStör我很高兴能为你服务,即使是因为无知。我编辑了答案,使
值成为默认值tring,这样其他人就不会犯同样的错误。
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()
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()