I've tried every way I can possibly see to try and convert the value from the numctrl to a decimal but the problem lies with the retrieved value being a float.

I cant covert the float to a string first because it looses the trailing 0 in values such as 1.30 which I need because I am trying to represent currency.

Does anyone know the way forward or what I am doing wrong please?

import wx
from wx.lib.masked import NumCtrl
from decimal import *

class MyFrame(wx.Frame):

    def __init__(self, parent, id):
        wx.Frame.__init__(self, parent, id, 'Quick Silver', size = (180, 100))

        context = Context(prec = 2)
        setcontext(context)
        
        panel = wx.Panel(self)
        lolinput = wx.lib.masked.NumCtrl(panel, pos = (20,20), fractionWidth = 2) # for 2 decimal places.
        lolinput.SetValue(1.30)

        x = Decimal(lolinput.GetValue(), 2)
        context.create_decimal(x)
        print x

app = wx.App(redirect = False)
frame = MyFrame(parent = None, id = -1)
frame.Show()
app.MainLoop()

Dani AI

Generated

Root cause and short answer: NumCtrl hands you a Python float when fractionWidth>0, and converting that float directly to a Decimal will carry binary‑float artifacts. Prefer constructing Decimal from the control’s textual value (so trailing zeros like "1.30" survive) or, if you really must start from a float, use the Decimal context conversion helpers. was right to flag the float->Decimal issue. (NumCtrl docs; Python decimal notes.) (docs.wxpython.org)

A robust workflow that avoids the epsilon hacks you tried () and the fragile float-format trick () is:

  • Keep fractionWidth=2 so the widget displays two decimals.
  • Grab the control’s plain text (no grouping/format characters) and turn that string straight into Decimal.
  • Use Decimal.quantize (or your Context) to force the exact scale and the rounding mode you want.

Example (illustrates the idea — uses methods that NumCtrl / MaskedEdit provide):

raw = numctrl.GetPlainValue()        # control text, preserves trailing zeros
raw = raw.replace(numctrl.GetDecimalChar(), '.')  # normalize locale decimal char
d = Decimal(raw)
d = d.quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN)

GetPlainValue() (and related masked control helpers) is described in the masked edit docs; NumCtrl API notes that GetValue() returns a float when a fractional width is set. (docs.wxpython.org)

If you already have a float and cannot get a string from the control, the decimal module exposes context helpers (e.g. create_decimal_from_float) that convert a float using your current context rather than importing the full binary expansion. This is more predictable than adding/subtracting tiny epsilons. For details and caveats see the Python decimal documentation. (docs.python.org)

Troubleshooting checklist: confirm fractionWidth is set, inspect GetPlainValue() output for the exact characters (watch locale decimal separators), choose an explicit rounding mode for quantize, and avoid constructing Decimal from raw floats.

Recommended Answers

All 6 Replies

Tried quantize to no avail

import wx
from wx.lib.masked import NumCtrl
from decimal import *

class MyFrame(wx.Frame):

    def __init__(self, parent, id):
        wx.Frame.__init__(self, parent, id, 'Quick Silver', size = (180, 100))
        
        panel = wx.Panel(self)
        lolinput = wx.lib.masked.NumCtrl(panel, pos = (20,20), fractionWidth = 2) # for 2 decimal places.
        lolinput.SetValue(1.30)

        x = Decimal(lolinput.GetValue())
        Decimal(x).quantize(Decimal('.01'), rounding=ROUND_DOWN)

        print x

app = wx.App(redirect = False)
frame = MyFrame(parent = None, id = -1)
frame.Show()
app.MainLoop()

try and convert the value from the numctrl to a decimal

You can not convert a float to decimal.Decimal because floats are inaccurate past a certain number of digits. You will have to get/enter the value as a string and convert.

Solved it by fiddling around with data types

import wx
from wx.lib.masked import NumCtrl
from decimal import *

class MyFrame(wx.Frame):

    def __init__(self, parent, id):
        wx.Frame.__init__(self, parent, id, 'Quick Silver', size = (180, 100))

        getcontext().prec = 3
        
        panel = wx.Panel(self)
        lolinput = wx.lib.masked.NumCtrl(panel, pos = (20,20), fractionWidth = 2) # for 2 decimal places.
        lolinput.SetValue(1.30)

        x = lolinput.GetValue()
        x = x + 0.001
        x = Decimal(x) - Decimal("0.001")
        Decimal(x).quantize(Decimal('.01'), rounding=ROUND_DOWN)

        print x

app = wx.App(redirect = False)
frame = MyFrame(parent = None, id = -1)
frame.Show()
app.MainLoop()

Why not x = Decimal('{0:.2f}'.format(lolinput.GetValue()), 2) ?

Yours would more than likely have worked fine grib and I may actually use it tbh.

They way I solved it in the end was create a function call.

self.Round2DP(lolinput)

    def Round2DP(self, event):

        x = event.GetValue()
        x = x + 0.001
        x = Decimal(x) - Decimal("0.001")
        Decimal(x).quantize(Decimal('.01'), rounding=ROUND_DOWN)

        print x
        return x

Why not just do something like
x = 12.30
x_str = "%5.2f" % (x+0.004)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.