Forum Archive

Action being triggered twice

robinsiebler112

I have 2 different controls on the same form calling the same action. The 2 ways of triggering the action are a) Pressing enter after entering text into the textfield and b) clicking the Select button. Unfortunately, if you click the Select button, both actions are being triggered and I'm not sure how to fix this. The full code is available on github

The action being triggered twice is self.process_speak_request which is set once in a form and once below.

class TextDelegate(object):

def textfield_did_change(self, textfield):
    view = textfield.superview
    button = view['button_select']
    button.enabled = textfield.text != ''

class Menu:

def prompt_speak(self, sender):
    """Prompt the user for the task(s) to speak."""

    self.prompt_dialog = ui.load_view('dialogs/speak_task_number')
    self.prompt_dialog['button_select'].enabled = False
    td = TextDelegate()
    self.prompt_dialog['textfield1'].delegate = td
    self.prompt_dialog["segmentedcontrol1"].action = self.display_speak_options
    self.prompt_dialog['textfield1'].begin_editing()
    self.prompt_dialog['textfield1'].action = self.process_speak_request
    self.prompt_dialog.present('popover', popover_location=(500, 500))

def display_speak_options(self, sender):
    """Display the controls to enter a number"""

    if self.prompt_dialog["segmentedcontrol1"].selected_index == 0:
        self.prompt_dialog["label1"].hidden = False
        self.prompt_dialog["textfield1"].hidden = False
        if self.prompt_dialog["textfield1"].text == '':
            self.prompt_dialog['button_select'].enabled = False
        else:
            self.prompt_dialog["button_select"].enabled = True
    else:
        self.prompt_dialog["label1"].hidden = True
        self.prompt_dialog["textfield1"].hidden = True
        self.prompt_dialog['button_select'].enabled = True

def enable_select(self, sender):
    """Enable the Select button after a task # has been provided."""

    self.prompt_dialog['button_select'].enabled = True
    self.main_view.set_needs_display()

def process_speak_request(self, sender):
    """""Determine which task(s) to recite"""

    recite = self.prompt_dialog["segmentedcontrol1"].selected_index
    if recite == 0:
        task_id = self._validate_task_id(self.prompt_dialog['textfield1'].text)
        if task_id:
            self.prompt_dialog.close()
            self.current_task = self.tasklist._find_task(task_id)
            self.speak_task(self.current_task)
        else:
            self.prompt_dialog['textfield1'].text = ''
            self.prompt_dialog['button_select'].enabled = False
    else:
        self.prompt_dialog.close()
        for task in self.tasklist.tasks:
            self.speak_task(task)
    speech.say('Recitation complete.', self.language, self.speech_rate)

def speak_task(self, task):
    """""Recite the provided task"""

    if not task:
        return
    if len(task.tags) > 0:
        fmt = "Task number {}, priority: {}, {}, This task has the following tags: {}"
        msg = fmt.format(task.id, task.priority, task.note, ' and '.join(task.tags.split()))
    else:
        fmt = "Task number {}, priority: {}, {}, This task does not have any tags."
        msg = fmt.format(task.id, task.priority, task.note)
    speech.say(msg, self.language, self.speech_rate)
JonB

when you call display_speak_options, you begin editing the text field. Since you set the action of the textfield, whenever you stop editing, it will trigger. I suspect if you click on another control, rather than pressing enter, it will speak too.
So once is from changing focus which ends editing, and the second is from the button itself.

Instead of using action for the textfield, I think you want to instead use the textfield_should_return method of the delegate, which is only is called when you press Return, not when you change focus. That's not obvious from the docs, but since this is a wrapper around the native library, you can get descriptions of these events from apple

robinsiebler112

How exactly would I do that? I added the below code to the TextDelegate() function, but it does nothing.

def textfield_should_return(self, textfield):
    return True
omz

@robinsiebler You'd basically trigger the action from the textfield_should_return method, and return True afterwards.

def textfield_should_return(self, textfield):
    self.process_speak_request(None)
    return True

(of course, you also have to remove the text field's action, as suggested by JonB)

robinsiebler112

Now I have the below and I get the following error: AttributeError: 'TextDelegate' object has no attribute 'process_speak_request

class TextDelegate(object):

    def textfield_did_change(self, textfield):
        view = textfield.superview
        button = view['button_select']
        button.enabled = textfield.text != ''

    def textfield_should_return(self, textfield):
        self.process_speak_request(None)              <-- AttributeError: ...
        return True
ccc

Don't make a special TextDelegate class but instead name your main class (the one that does have a process_speak_request() method) as the delegate. Then move the TextDelegate methods over to that main class.

robinsiebler112

Thanks! I got that to work.