Joril
Joril

Reputation: 20596

Alternatives to wx.lib.masked.NumCtrl

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

Answers (2)

I_4m_Z3r0
I_4m_Z3r0

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

tom10
tom10

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

Related Questions