wxWidgets / Phoenix

wxPython's Project Phoenix. A new implementation of wxPython, better, stronger, faster than he was before.
http://wxpython.org/
2.34k stars 517 forks source link

Problems with wx.lib.masked.numctrl.NumCtrl #2587

Open JamesRandom opened 3 months ago

JamesRandom commented 3 months ago

Operating system: Mac OS Sonoma 14.6 (23G80) (Apple M1 hardware) wxPython version & source: wxPython==4.2.1 (pypi) Python version & source: Python 3.11.9 (main, Apr 3 2024, 23:19:16) [Clang 15.0.0 (clang-1500.1.0.2.5)] (Mac ports)

Description of the problem:

There are several problems with the masked NumCtrl, which make it unusuable. I have described three below. I have also seen some other errors, but the causes (the sequence of inputs and edits) are not so easy to reproduce.

1. Unable to type if initial digits deleted

If I delete the initial value in the control, I can't type anything. Attempting to type digits results in the following error:

Traceback (most recent call last):
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 3088, in _OnChar
    if keep_processing and self._isCharAllowed( char, pos, checkRegex = True ):
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 4712, in _isCharAllowed
    and (sel_to == end or (sel_to < self._masklength and value[sel_start] != field._fillChar))
                                                         ~~~~~^^^^^^^^^^^
IndexError: string index out of range

The displayed number is

2. Deleting the decimal point

If I delete the decimal point and then attempt to type "." I get the following error message:

Traceback (most recent call last):
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 3049, in _OnChar
    keep_processing = self._keyhandlers[key](event)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 3992, in _OnDecimalPoint
    newstr = self._adjustFloat(value)
             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 4439, in _adjustFloat
    intStr, fracStr = value.split(self._decimalChar)
    ^^^^^^^^^^^^^^^
ValueError: not enough values to unpack (expected 2, got 1)

If I move the cursor and then type a decimal point then the program crashes with:

Python[44871:4565493] *** Assertion failure in -[NSTextRange initWithLocation:endLocation:], NSTextRange.m:32
Fatal Python error: PyGILState_Release: thread state 0x1036eb260 must be current when releasing
Python runtime state: initialized

Current thread 0x00000001f7804f40 (most recent call first):
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/textctrl.py", line 140 in _SetSelection
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/core.py", line 3427 in <lambda>
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/core.py", line 2262 in MainLoop
  File "/Users/jamie/Documents/Projects/python/timer/src/test.py", line 37 in main
  File "/Users/jamie/Documents/Projects/python/timer/src/test.py", line 41 in <module>

Extension modules: wx._core (total: 1)
Abort trap: 6

3. Errors after deleting leading spaces

The initial value in the control is preceded by several (10) spaces. Deleting more than 4 of these results in the following error:

Traceback (most recent call last):
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/numctrl.py", line 1258, in OnTextChange
    if not BaseMaskedTextCtrl._OnTextChange(self, event):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 2965, in _OnTextChange
    self._CheckValid()  # Recolor control as appropriate
    ^^^^^^^^^^^^^^^^^^
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 5377, in _CheckValid
    self._applyFormatting()
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 4784, in _applyFormatting
    text, signpos, right_signpos = self._getSignedValue()
                                   ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 4955, in _getSignedValue
    abstext, signpos, right_signpos = self._getAbsValue(text)
                                      ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 4919, in _getAbsValue
    if len(text) >= signpos+1 and  text[signpos+1] in ('-','('):
                                   ~~~~^^^^^^^^^^^
IndexError: string index out of range
Traceback (most recent call last):
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 5578, in _OnKillFocus
    self._AdjustField(self._GetInsertionPoint())
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 4304, in _AdjustField
    newfield = field._AdjustField(slice)
               ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jamie/Library/Python/3.11/lib/python/site-packages/wx/lib/masked/maskededit.py", line 1653, in _AdjustField
    intStr = str(int(intStr))
                 ^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: '0.0'

(That last line is puzzling)

Code Example (click to expand) ```python #!/usr/bin/env python3 import wx import wx.lib.masked class MainWindow(wx.Dialog): """Subclass of wx.Frame to serve as the main window of the example""" def __init__(self): """Create the main window""" super().__init__(None, title="Test") sizer = wx.BoxSizer(wx.VERTICAL) control = wx.lib.masked.numctrl.NumCtrl(parent=self) control.SetFractionWidth(2) sizer.Add(control, flag=wx.ALIGN_LEFT | wx.EXPAND) self.SetSizer(sizer) def main(): # Create the Wx App app = wx.App() # Create the main window with access to the database frame = MainWindow() app.SetTopWindow(frame) frame.Show() app.MainLoop() if __name__ == "__main__": main() ```
JamesRandom commented 3 months ago

BTW I am currently experimenting with using validators on a text control, as that may be a better solution.