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.


    Form dialogs: Add segmented control type

    Pythonista
    5
    8
    5871
    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.
    • Grun6
      Grun6 last edited by Grun6

      Hello,

      Limited coding experience.
      The forms dialog function accepts several types ('switch', 'text', 'url', 'email, etc...) but there is no option for segmented controls. Is it possible to write code that adds that option? In trying to do so, I wanted to look at the source code for the dialogs module but cannot find it.
      help(dialogs) at the prompt tells me the file is in : /var/containers/Bundle/Application/D5B2.......
      And I don't know how to get there... #EDIT: found a work around: in prompt type print(inspect.getsource(dialogs)) ==> gives you the source code of the module. Now in the _FormDialogController class definition, I feel there is a way to add a segmented control item somewhere

      Any pointers are appreciated

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

        Try this

        import ui
        import dialogs
        
        #==================== copied from dialogs: begin	
        import collections
        import sys
        PY3 = sys.version_info[0] >= 3
        if PY3:
        	basestring = str
        	
        def my_form_dialog(title='', fields=None, sections=None, done_button_title='Done'):
        	if not sections and not fields:
        		raise ValueError('sections or fields are required')
        	if not sections:
        		sections = [('', fields)]
        	if not isinstance(title, basestring):
        		raise TypeError('title must be a string')
        	for section in sections:
        		if not isinstance(section, collections.Sequence):
        			raise TypeError('Sections must be sequences (title, fields)')
        		if len(section) < 2:
        			raise TypeError('Sections must have 2 or 3 items (title, fields[, footer]')
        		if not isinstance(section[0], basestring):
        			raise TypeError('Section titles must be strings')
        		if not isinstance(section[1], collections.Sequence):
        			raise TypeError('Expected a sequence of field dicts')
        		for field in section[1]:
        			if not isinstance(field, dict):
        				raise TypeError('fields must be dicts')
        
        	c = dialogs._FormDialogController(title, sections, done_button_title=done_button_title)
        
        	#==================== dialogs.form_dialog modification 1: begin	
        	for i in range(0,len(c.cells[0])):			# loop on rows of section 0
        		cell = c.cells[0][i]									# ui.TableViewCell of row i
        		# some fields types are subviews of the cell:
        		#   text,number,url,email,password,switch
        		#  but check, date and time are not set as subviews of cell.content_view
        		if len(cell.content_view.subviews) > 0:
        			tf = cell.content_view.subviews[0] 		# ui.TextField of value in row
        			# attention: tf.name not set for date fields
        			if tf.name == 'segmented':
        				item = c.sections[0][1][i]	# section 0, 1=items, row i
        				segmented = ui.SegmentedControl()
        				segmented.name = cell.text_label.text
        				segmented.frame = tf.frame
        				segmented.segments = item['segments']
        				cell.content_view.remove_subview(tf)
        				del c.values[tf.name]
        				del tf
        				cell.content_view.add_subview(segmented)
        	#==================== dialogs.form_dialog modification 1: end
        	
        	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:
        		return None
        	
        #==================== dialogs.form_dialog modification 2: begin	
        	for i in range(0,len(c.cells[0])):			# loop on rows of section 0
        		cell = c.cells[0][i]									# ui.TableViewCell of row i
        		# some fields types are subviews of the cell:
        		#   text,number,url,email,password,switch
        		#  but check, date and time are not set as subviews of cell.content_view
        		for tf in cell.content_view.subviews:
        			if 'SegmentedControl' in str(type(tf)):
        				item = c.sections[0][1][i]	# section 0, 1=items, row i
        				c.values[tf.name] = item['segments'][tf.selected_index]
        #==================== dialogs.form_dialog modification 2: end
        
        	return c.values
        #==================== copied from dialogs: end
        	
        fields = []
        field = {'title':'title 1','type':'text','value':'test 1'}
        fields.append(field)
        field = {'title':'title 2','type':'text','value':'test 1','key':'segmented','segments':['seg1','seg2']}
        fields.append(field)
        updated_fields = my_form_dialog(title='my dialog title', fields=fields)
        print(updated_fields)
        
        1 Reply Last reply Reply Quote 0
        • Grun6
          Grun6 last edited by

          Ok so first of all thank you cvp for the answer ! that works great.
          I also "solved" it on my side with the following code:
          I copied the _FormDialogController class and renamed it _SCFormDialogController - note: SC for segmented control
          There, just before

          if t == 'switch'
          

          I inserted:

          if t == 'segmented':
            value = item.get('value', '')
            self.values[key] = value
            cell.selectable = False
            cell.text_label.text = title
            segment = ui.SegmentedControl()
            segment.segments = item.get('value', '').split("|")
            segment.value = value
            segment.name = key
            segment.action = self.segment_action
            label_width = ui.measure_string(title, font=cell.text_label.font)[0]
            cell_width, cell_height = cell.content_view.width, cell.content_view.height
            segment_width = max(40, cell_width - label_width - 32)
            segment.frame = (cell_width - segment_width, 5, segment_width, cell_height -10)
            segment.bordered = False
            segment.flex = 'W'
            cell.content_view.add_subview(segment)
          

          I also added in that class:

          def segment_action(self, sender):
          	self.values[sender.name] = sender.segments[sender.selected_index]
          

          and finally in form_dialog() just changed :

          	c = _SCFormDialogController(title, sections, done_button_title=done_button_title)
          

          Again thanks for the help!

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

            Good job
            When I have to add a feature to form_dialog, I always hesitate between changing form_dialog or changing its controller...

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

              By the way, you can get to built in module source code by going to Modules->Standard Library(3.6)->site-packages. Thats where pythonista custom modules are.

              You can also do

              import editor
              editor.open_file(dialogs.__file__)
              1 Reply Last reply Reply Quote 0
              • ccc
                ccc last edited by ccc

                import sys
                PY3 = sys.version_info[0] >= 3
                if PY3:
                    basestring = str
                
                # could be rewritten as:
                
                try:
                    basestring        # Python 2
                except NameError
                    basestring = str  # Python 3
                

                This follows the Python best practice, use feature detection instead of version detection.

                • https://docs.python.org/3/howto/pyporting.html#use-feature-detection-instead-of-version-detection

                It usually works better with linters like flake8, etc.

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

                  Agree but these lines were copied from standard module...

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

                    Here is a simple dialog template that uses a pyui file created using designer. This helps to use ui controls like images, textviews, segmentedcontrol in dialogs. The gist with sample pyui file is here. I hope it helps

                    https://gist.github.com/73431fef1f00c462c3bee0551bd14be8

                    import ui
                    
                    class PyuiDialogController (object):
                        def __init__(self, title='Pyui Dialog', pyui_dialog='pyui_dialog.pyui',
                                cancel_button_title='Cancel',
                                done_button_title='Done'):
                            self.params = None
                            self.view = ui.load_view(pyui_dialog)
                            self.view.frame = (0, 0, 500, 500)
                            self.view.name = title
                            done_button = ui.ButtonItem(title=done_button_title)
                            done_button.action = self.done_action
                            cancel_button = ui.ButtonItem(title=cancel_button_title)
                            cancel_button.action = self.cancel_action
                            self.view.right_button_items = [done_button, cancel_button]
                    
                        def get_params(self):
                            params = {}
                            params['switch1'] = self.view['switch1'].value
                            params['textfield1'] = self.view['textfield1'].text
                            sg = self.view['segmentedcontrol1']
                            params['segmentedcontrol1'] = sg.segments[sg.selected_index]
                            return params
                    
                        def cancel_action(self, sender):
                            self.view.close()
                                                            
                        def done_action(self, sender):
                            self.params = self.get_params()
                            self.view.close()
                    
                    def pyui_dialog():
                        c = PyuiDialogController()
                        c.view.present('sheet')
                        c.view.wait_modal()
                        return c.params
                    
                    def button_action(sender):
                        print(pyui_dialog())  
                    
                    v = ui.View(frame=(0,0,600,600), name='Test Pyui Dialog')
                    v.add_subview(ui.Button(frame=(200,300, 100,100), 
                        title='Test Dialog', action=button_action))
                    v.present('sheet')
                    
                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post
                    Powered by NodeBB Forums | Contributors