Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
'Enter'-keypress/softpress to call action in custom form input
-
I just started on Pythonista a few days ago (and so far I love it), in order to learn a bit more of Python (I have no professional programming experience).
So after having stolen and modified the code by @cvp here and modified it a bit I ended up with the following little snippet, to convert a temperature from Fahrenheit to Celcius (just to have it do something more than print hello world):
from dialogs import _FormDialogController from console import set_color c = _FormDialogController('Input Box', [('', [{'title':'Fahrenheit:','type':'text','value':''}])], done_button_title='Done') c.container_view.frame = (0, 0, 400,130) c.container_view.present('sheet') c.container_view.wait_modal() c.container_view = None if c.was_canceled: set_color(1,0,0) print('! ', end='') set_color() print('Cancelled') else: try: float(c.values['Fahrenheit:']) set_color(1,1,1) print(round((int(c.values['Fahrenheit:'])-32)*5/9,2), end='') set_color() print('˚C') except: set_color(1,0,0) print('! ', end='') set_color()
I went the way with the custom box, in order to generate a simple single-line input form, which the dialogs in the dialogs-module does not seem to have, so this seemed like the appropriate way.
I am missing a way to to press enter after having entered the Fahrenheit value, rather than having pto press Done. Is this doable?On a side-note: The documentation for console.set_color doesn't actually mention this, unlike for console.set_font, but set_color() (without parameters) does actually set the color back to default color.
-
@boegh try something like
import ui import dialogs from console import set_color class mytf_delegate(object): global c def textfield_did_end_editing(self, textfield): c.done_action('sender') # unused parameter def textfield_did_change(self, tf): c.values[tf.name] = tf.text def myform_dialog(title='', fields=None,sections=None, done_button_title='ok'): global c sections = [('', fields)] c = dialogs._FormDialogController(title, sections, done_button_title=done_button_title) for s in range(0,len(c.sections)): for i in range(0,len(c.cells[s])): # loop on rows of section s cell = c.cells[s][i] # ui.TableViewCell of row i tf = cell.content_view.subviews[0] # ui.TextField of value in row tf.delegate = mytf_delegate() c.container_view.frame = (0, 0, 400, 130) c.container_view.present('sheet') c.container_view.wait_modal() # Get rid of the view to avoid a retain cycle: c.container_view = None if c.was_canceled: set_color(1,0,0) print('! ', end='') set_color() print('Cancelled') return None else: try: #print(c.values['Fahrenheit:']) float(c.values['Fahrenheit:']) set_color(0,0,1) # not 1,1,1 because background is also white print(round((int(c.values['Fahrenheit:'])-32)*5/9,2), end='') set_color() print('˚C') except Exception as e: print(e) set_color(1,0,0) print('! ', end='') set_color() return c.values fields = [{'title':'Fahrenheit:','type':'text','value':''}] f = myform_dialog(title='dialog title', done_button_title='ok',fields=fields, sections=None)
-
@boegh this is even better/shorter because in the standard _FormDialogController, delegate is self, thus overriding the textfield_did_end_editing delegate can be set to the done_action even if their (unused) parameter TextField or sender are different but here unused.
Complex thinking but easy solution. Complex? Ok, I've exaggerated 😀import ui import dialogs from console import set_color def myform_dialog(title='', fields=None,sections=None, done_button_title='ok'): global c sections = [('', fields)] c = dialogs._FormDialogController(title, sections, done_button_title=done_button_title) c.textfield_did_end_editing = c.done_action c.container_view.frame = (0, 0, 400, 130) c.container_view.present('sheet') c.container_view.wait_modal() # Get rid of the view to avoid a retain cycle: c.container_view = None if c.was_canceled: set_color(1,0,0) print('! ', end='') set_color() print('Cancelled') return None else: try: #print(c.values['Fahrenheit:']) float(c.values['Fahrenheit:']) set_color(0,0,1) # not 1,1,1 because background is also white print(round((int(c.values['Fahrenheit:'])-32)*5/9,2), end='') set_color() print('˚C') except Exception as e: print(e) set_color(1,0,0) print('! ', end='') set_color() return c.values fields = [{'title':'Fahrenheit:','type':'text','value':''}] f = myform_dialog(title='dialog title', done_button_title='ok',fields=fields, sections=None)
-
@cvp, thank you for your quick response.
I see the magic lies in utilising
c.textfield_did_end_editing
and assigning that to adone_action
(or like) function.Again thank you - can I buy you a cup of coffee?
-
@boegh it's very nice of you, I did not know buymeacoffee.com and it sounds nice, but it is certain that I do not deserve anything for my (always quick and dirty) posts.
-
Just for the fun...
I never thought about it before but there is a way to change the frame of the dialog (and define the return as the done button) without redefining the form_dialog.
You just have to monkey patch the init method of the _FormDialogController class.
The only problem is that, during a rerun, you have to delete the import of dialogs, otherwise a problem of recurrence occurs.
Try this, if you want 😀import ui from console import set_color try: del sys.modules['dialogs'] except: pass import dialogs original__init__ = dialogs._FormDialogController.__init__ def my__init__(self, *args, **kwargs): original__init__(self, *args, **kwargs) self.container_view.frame = (0, 0, 400, 130) self.textfield_did_end_editing = self.done_action dialogs._FormDialogController.__init__ = my__init__ fields = [{'title':'Fahrenheit:','type':'text','value':''}] f = dialogs.form_dialog(title='dialog title', done_button_title='ok',fields=fields, sections=None) if f: try: float(f['Fahrenheit:']) set_color(0,0,1) # not 1,1,1 because background is also white print(round((int(f['Fahrenheit:'])-32)*5/9,2), end='') set_color() print('˚C') except Exception as e: print(e) set_color(1,0,0) print('! ', end='') set_color() else: set_color(1,0,0) print('! ', end='') set_color() print('Cancelled')
I would like to add that I am proud of myself for having found this, because, sincerely, I am far from mastering Python 😢
-
Again thank you for the teaching points @cvp :)
I tried your code, and as you suggested, the rerun did give an error:
Traceback (most recent call last): File "/private/var/mobile/Containers/Shared/AppGroup/60EBD24F-667F-4F6E-B595-9100C20FC784/Pythonista3/Documents/dialogs_2.py", line 16, in <module> f = dialogs.form_dialog(title='dialog title', done_button_title='ok',fields=fields, sections=None) File "/var/containers/Bundle/Application/CD376C3C-8193-4F37-B990-A9B960D30F2D/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/site-packages/dialogs.py", line 456, in form_dialog c = _FormDialogController(title, sections, done_button_title=done_button_title) File "/private/var/mobile/Containers/Shared/AppGroup/60EBD24F-667F-4F6E-B595-9100C20FC784/Pythonista3/Documents/dialogs_2.py", line 10, in my__init__ original__init__(self, *args, **kwargs) File "/private/var/mobile/Containers/Shared/AppGroup/60EBD24F-667F-4F6E-B595-9100C20FC784/Pythonista3/Documents/dialogs_2.py", line 10, in my__init__ original__init__(self, *args, **kwargs) File "/private/var/mobile/Containers/Shared/AppGroup/60EBD24F-667F-4F6E-B595-9100C20FC784/Pythonista3/Documents/dialogs_2.py", line 10, in my__init__ original__init__(self, *args, **kwargs) [Previous line repeated 494 more times] RecursionError: maximum recursion depth exceeded while calling a Python object
Am I missing something here or isn't it the purpose of the
del sys.modules['dialogs']
-statement to avoid this issue? -
@boegh said:
Am I missing something here or isn't it the purpose of the del sys.modules['dialogs']-statement to avoid this issue?
Normally, it is the purpose of deleting the imported dialogs module, did you remove this part of code?
I retried my code and it works
-
@cvp said:
Normally, it is the purpose of deleting the imported dialogs module, did you remove this part of code?
No I kept it exactly as you wrote it (copy/paste). I can offer no explanation, but I did narrow in on the problem:
Removing the try/except-block around the
del sys.modules['dialogs']
-line, gave me the following error:Traceback (most recent call last): File "/private/var/mobile/Containers/Shared/AppGroup/60EBD24F-667F-4F6E-B595-9100C20FC784/Pythonista3/Documents/dialogs_2.py", line 4, in <module> del sys.modules['dialogs'] NameError: name 'sys' is not defined
So I tried to add
import sys
in the beginning of the file, and it worked well. I am a bit perplexed by this behaviour, as I understood it so, that the sys-module would always be loaded?Keeping the import-statement in place, I reinserted the try/except-block and it works flawlessly.
I am using Python 3.6 Interpreter in Pythoniste v.3.2 (320000).
-
@boegh Strange because, for me, my script runs perfectly several times...
Hoping a Python guru would explain us... -
@boegh I think that you are right, you need to import sys to allow correct working of the del.
In my case, my pythonista_startup imports it thus its is already imported before my script.