Use a better FloatCtrl in patternEditor

This commit is contained in:
HomeWorld
2010-11-28 12:09:36 +02:00
parent c06e164fb1
commit ae34992acf
2 changed files with 231 additions and 17 deletions

View File

@@ -19,10 +19,9 @@
import wx
import bitmapLoader
from wx.lib.intctrl import IntCtrl
from wx.lib.masked.numctrl import NumCtrl
import service
from util import toClipboard, fromClipboard
from util import toClipboard, fromClipboard, FloatCtrl
###########################################################################
## Class DmgPatternEditorDlg
###########################################################################
@@ -104,26 +103,26 @@ class DmgPatternEditorDlg (wx.Dialog):
self.bmpEM = wx.StaticBitmap(self, wx.ID_ANY, self.embitmap)
dmgeditSizer.Add(self.bmpEM, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 5)
self.editEm = NumCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize)
self.editEm.SetFractionWidth(2)
self.editEm = FloatCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize)
self.editEm.SetPrecision(2)
dmgeditSizer.Add(self.editEm, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5)
self.bmpTHERM = wx.StaticBitmap(self, wx.ID_ANY, self.thermbitmap)
dmgeditSizer.Add(self.bmpTHERM, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT, 25)
self.editThermal = NumCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize, 0)
self.editThermal.SetFractionWidth(2)
self.editThermal = FloatCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize, 0)
self.editThermal.SetPrecision(2)
dmgeditSizer.Add(self.editThermal, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5)
self.bmpKIN = wx.StaticBitmap(self, wx.ID_ANY, self.kinbitmap)
dmgeditSizer.Add(self.bmpKIN, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT, 5)
self.editKinetic = NumCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize)
self.editKinetic.SetFractionWidth(2)
self.editKinetic = FloatCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize)
self.editKinetic.SetPrecision(2)
dmgeditSizer.Add(self.editKinetic, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5)
self.bmpEXP = wx.StaticBitmap(self, wx.ID_ANY, self.expbitmap)
dmgeditSizer.Add(self.bmpEXP, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT, 25)
self.editExplosive = NumCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize, 0)
self.editExplosive.SetFractionWidth(2)
self.editExplosive = FloatCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize, 0)
self.editExplosive.SetPrecision(2)
dmgeditSizer.Add(self.editExplosive, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5)
contentSizer.Add(dmgeditSizer, 1, wx.EXPAND | wx.ALL, 5)
@@ -166,12 +165,6 @@ class DmgPatternEditorDlg (wx.Dialog):
bsize = self.GetBestSize()
self.SetSize((-1,bsize.height))
self.editEm.SetLimited(True)
self.editThermal.SetLimited(True)
self.editKinetic.SetLimited(True)
self.editExplosive.SetLimited(True)
self.editEm.SetMin(0)
self.editThermal.SetMin(0)
self.editKinetic.SetMin(0)

221
util.py
View File

@@ -1,5 +1,6 @@
import math
import wx
import fpformat
def formatAmount(val, prec=3, lowest=0, highest=0):
"""
@@ -91,3 +92,223 @@ def fromClipboard():
else:
clip.Close()
return None
def set_float(val,default=None):
""" utility to set a floating value, useful for converting from strings """
if val in (None,''): return default
try:
return float(val)
except:
return default
class closure:
"""A very simple callback class to emulate a closure (reference to
a function with arguments) in python.
This class holds a user-defined function to be executed when the
class is invoked as a function. This is useful in many situations,
especially for 'callbacks' where lambda's are quite enough.
Many Tkinter 'actions' can use such callbacks.
>>>def my_action(x=None):
... print 'my action: x = ', x
>>>c = closure(my_action,x=1)
..... sometime later ...
>>>c()
my action: x = 1
>>>c(x=2)
my action: x = 2
based on Command class from J. Grayson's Tkinter book.
"""
def __init__(self,func=None,*args, **kw):
self.func = func
self.kw = kw
self.args = args
def __call__(self, *args, **kw):
self.kw.update(kw)
if (self.func == None): return None
self.args = args
return apply(self.func,self.args,self.kw)
class FloatCtrl(wx.TextCtrl):
""" Numerical Float Control::
a wx.TextCtrl that allows only numerical input, can take a precision argument
and optional upper / lower bounds
"""
def __init__(self, parent, value='', min='', max='',
action=None, precision=3, action_kw={}, **kwargs):
self.__digits = '0123456789.-'
self.__prec = precision
if precision is None: self.__prec = 0
self.format = '%%.%if' % self.__prec
self.__val = set_float(value)
self.__max = set_float(max)
self.__min = set_float(min)
self.fgcol_valid = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)
self.bgcol_valid = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)
self.fgcol_invalid ="Red"
self.bgcol_invalid =(254,254,80)
# set up action
self.__action = closure()
if callable(action): self.__action.func = action
if len(action_kw.keys())>0: self.__action.kw = action_kw
this_sty = wx.TE_PROCESS_ENTER
kw = kwargs
if kw.has_key('style'): this_sty = this_sty | kw['style']
kw['style'] = this_sty
wx.TextCtrl.__init__(self, parent, wx.ID_ANY, **kw)
self.__CheckValid(self.__val)
self.SetValue(self.__val)
self.Bind(wx.EVT_CHAR, self.onChar)
# self.Bind(wx.EVT_CHAR, self.CharEvent)
self.Bind(wx.EVT_TEXT, self.onText)
self.Bind(wx.EVT_SET_FOCUS, self.onSetFocus)
self.Bind(wx.EVT_KILL_FOCUS, self.onKillFocus)
self.Bind(wx.EVT_SIZE, self.onResize)
self.__GetMark()
def SetAction(self,action,action_kw={}):
self.__action = closure()
if callable(action): self.__action.func = action
if len(action_kw.keys())>0: self.__action.kw = action_kw
def SetPrecision(self,p):
if p is None: p = 0
self.__prec = p
self.format = '%%.%if' % p
def __GetMark(self):
" keep track of cursor position within text"
try:
self.__mark = min(wx.TextCtrl.GetSelection(self)[0],
len(wx.TextCtrl.GetValue(self).strip()))
except:
self.__mark = 0
def __SetMark(self,m=None):
" "
if m==None: m = self.__mark
self.SetSelection(m,m)
def SetValue(self,value=None,act=True):
" main method to set value "
# print 'Set Value '
if value == None: value = wx.TextCtrl.GetValue(self).strip()
self.__CheckValid(value)
self.__GetMark()
if self.__valid:
self.__Text_SetValue(self.__val)
self.SetForegroundColour(self.fgcol_valid)
self.SetBackgroundColour(self.bgcol_valid)
if callable(self.__action) and act: self.__action(value=self.__val)
else:
self.__val = self.__bound_val
self.__Text_SetValue(self.__val)
self.__CheckValid(self.__val)
self.SetForegroundColour(self.fgcol_invalid)
self.SetBackgroundColour(self.bgcol_invalid)
self.__SetMark()
def onKillFocus(self, event):
self.__GetMark()
event.Skip()
def onResize(self, event):
event.Skip()
def onSetFocus(self, event=None):
self.__SetMark()
if event: event.Skip()
def onChar(self, event):
""" on Character event"""
key = event.GetKeyCode()
entry = wx.TextCtrl.GetValue(self).strip()
pos = wx.TextCtrl.GetSelection(self)
# really, the order here is important:
# 1. return sends to ValidateEntry
if (key == wx.WXK_RETURN):
self.SetValue(entry)
return
# 2. other non-text characters are passed without change
if (key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255):
event.Skip()
return
# 3. check for multiple '.' and out of place '-' signs and ignore these
# note that chr(key) will now work due to return at #2
has_minus = '-' in entry
ckey = chr(key)
if ((ckey == '.' and (self.__prec == 0 or '.' in entry) ) or
(ckey == '-' and (has_minus or pos[0] != 0)) or
(ckey != '-' and has_minus and pos[0] == 0)):
return
# 4. allow digits, but not other characters
if (chr(key) in self.__digits):
event.Skip()
return
# return without event.Skip() : do not propagate event
return
def onText(self, event=None):
try:
if event.GetString() != '':
self.__CheckValid(event.GetString())
except:
pass
event.Skip()
def GetValue(self):
if self.__prec > 0:
return set_float(fpformat.fix(self.__val, self.__prec))
else:
return int(self.__val)
def GetMin(self): return self.__min
def GetMax(self): return self.__max
def SetMin(self,min): self.__min = set_float(min)
def SetMax(self,max): self.__max = set_float(max)
def __Text_SetValue(self,value):
wx.TextCtrl.SetValue(self, self.format % set_float(value))
self.Refresh()
def __CheckValid(self,value):
# print ' Check valid ', value
v = self.__val
try:
self.__valid = True
v = set_float(value)
if self.__min != None and (v < self.__min):
self.__valid = False
v = self.__min
if self.__max != None and (v > self.__max):
self.__valid = False
v = self.__max
except:
self.__valid = False
self.__bound_val = v
if self.__valid:
self.__bound_val = self.__val = v
self.SetForegroundColour(self.fgcol_valid)
self.SetBackgroundColour(self.bgcol_valid)
else:
self.SetForegroundColour(self.fgcol_invalid)
self.SetBackgroundColour(self.bgcol_invalid)
self.Refresh()