Forum Archive

Implementing a radio style (single selected option) check mark in a forms dialog

madivad

I would like to use the check type entry in a form dialog but in a radio button fashion. ie only one check mark in that section at a time.

I’m trying to work it out and I think I know how to program it, I just don’t know where to implement it in the _FormDialogController

ccc

http://omz-software.com/pythonista/docs/ios/ui.html#switch

madivad

I hadn’t thought of switch, I think that looks better than check anyway :)

@ccc or @cvp , What is the method that is called within the form controller that would need to be overwritten/created?

madivad

Wow, and I've just found the segmented control too that @cvp was talking about. Am working to implement that. A single line of options is much better than a list. That's very nice, I'll let you know how I get on

cvp

@madivad try this and see field under age and value of returned diag when you select a segment

import dialogs
import datetime
import ui

#================ copy dialogs.form_dialog: 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')

    #========== modify 1: begin
    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]                                    
            if len(cell.content_view.subviews) > 0:
                tf = cell.content_view.subviews[0]      # ui.TextField of value in row
                item = c.sections[s][1][i]                      # section s, 1=items, row i
                if 'segments' in item:
                    segmented = ui.SegmentedControl()
                    segmented.name = cell.text_label.text
                    segmented.frame = tf.frame
                    segmented.x = c.view.width - segmented.width - 8
                    segmented.segments = item['segments']
                    cell.content_view.remove_subview(tf)
                    del c.values[tf.name]
                    del tf
                    cell.content_view.add_subview(segmented)
    #========== modify 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

    #========== modify 2: begin
    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]                                    
            if len(cell.content_view.subviews) > 0:
                tf = cell.content_view.subviews[0]      # ui.TextField of value in row
                if isinstance(tf, ui.SegmentedControl):
                    if tf.selected_index >= 0:
                        item = c.sections[s][1][i]                      # section s, 1=items, row i
                        c.values[tf.name] = item['segments'][tf.selected_index]
    #========== modify 2: end       

    return c.values
#================ copy dialogs.form_dialog: end

form_list_of_sections = []

sectionA_dicts = []
sectionA_dicts.append(dict(type = 'text', title = 'First Name',
key = 'first', placeholder = 'John'))

sectionA_dicts.append(dict(type = 'text', title = 'Last Name',
key = 'last', placeholder = 'Doe')) 

sectionA_dicts.append(dict(type = 'number', title = 'age',
key = 'age', placeholder='30')) 

sectionA_dicts.append(dict(type = 'text', title = 'My segmented control',
key = 'segm', segments = ['yes','no']))

form_list_of_sections.append(('Section A', sectionA_dicts, 'Section A ends'))

sectionB_dicts = []
sectionB_dicts.append(dict(type = 'date', title = 'Date Of Birth',
key = 'DOB', value = datetime.date.today()))

sectionB_dicts.append(dict(type = 'url', title = 'Home Page',
    key = 'homepage', placeholder = 'http://example.com')) 

form_list_of_sections.append(('Section B', sectionB_dicts, 'Section B ends'))

sectionC_dicts = []
sectionC_dicts.append(dict(type = 'email', title = 'email',
key = 'email', placeholder = 'name@mailserver.com')) 

sectionC_dicts.append(dict(type = 'switch', title = 'is_married',
key = 'is_married', value = True))  

sectionC_dicts.append(dict(type = 'check', title = 'is_registered',
key = 'is_registered', value = False))  

form_list_of_sections.append(('Section C', sectionC_dicts, 'Section C ends'))

diag = my_form_dialog(title = 'Form Dialog', sections=form_list_of_sections)
print(diag) 
cvp

@madivad said:

What is the method that is called within the form controller that would need to be overwritten/created?

I don't understand the question if it is relative to the switch because your sample already contains a switch.

cvp

And without your own form_dialog....but it has been hard to find 😅

def segmented_action(sender):
    global c
    if sender.selected_index >= 0:      
        c.values[sender.name] = sender.segments[sender.selected_index]

def my_tableview_cell_for_row(self,tv, section, row):
    global c
    c = self
    cell = self.cells[section][row]
    if len(cell.content_view.subviews) > 0:
        tf = cell.content_view.subviews[0]      # ui.TextField of value in row
        item = self.sections[section][1][row]   
        if 'segments' in item:
            # check if SegmentedControl already added
            for sv in cell.content_view.subviews:
                if type(sv) is ui.SegmentedControl:
                    return cell
            segmented = ui.SegmentedControl()
            segmented.name = cell.text_label.text
            segmented.action = segmented_action
            segmented.frame = tf.frame
            segmented.x = c.view.width - segmented.width - 8
            segmented.segments = item['segments']
            cell.content_view.add_subview(segmented)
        elif isinstance(tf, ui.TextField):
            tf.alignment=ui.ALIGN_RIGHT
    return cell

dialogs._FormDialogController.tableview_cell_for_row = my_tableview_cell_for_row

diag = dialogs.form_dialog(title = 'Form Dialog', sections=form_list_of_sections)  
cvp

The last code is not correct if you scroll the dialog so the Segments are hidden and reshown back because the code adds a new subview each time. Let me some time to correct it.
That's the problem when you do this process in tableview_cell_for_row

cvp

Code corrected (and edited in the post), sorry for that

cvp

And if you want a dialog field as a button (radio, checkbox, ...), this code could help you.
You have to design the field with two images for your button, like:

sectionA_dicts.append(dict(type = 'text', title = 'My button',
key = 'button', button = ['iob:ios7_checkmark_32','iob:ios7_checkmark_outline_32'])) 

and the associated process:

def button_action(sender):
    global c
    sender.idx = 1 - sender.idx
    sender.image = ui.Image.named(sender.images[sender.idx])
    c.values[sender.name] = sender.idx

def my_tableview_cell_for_row(self,tv, section, row):
    global c
    c = self
    cell = self.cells[section][row]
    if len(cell.content_view.subviews) > 0:
        tf = cell.content_view.subviews[0]      # ui.TextField of value in row
        item = self.sections[section][1][row]   
        if 'segments' in item:
            # check if SegmentedControl already added
            for sv in cell.content_view.subviews:
                if type(sv) is ui.SegmentedControl:
                    return cell
            segmented = ui.SegmentedControl()
            segmented.name = cell.text_label.text
            segmented.action = segmented_action
            segmented.frame = tf.frame
            segmented.x = c.view.width - segmented.width - 8
            segmented.segments = item['segments']
            cell.content_view.add_subview(segmented)
        elif 'button' in item:
            # check if Button already added
            for sv in cell.content_view.subviews:
                if type(sv) is ui.Button:
                    return cell
            button = ui.Button()
            button.name = cell.text_label.text
            button.action = button_action
            button.frame = tf.frame
            button.width = button.height
            button.x = c.view.width - button.width - 8
            button.images = item['button']
            button.title = ''
            button.idx = 0
            button.image = ui.Image.named(button.images[button.idx])
            cell.content_view.add_subview(button)
        elif type(tf) is ui.TextField:
            tf.alignment=ui.ALIGN_RIGHT
    return cell 

cvp

and supporting color images in button:

.
.
.

sectionA_dicts.append(dict(type = 'text', title = 'My face',
key = 'face', button = ['emj:Smiling_1','emj:Disappointed']))
.
.
.
def button_action(sender):
    global c
    sender.idx = 1 - sender.idx
    sender.image = ui.Image.named(sender.images[sender.idx]).with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)
    c.values[sender.name] = sender.idx
.
.
.

            button.image = ui.Image.named(button.images[button.idx]).with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)
.
.
.

madivad

@cvp you are incredible...

I saw your first posts on the way to work and thought, “yeah, I have to get to that!” And now I log back and on and find this!!!! MUCH to digest!

I will be back (busy the next few days, but the kids are on holidays after tomorrow and I’ll slow down on the workload, so I should be able to get into this more)

Seriously, thanks for your contributions, you are awesome!

madivad

@cvp I finally got back to this, I got it all working as expected, the only issue I have is with the custom rows (My segmented control*, My button, and My face), they allow the cursor into the row label on the left hand side if the user touches near them.

I’ve spent a couple of hours going over the cradle and only minimally beginning to understand it :)

* the segmented control is ok in the custom/my_form_dialog and doesn’t allow the cursor in. It only allows the cursor when using the inbuilt dialog.

cvp

@madivad It is normal. When we create the new fields types in tableview_cell_for_row, the original TextField still exists. In the other case, the code deletes the TextField.

cvp

@madivad you can add these lines when you create a new field type
after

            cell.content_view.add_subview(segmented) 

Add

            cell.content_view.remove_subview(tf)
            del self.values[tf.name]
            del tf 
madivad

@cvp said:

        cell.content_view.remove_subview(tf)
        del self.values[tf.name]
        del tf

I knew it was something like that, I was giving it a go but hadn’t got it right.

Thanks.