Forum Archive

Form dialogs: Add segmented control type

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

cvp

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)
Grun6

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!

cvp

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

JonB

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)

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.

cvp

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

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')