Forum Archive

Is it possible to Limit the decimals of a float?

RocketBlaster05

I need to be able to prevent the user from putting more than 2 decimals on an input, which gets turned into a float. Is there any way to limit the input?

Thanks!

cvp

@RocketBlaster05 if you use an ui.TextField for introduction of the number, you could use delegate methods.

RocketBlaster05

@cvp would you be able to link me to a good example of delegates? I've never used them before. Thanks!

cvp

@RocketBlaster05 see this topic

cvp

@RocketBlaster05 try this quick and dirty script to better understand

import ui

class MyTextFieldDelegate (object):
    def textfield_should_change(self, textfield, range_c, replacement):
        # Build field value if replacement would be accepted
        fld = textfield.text
        #print(fld, range_c, replacement)
        if replacement == '':
            # Backspace or cut to remove textfield[range[0]to[range[1]-1]
            new = fld[:range_c[0]]+fld[range_c[1]:]
        else:
            # replacement could even be more than 1 character if paste
            new = fld[:range_c[0]]+replacement+fld[range_c[0]:]
        i = new.find('.')
        l = len(new.rstrip())
        if i < (l-3):
            return False
        return True

tf = ui.TextField()
tf.frame = (0,0,200,30)
tf.delegate = MyTextFieldDelegate()
tf.placeholder = 'type your number here'
tf.present('sheet')
RocketBlaster05

@cvp oh ok nevermind sorry it looks like I don't need a delegate. I can just use round() and it rounds it to 2 decimal places. Sorry for the inconvenience but I appreciate your help nonetheless. Thanks!

cvp

@RocketBlaster05 ok but that does not prohibit user to introduce more than 2 decimals... even if the effect is The same. Keep topic for when you will need delegate, and you will need it 😁

RocketBlaster05

@cvp I will keep this in mind. Thanks!

RocketBlaster05

@cvp looks like I might be using delegate soon... for some reason round() is glitching and... well, not rounding properly. It is giving me .9999999999 when I stress test it with long decimals

cvp

@RocketBlaster05 sorry, no idea

mikael

@RocketBlaster05, look at decimal.Decimal, it handles decimal (duh) numbers sanely.

RocketBlaster05

@cvp I have it applied to a text field in a presented ui. The sample you showed works but not when I implement it as such:

class MyTextFieldDelegate (object):
    def textfield_should_change(self, textfield, range_c, replacement):
        # Build field value if replacement would be accepted
        fld = textfield.text
        #print(fld, range_c, replacement)
        if replacement == '':
            # Backspace or cut to remove textfield[range[0]to[range[1]-1]
            new = fld[:range_c[0]]+fld[range_c[1]:]
        else:
            # replacement could even be more than 1 character if paste
            new = fld[:range_c[0]]+replacement+fld[range_c[0]:]
        i = new.find('.')
        l = len(new.rstrip())
        if i < (l-3):
            return False
        return True

tf = ui.load_view('GlassInput')['textview1']
tf.delegate = MyTextFieldDelegate() 
#No errors pop up but delegate doesn’t work when view is presented elsewhere
cvp

@RocketBlaster05 said: incorrect

tf = ui.load_view('GlassInput')['textview1']

try

v = ui.load_view('GlassInput')
tf = v['textview1']
cvp

@RocketBlaster05 use à TextField instead of a TextView

RocketBlaster05

@cvp this is all my code for the process I’m trying to complete. I’ve done all you have said yet it doesn’t work. Everything gets properly presented but the delegate just isn’t applied:
```
def text_entered_glass(sender):
global userinput
userinput = sender.text
try:
input = float(userinput)
roundedinput = round(input, 2)
if roundedinput <= 10 and roundedinput >= 0:
global gltot
gltot += roundedinput
v.name = str(gltot)
with open(fil3,mode='wt') as f:
f.write(str(gltot))
m.changetotals()
print (gltot)
nav.close()
else:
ui.load_view('GlassError').present('sheet')
except ValueError:
ui.load_view('GlassError').present('sheet')

class MyTextFieldDelegate (object):
def textfield_should_change(self, textfield, range_c, replacement):
# Build field value if replacement would be accepted
fld = textfield.text
#print(fld, range_c, replacement)
if replacement == '':
# Backspace or cut to remove textfield[range[0]to[range[1]-1]
new = fld[:range_c[0]]+fld[range_c[1]:]
else:
# replacement could even be more than 1 character if paste
new = fld[:range_c[0]]+replacement+fld[range_c[0]:]
i = new.find('.')
l = len(new.rstrip())
if i < (l-3):
return False
return True

gi = ui.load_view('GlassInput')
tf = gi['textfield1']
tf.delegate = MyTextFieldDelegate() ```

cvp

@RocketBlaster05 I see that the input field is named TextField1 but is it a TextField or a TextView?

And where do you present the view named gi ?

Do you use twice ui.load_view('GlassInput')?

Perhaps, could it be better if you post your entire code...

RocketBlaster05

@cvp alright but this is might be a wild ride:

class Menu(ui.View): # Main Menu setup
    def __init__(self):
        ui.View.__init__(self)
        self.scene_view = scene.SceneView()
        self.present(hide_title_bar=True)
        self.scene_view.frame = self.bounds
        self.scene_view.bg_color = '#4fb357'
        self.add_subview(self.scene_view)
        self.mm = ui.load_view('MainMenu')
        self.mm['imageview1'].image = ui.Image('companylogo.PNG')
        self.changetotals()
        self.mm.present('fullscreen', hide_title_bar=True)  

    def changetotals(self): #just updates stats below
        if os.path.exists(fil):
            with open(fil,mode='rt') as f:
                global wbtot
                wbtot = int(f.read())
        else:
            wbtot = 0
        if os.path.exists(fil2):
            with open(fil2,mode='rt') as f:
                global cbbtot
                cbbtot = int(f.read())
        else:
            cbbtot = 0
        if os.path.exists(fil3):
            with open(fil3,mode='rt') as f:
                global gltot
                gltot = float(f.read())
        else:
            gltot = 0
        if os.path.exists(fil4):
            with open(fil4,mode='rt') as f:
                global paptot
                paptot = float(f.read())
        else:
            paptot = 0
        self.mm['textview1'].text = 'Water Bottles Recyled: ' + str(wbtot)
        self.mm['textview2'].text = 'Cardboard Boxes Recycled: ' + str(cbbtot)
        self.mm['textview3'].text = 'Glass Recycled: ' + str(gltot) + ' lbs'
        self.mm['textview4'].text = 'Paper Recycled: ' + str(paptot) + ' lbs' 

def glass_pressed(Glass): # ui button on main menu
    global v
    v = ui.load_view('GlassInput')
    rm.update_menus(nam='View4', col='#4fb357') # changes a navigation view tab to open glass input page; possible source of error

class MyTextFieldDelegate (object): # The delegate script
    def textfield_should_change(self, textfield, range_c, replacement):
        # Build field value if replacement would be accepted
        fld = textfield.text
        #print(fld, range_c, replacement)
        if replacement == '':
            # Backspace or cut to remove textfield[range[0]to[range[1]-1]
            new = fld[:range_c[0]]+fld[range_c[1]:]
        else:
            # replacement could even be more than 1 character if paste
            new = fld[:range_c[0]]+replacement+fld[range_c[0]:]
        i = new.find('.')
        l = len(new.rstrip())
        if i < (l-3):
            return False
        return True

def text_entered_glass(sender): # Textfield input process
    global userinput
    userinput = sender.text
    try:
        input = float(userinput)
        roundedinput = round(input, 2)
        if roundedinput <= 10 and roundedinput >= 0:
            global gltot
            gltot += roundedinput
            v.name = str(gltot)
            with open(fil3,mode='wt') as f:
                f.write(str(gltot))
            m.changetotals()
            print (gltot)
            nav.close()
        else:
            ui.load_view('GlassError').present('sheet')
    except ValueError:
        ui.load_view('GlassError').present('sheet')

v = ui.load_view('GlassInput') #attempt at setting delegate
tf = v['textfield1']
tf.delegate = MyTextFieldDelegate()

class RecycleMenu(): # Navigation menu setup mentioned earlier
    def __init__(self):
        self.background_color = '#4fb357'
        self.start_NavView()

    def start_NavView(self):
        self.view = ui.load_view('RecycleMenuOptions')
        self.view.name='Home'
        global nav
        nav = ui.NavigationView(self.view)
        nav.present(hide_title_bar=True)    

    def update_menus(self, nam=None, col=None):
        v.name = nam
        v.background_color = col
        nav.push_view(v)
cvp

@RocketBlaster05 it is what I thought, you have twice your view...
Move the tf = after the first one

def glass_pressed(Glass): # ui button on main menu
    global v
    v = ui.load_view('GlassInput')
    tf = v['textfield1']
    tf.delegate = MyTextFieldDelegate()
    rm.update_menus(nam='View4', col='#4fb357') # changes a navigation view tab to open glass input page; possible source of error

Then remove the second one

#v = ui.load_view('GlassInput') #attempt at setting delegate
#tf = v['textfield1']
#tf.delegate = MyTextFieldDelegate()

You also would need to move the class MyTextFieldDelegate before the def glass_pressed.

Please, try these modifications and tell me....

RocketBlaster05

@cvp YES YES YES YES YES!!!!!!! Thank you so much it is finally working as planned!

cvp

@RocketBlaster05

RocketBlaster05

@cvp you are the man don't let anybody tell you otherwise!

cvp

@RocketBlaster05 you are too kind. Do you agree that I forward your sentence to my wife? 😂

RocketBlaster05

@cvp without a doubt my man

RocketBlaster05

@cvp I've been messing around testing all the functions of the app but occasionally when I rerun the program the textview under the changetotals() function adds an extra .0000000000001 to the end of the number and I don't know why. Any Ideas?

RocketBlaster05

I assume it is related to the delegate... If you press more than the delegate allows it bugs out the number every now and then for some reason.

cvp

@RocketBlaster05 Are should sure that textfield1 is a TextField not a TextView in your pyui?

RocketBlaster05

@cvp yes

cvp

@RocketBlaster05
text_entered_glass is called after the delegate. Is your round still needed?

RocketBlaster05

@cvp Here is what I have it set to now:
def text_entered_paper(sender): global userinput userinput = sender.text try: input = float(userinput) if input <= 5 and input >= 0: global paptot paptot += input v.name = str(paptot) with open(fil4,mode='wt') as f: f.write(str(paptot)) m.changetotals() print (paptot) nav.close() else: ui.load_view('PaperError').present('sheet') except ValueError: ui.load_view('PaperError').present('sheet')

cvp

@RocketBlaster05 and is your userinput erroneous ?

RocketBlaster05

@cvp it should be, as I am using the try and except for keeping track of the input. And user input is covered by the delegate

cvp

@RocketBlaster05 said:

input = float(userinput)

Could you add a print(userinput,input) just after to be sure

RocketBlaster05

@cvp I'm telling it to print input yet it won't print in in the console... but I know it must be being kept track of because it goes through the if statement properly...

cvp

@RocketBlaster05 and print paptot before and after its increase

cvp

@RocketBlaster05 please use something else than the word input, it is a reserved word!

RocketBlaster05

@cvp yeah everything is tracking properly it seems... oh nope nevermind ok yep there it is... I just printed it and for some reason after adding the input it added an extra 00000000004 to the end of it

RocketBlaster05

@cvp ok...I've been looking at it and I added 2.11 to the previous value and it went up by 2.10999999999999... for some reason it is failing to keep track of just the 2 decimals...

RocketBlaster05

@cvp I fixed it now... I used format in order to properly round it. In changetotals() I went to my variables and changed paptot and gltot to this format:

gltot = format(float(f.read()), '.2f')
paptot = format(float(f.read()), '.2f')
cvp

@RocketBlaster05 Nice.
For your information, a decimal number (a number with decimals) is stored in a binary memory and is almost never exactly correct, and it is so since the invention of IT.

RocketBlaster05

@cvp ugh... one Last bug for my demo... when I do a callback on changetotals() it works every time until I open a certain ui...

class Menu(Scene):
    def __init__(self):
        self.background_color = '#4fb357'
        self.mm = ui.load_view('MainMenu')
        self.mm['imageview1'].image = ui.Image('companylogo.PNG')
        self.changetotals()
        self.mm.present('fullscreen', hide_title_bar=True)

    def changetotals(self): # works until learn ui is called
        if os.path.exists(fil):
            with open(fil,mode='rt') as f:
                global wbtot
                wbtot = int(f.read())
        else:
            wbtot = 0
        if os.path.exists(fil2):
            with open(fil2,mode='rt') as f:
                global cbbtot
                cbbtot = int(f.read())
        else:
            cbbtot = 0
        if os.path.exists(fil3):
            with open(fil3,mode='rt') as f:
                global gltot
                gltot = float(format(float(f.read()), '.2f'))
        else:
            gltot = 0
        if os.path.exists(fil4):
            with open(fil4,mode='rt') as f:
                global paptot
                paptot = float(format(float(f.read()), '.2f'))
        else:
            paptot = 0
        self.mm['textview1'].text = 'Water Bottles Recyled: ' + str(wbtot)
        self.mm['textview2'].text = 'Cardboard Boxes Recycled: ' + str(cbbtot)
        self.mm['textview3'].text = 'Glass Recycled: ' + str(gltot) + ' lbs'
        self.mm['textview4'].text = 'Paper Recycled: ' + str(paptot) + ' lbs' 

def button_tapped_learn(Learn): # ui button on main menu that presents bugged ui.View
    global m
    m.mm = ui.load_view('LearnPage')
    m.mm.present('fullscreen', hide_title_bar=True)

def menu_return(Back): # process that returns to main menu... causes changetotals() to not work properly
    global m
    m.mm.close()
    m.mm = ui.load_view('MainMenu')
    m.changetotals()
RocketBlaster05

Oh no sorry I fixed it... was using a universal back button but now problem is fixed since I made two separate menu return buttons. Yay no bugs!