omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    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

    Pythonista
    2
    11
    3517
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • boegh
      boegh last edited by boegh

      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.

      cvp 2 Replies Last reply Reply Quote 0
      • cvp
        cvp @boegh last edited by cvp

        @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) 
        
        1 Reply Last reply Reply Quote 0
        • cvp
          cvp @boegh last edited by cvp

          @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) 
          
          boegh 1 Reply Last reply Reply Quote 1
          • boegh
            boegh @cvp last edited by

            @cvp, thank you for your quick response.

            I see the magic lies in utilising c.textfield_did_end_editing and assigning that to a done_action(or like) function.

            Again thank you - can I buy you a cup of coffee?

            cvp 1 Reply Last reply Reply Quote 0
            • cvp
              cvp @boegh last edited by

              @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.

              1 Reply Last reply Reply Quote 0
              • cvp
                cvp last edited by cvp

                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 😢

                boegh 1 Reply Last reply Reply Quote 0
                • boegh
                  boegh @cvp last edited by boegh

                  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?

                  cvp 1 Reply Last reply Reply Quote 0
                  • cvp
                    cvp @boegh last edited by cvp

                    @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

                    1 Reply Last reply Reply Quote 0
                    • boegh
                      boegh last edited by boegh

                      @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).

                      cvp 2 Replies Last reply Reply Quote 0
                      • cvp
                        cvp @boegh last edited by

                        @boegh Strange because, for me, my script runs perfectly several times...
                        Hoping a Python guru would explain us...

                        1 Reply Last reply Reply Quote 0
                        • cvp
                          cvp @boegh last edited by

                          @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.

                          1 Reply Last reply Reply Quote 0
                          • First post
                            Last post
                          Powered by NodeBB Forums | Contributors