Forum Archive

checking for integers inside of a ui textfield

wenchoheelio

I'm trying to check if a textfield in my ui has any numbers in it when I press on a button. When I use the same code on a string it works. When I use the code for textfield within a button's action it doesn't work. Please help? Here's what my code is like:

import ui
import console

def contains_digits(d):
 for char in d:
  if char.isdigit():
   return True
  return False

test='test3'
print(contains_digits(test)) #this works - it prints TRUE!

#below always prints failed even though it should print whatever is in the textfield?!

def button_pushed(sender):
  if contains_digits(textfield1):
   print(textfield1)
  print('failed')

v=ui.load_view()
textfield1 = str(v['textfield1'].text)
v.present('fullscreen')
JonB

To debug this problem, print textfield when entering button_pushed.
As a reminder, strings in pythons are their own object, not pointers.

>a='hello'
>b=a
>a='world'
>print(a==b)
False

By the way, in case you have never used pdb before, this can really be useful.

def button_pushed(sender):
   import pdb
   pdb.set_trace()

now, when you push the button, type

print(textfield)
print(v['textfield1'].text)

wenchoheelio

Thanks so much for your help here. It seems that it's a problem with the variable - textfield1

Doing what you said - textfield1 prints nothing to the console and v['textfield1'].text prints whatever was typed into the textfield

Do you happen to know anyway in which I can use a variable to print to the console using the button?

I think I understand the issue from how you've explained it - the variable textfield1 starts of as "" and so even when it changes it's always going to remain as "", in order for me to put it into a variable - I'd have to put it into a variable within the function, when it's already set as whatever the user has set it as.

I think I may have answered my own question there?

JonB

Yep. Just use v['textfield1'].text, or store a refernce to the actual textfield object, then you can print the .text attribute.

Phuket2

@wenchoheelio , below is another way to consider filtering your TextField. Ie using a TextField Delegate. This way you are getting a peek at the key before it makes it into the TextField(you can accept or reject it, by either returning True or False). Below I have just copied the standard Delegate from the Pythonista Documentation. I just modified the textfield_should_change callback.
I could have deleted all the other methods in the class as in this case I am not using them. They will just do their default behaviour if they are not there. I point that out because the code looks longer and more complicated that it needs to to handle your case. I left them in so it was easy to see the other events you can take control of. Basically meaning you can have a lot of control over the data entry into the TextField.

Btw, I am looping over the replacement as it maybe a string that is being pasted into the textfield. You can try it by copying say '123456' and pasting it into your field. It will appear. But then try pasting '123456R' into the field, that will not work.

Hope it helps.

import ui

class MyTextFieldDelegate (object):
    def textfield_should_begin_editing(self, textfield):
        return True
    def textfield_did_begin_editing(self, textfield):
        pass
    def textfield_did_end_editing(self, textfield):
        pass
    def textfield_should_return(self, textfield):
        textfield.end_editing()
        return True
    def textfield_should_change(self, textfield, range, replacement):
        for c in replacement:
            if not c.isdigit():
                return False
        return True
    def textfield_did_change(self, textfield):
        pass

class MyClass(ui.View):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.make_view()

    def make_view(self):
        tf = ui.TextField(frame=(0, 0, self.width, 32))
        tf.delegate = MyTextFieldDelegate()
        self.add_subview(tf)

if __name__ == '__main__':
    f = (0, 0, 300, 400)
    v = MyClass(frame=f)
    v.present(style='sheet', animated=False)
wenchoheelio

@Phuket2 Thanks, I really appreciate the time you've taken to write this.

wenchoheelio

@Phuket2

super().init(args, *kwargs)

I don't fully understand the super() part here. Not sure I really need to understand or use it early on? Also, I understand arguments/parameters - but what's args, *kwargs ?Does that mean something? Arguments and KW arguments?!

(frame=f)

What is this code for? It doesn't look like the function requires a parameter? (obviously it does.. I just don't understand it)

Thanks.

dgelessus

@wenchoheelio MyClass is a subclass of ui.View. ui.View already has its own __init__ method, and now we also define an __init__ method in our subclass. By default, our subclass __init__ would completely replace the __init__ from ui.View. This would not be good, because then the code in ui.View.__init__ would never run for our MyClass views, and they would not be set up properly. To avoid this, we call super().__init__(*args, **kwargs), which is (in this case) a shortcut for ui.View.__init__(self, *args, **kwargs). That is, we let ui.View initialize our custom view before we do our own initialization.

The *args and **kwargs syntax is used here to pass all arguments from our own __init__ to the superclass __init__. In a function/method definition, *args stands for "accept any number of positional (unnamed) arguments and put them into args as a tuple", and **kwargs stands for "accept any number of keyword (named) arguments and put them into kwargs as a dictionary". In a function/method call, they do basically the opposite, and pass the contents of the given tuple/dictionary as arguments.