Reputation: 20596
In a wxPython application I'm developing I need a lot of input fields for numbers (integers and floats), so I tried using wx.lib.masked.NumCtrl, but my users now tell me that it's quite uncomfortable to use (and I agree with them).
Is there an alternative widget implementation I can use, or should I just roll my own, starting from a bare TextCtrl?
(wxPython 2.8.9.1)
Edit
For completeness, here's an example of "uncomfortableness":
given a NumCtrl with selectOnEntry
and fractionWidth > 0
, when you switch to the decimal part of the field, it gets correctly selected, but pressing numbers doesn't do anything, you have to delete the contents of the field first.
Upvotes: 1
Views: 1573
Reputation: 1090
You can use a TxtCtrl and check the user key code pressed and add only the numerical key codes. I personally did this and managed all the selection part of the fields ecc.
Usage:
self.__MyTxtCtrl.Bind(wx.EVT_CHAR, self.__on_change_text_check_is_float_value)
def __on_change_text_check_is_int_value(self, evt):
KeyboardEventUtils.on_change_text_check_is_int_value(self, evt)
The listener in a singleton class utils:
from pyglet.window import key
from Utils.NumberUtils import NumberUtils
ENTRY_ID = "id"
ENTRY_KEY_EVENT = "keyEvent"
ENTRY_SELECTION_EVENT = "selectionEvent"
MASK_STARTED_RIGHT_SELECTION = 1
MASK_STARTED_LEFT_SELECTION = -1
class KeyboardEventUtils(object):
__mLastEvt = {}
def on_change_text_check_is_float_value(self, evt):
txt = evt.GetEventObject()
strng = txt.GetValue()
rawKey = evt.GetRawKeyCode()
modifiers = evt.GetModifiers()
if KeyboardEventUtils.__mLastEvt != None and len(KeyboardEventUtils.__mLastEvt) > 0 and KeyboardEventUtils.__mLastEvt[ENTRY_ID] != txt.GetId():
self._mLastEvt = {}
if chr(rawKey).isnumeric() or (rawKey == key.PERIOD and not chr(rawKey) in strng and len(strng) > 0) or (rawKey == key.MINUS and not chr(rawKey) in strng and (len(strng) == 0 or txt.GetInsertionPoint() == 0)):
pos = txt.GetInsertionPoint()
txt.SetValue(str(float(txt.GetValue()[:pos] + chr(rawKey) + txt.GetValue()[pos:])))
txt.SetInsertionPoint(pos + 1)
elif (modifiers == 4 or modifiers == key.MOD_SHIFT) and rawKey == key.LEFT: # 4 (?) key.MOD_SHIFT (?)
pos = txt.GetInsertionPoint()
rng = list(txt.GetSelection())
if rng[0] == rng[1] and ENTRY_SELECTION_EVENT in KeyboardEventUtils.__mLastEvt:
del KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT]
if ENTRY_SELECTION_EVENT in KeyboardEventUtils.__mLastEvt and KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT] == MASK_STARTED_RIGHT_SELECTION:
rng[1] = rng[1] - 1
else:
KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT] = MASK_STARTED_LEFT_SELECTION
rng[0] = rng[0] - 1
txt.SetSelection(rng[0], rng[1])
elif (modifiers == 4 or modifiers == key.MOD_SHIFT): # 4 (?) key.MOD_SHIFT (?)
pos = txt.GetInsertionPoint()
rng = list(txt.GetSelection())
if rng[0] == rng[1] and ENTRY_SELECTION_EVENT in KeyboardEventUtils.__mLastEvt:
del KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT]
if ENTRY_SELECTION_EVENT in KeyboardEventUtils.__mLastEvt and KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT] == MASK_STARTED_LEFT_SELECTION:
rng[0] = rng[0] + 1
else:
KeyboardEventUtils.__mLastEvt[ENTRY_SELECTION_EVENT] = MASK_STARTED_RIGHT_SELECTION
rng[1] = rng[1] + 1
txt.SetSelection(rng[0], rng[1])
elif rawKey == key.LEFT:
rng = list(txt.GetSelection())
if rng[0] != rng[1]:
txt.SetInsertionPoint(rng[0])
else:
txt.SetInsertionPoint(txt.GetInsertionPoint() - 1)
elif rawKey == key.RIGHT:
rng = list(txt.GetSelection())
if rng[0] != rng[1]:
txt.SetInsertionPoint(rng[1])
else:
txt.SetInsertionPoint(txt.GetInsertionPoint() + 1)
elif rawKey == key.UP or rawKey == key.DOWN:
pass
elif rawKey == key.BACKSPACE:
pos = txt.GetInsertionPoint()
if txt.GetStringSelection() == "":
txt.SetValue(txt.GetValue()[:pos-1] + txt.GetValue()[pos:])
txt.SetInsertionPoint(pos - 0x1)
else:
r = txt.GetSelection()
txt.SetValue(txt.GetValue()[:r[0]] + txt.GetValue()[r[1]:])
txt.SetInsertionPoint(pos)
elif rawKey == key.DELETE:
pos = txt.GetInsertionPoint()
if txt.GetStringSelection() == "":
txt.SetValue(txt.GetValue()[:pos] + txt.GetValue()[pos+1:])
else:
r = txt.GetSelection()
txt.SetValue(txt.GetValue()[:r[0]] + txt.GetValue()[r[1]:])
txt.SetInsertionPoint(pos)
elif modifiers == key.MOD_CTRL and rawKey == key.A:
txt.SelectAll()
else:
KeyboardEventUtils.__mLastEvt[ENTRY_ID] = txt.GetId()
KeyboardEventUtils.__mLastEvt[ENTRY_KEY_EVENT] = evt
return False
KeyboardEventUtils.__mLastEvt[ENTRY_ID] = txt.GetId()
KeyboardEventUtils.__mLastEvt[ENTRY_KEY_EVENT] = evt
return True
I hope this can help ^^
SOURCE of INFOs key MODULE Python
Upvotes: 0
Reputation: 69242
In the usual wxPython distribution there's IntCtrl, and then a few other GUI controls like Slider, Spin, FloatSpin, and KnobCtrl.
There's also the Enthought Traits approach, and the GUI part of this seems to have put a fair amount of focus on numerical entry and display, such as logarithmic sliders, float array editors, etc. Looking at their designs might give some inspiration even if you don't take this path.
Also, it's not really clear why you don't like the masked NumCtrl, but it's very easy to write your own, so if there's some specific thing you want, that's probably the way to go.
Upvotes: 1