I'm trying to create a custom SpinCtrl with a step increment. It seems li开发者_StackOverflowke such a simple thing so I was surprised the native SpinCtrl doesn't appear to have this functionality, and Google proves uncommonly useless in this matter as well.
When I try to make a custom one, however, I run into problems. Here's some quick and dirty code
class SpinStepCtrl( wx.SpinCtrl ):
def __init__( self, *args, **kwargs ):
super( SpinStepCtrl, self ).__init__( *args, **kwargs )
self.step = 99
self.Bind( wx.EVT_SPINCTRL, self.OnSpin )
#self.Bind( wx.EVT_SPIN_UP, self.OnUp )
#self.Bind( wx.EVT_SPIN_DOWN, self.OnDown )
def OnSpin( self, event ):
print 'X'
self.SetValue( self.GetValue() + self.step )
The print is just there so I can see what, if anything, happens. The EVT_SPIN_UP and EVT_SPIN_DOWN events don't seem to work at all, at least the callbacks are never called which is why I took them out.
When using EVT_SPINCTRL, the callback is called, but end up in an infinite loop because SetValue apparently causes a new such event to be called. It doesn't help much either way, because I can't find a way of telling whether it was a spin up or spin down event, so I can't change the value appropriately.
How do I get this to work?
wx.lib.agw has floatspin widget which has more options. I use it with SetDigits(0) for integer input as well.
import wx
import wx.lib.agw.floatspin as FS
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "FloatSpin Demo")
panel = wx.Panel(self)
floatspin = FS.FloatSpin(panel, -1, pos=(50, 50), min_val=0, max_val=1000,
increment=99, value=0, agwStyle=FS.FS_LEFT)
floatspin.SetDigits(0)
# our normal wxApp-derived class, as usual
app = wx.App(0)
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
OK, not the best, but works on Ubuntu:
#!/usr/bin/python
import wx
class SpinStepCtrl(wx.SpinCtrl):
def __init__(self, *args, **kwargs):
wx.SpinCtrl.__init__(self, *args, **kwargs)
self.step = 99
self.last_value = 0
self.Bind(wx.EVT_SPINCTRL, self.OnSpin)
def OnSpin(self, event):
delta = self.GetValue() - self.last_value
if delta == 0:
return
elif delta > 0:
self.last_value = self.GetValue() + self.step
else:
self.last_value = self.GetValue() - self.step
self.SetValue(self.last_value)
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.spin = SpinStepCtrl(self.panel, min=0, max=1000)
self.Show()
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
Still, I would consider "self.SetValue" generating "wx.EVT_SPINCTRL" a bug.
This works for me:
import wx
class SpinStepCtrl(wx.SpinCtrl):
def __init__(self, *args, **kwargs):
wx.SpinCtrl.__init__(self, *args, **kwargs)
self.step = 99
self.Bind(wx.EVT_SPIN_UP, self.OnUp)
self.Bind(wx.EVT_SPIN_DOWN, self.OnDown)
def OnUp(self, event):
self.SetValue(self.GetValue() + self.step)
def OnDown(self, event):
self.SetValue(self.GetValue() - self.step)
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.spin = SpinStepCtrl(self.panel, min=0, max=1000)
self.Show()
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
Here is my solution for wxSpinCtrl, which takes values from a list to spin. It is a pity that the events EVT_SPIN_UP and EVT_SPIN_DOWN from wx.SpinButton do not work under all platforms (not working here with wx 4.0.6 and Windows 10 / 64 Bit)
import wx
class SpinCtrlList(wx.SpinCtrl):
"""A SpinCtrl that spins through a given list. This list must be in
ascending order and the values of the list must be integers,
whereat negative numbers are allowed
"""
def __init__(self, *args, **kwargs):
wx.SpinCtrl.__init__(self, *args, **kwargs)
self.Bind(wx.EVT_SPINCTRL, self._on_spin)
self.thelist = ([0,1])
self.SetList([0, 1])
self.SetValue(self.thelist[0])
def SetList(self, thelist):
self.thelist = thelist
self.SetRange(self.thelist[0], self.thelist[len(self.thelist)-1])
def GetList(self):
return self.thelist
def SetValue(self, val):
self.sp_old_value = val
super(SpinCtrlList, self).SetValue(val)
def _next_greater_ele(self, in_list, in_val):
for x, val in enumerate(in_list):
if val > in_val:
break
return val
def _next_smaller_ele(self, in_list, in_val):
for x, val in enumerate(reversed(in_list)):
if val < in_val:
break
return val
def _on_spin(self, event):
self.sp_new_value = self.GetValue()
#print(self.sp_old_value, self.sp_new_value)
if self.sp_new_value > self.sp_old_value:
set_val = self._next_greater_ele(self.thelist, self.sp_new_value)
self.SetValue(set_val)
self.sp_old_value = set_val
elif self.sp_new_value < self.sp_old_value:
set_val = self._next_smaller_ele(self.thelist, self.sp_new_value)
self.SetValue(set_val)
self.sp_old_value = set_val
class MainWindow(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.panel = wx.Panel(self)
self.spin = SpinCtrlList(self.panel)
ctrl_list =[100, 200, 500, 1000, 2000, 5000, 10000, 12000, 15000]
#ctrl_list = [-300, -100, 0]
self.spin.SetList(ctrl_list)
self.spin.SetValue(ctrl_list[-2])
self.Show()
def main():
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
if __name__ == '__main__':
main()
精彩评论