Forum Archive

Can't show an alert?

ruimac

I'm trying to show an alert with console.alert
But I keep getting an error saying: AssertionError: Cannot show alert from main UI thread
So, can't I show an alert anywhere I need to?
How can I show alerts?

MatthiasLehner

You have to decorate the function you are trying to show the alert from with "@ui.in_background".

@ui.in_background
def your_function(sender):
   console.alert()
ruimac

Well, I did that and the alert only shows when I close my View (in the top left X icon).

ruimac

My function is like this:

@ui.in_background
def alert(message):
    alert_result=console.alert('Warning',message, button1='OK')
    return

And I call it like this:

alert('The message to show in the alert')
ruimac

Is there any particular place where I should place that function for it to work?
I still find it confusing why can't we simply show an alert wherever and whenever we want?

MatthiasLehner

I unluckily am not able to reproduce your problem, therefore I can just make assumptions. Do you call any functions beyond the "alert ('The message to show in the alert')" call that might interfere with the ui? This might lead to unexpected behavior as your alert and anything you call beyond console.alert run on different threads. Do you maybe have any calls beyond this, that access the console?

ruimac

I have no other console access.
However, the alert function is being called from inside an action assigned to a TextField.

MatthiasLehner

That usually shouldn't be the problem then. Would you mind posting a little more of your code to give me more insight on what might be causing the problem ?

ruimac

Well, here is a snippet of the function that calls the alert:

def field_edit(sender):
    global current_player
    global lines
    global rows
    global scores
    global totals
    global finished
    global n_players
    global scores_grid
    global combs
    global info

    size=lines*rows
    start=current_player*size

    display_grid()

    for x in range(lines):

        last=-1
        for y in range(rows-1,-1,-1):
            value1=scores[start+(x*rows)+y]
            if value1!=0:
                last=y
                break

        if last!=-1:
            for y in range(last,-1,-1):
                value1=scores[start+(x*rows)+y]
                if value1==0:
                    alert('Esse valor não pode ser colocado nessa posição.')
                    return

This function is assigned to a few TextField items so that, whenever I type something in there and press Enter, the function is executed.

In the main function, a new View is created and the TextField items reside in that View.
My main function ends with a my_view.wait_modal() so that the view doesn't close (if this is important).

ruimac

Oh, the display_grid function updated some TextField items in my view, but not the ones whose field_edit function is attached to.

MatthiasLehner

I could reproduce your error with my_view.wait_modal() at the end, therefore I assume it to be the problem. As soon as you leave this line out it works. Can you by any chance restructure your code in a way to make .wait_modal() not necessary?

JonB

Think about what wait_modal does. It causes the current thread, in this case the main thread, to wait until the dialog is closed.

Think about what in_background does.. It calls a function on the main thread, instead of the ui thread. The name may therefore be a little misleading. So, your program main starts, then the thread sits and waits for the view to close. It can't do anything else until that wait_modal unblocks. Meanwhile, your ui thread is operating, and then tries to background a call to console.alert. That call just gets queued up until wait modal finishes. For instance if you have alert tied to a button press, and press the button 5 times, then close your view, the alert will come up 5 times after you quit.

Since your are just trying to display a message, console.hud_alert probably is what you really want, as it does not require backgrounding to be called from the main ui thread (it goes away on its own after a few seconds, and does not block while doing so).

If you really want to use the interactive bits of console.alert, see the following example which calls console.alert from a threading Timer. This allows you to have the main thread waiting on the modal close, while still popping up alert. The first button is what you tried, and does not work until the view is closed. The second button works as expected, except when the view is presented as a full screen view, in which case presumably the console.alert is still shown, you just can't see it.

#demonstrate using threading to call console from a running ui that is wait_modal'ing'
import ui, console
from threading import Timer

def myinbackground(f):
    Timer(0.25,f).start() # delay of 0 does lock ui, some small delay seems to be needed

def alert():
    try:
        pressed=console.alert('hello!','world','ok')
        console.hud_alert('got OK')
    except KeyboardInterrupt:
        console.hud_alert('got keyboardinterrupt')

@ui.in_background
def button_tapped1(sender=None):
    alert()    #start alert on main thread

def button_tapped2(sender=None):
    myinbackground(alert) # start alert in new thread

def setup():
    view = ui.View()
    view.name = 'Demo'
    view.background_color = 'white'
    view.width=200
    view.height=200
    button = ui.Button(title='ui.in_background')
    button.center = (view.width * 0.5, view.height * 0.25)
    button.flex = 'LRTB'
    button.action = button_tapped1

    button2 = ui.Button(title='myinbackground!')
    button2.center = (view.width * 0.5, view.height * 0.5)
    button2.flex = 'LRTB'
    button2.action = button_tapped2

    view.add_subview(button)
    view.add_subview(button2)
    return view

def main():
    view=setup()
    view.present('panel')   # sheet, popover, or panel work, not full_screen
    view.wait_modal()
main()
ruimac

Well, I create a view and I want to keep it visible all the time.
I will see if I can restructure my code taking you example into account.
Thank you very much :-)

JonB

You don't need to wait_modal to keep a view on the screen. Wait_modal would be used if you want to pop up a menu for example! then have your main script wait for the user to choose something.

It seems like you just wanted to display a notification, in which hud alert is the best option.