rawpython / remi

Python REMote Interface library. Platform independent. In about 100 Kbytes, perfect for your diet.
Apache License 2.0
3.48k stars 401 forks source link

How can I return value from "execute_javascript" #447

Closed m-kareem closed 3 years ago

m-kareem commented 3 years ago

Hi @dddomodossola,

I am trying to use confirm pop-up box via execute_javascript in my GUI. Something like this:

bt = gui.Button("pressMe")
bt.onclick.do(self.pressMe)

def pressMe(self, emitter):
       js = '''var txt; if (confirm("Press a button!")) {txt = "You pressed OK!"; console.log(txt)} else {txt = "You pressed Cancel!";}'''
       self.execute_javascript(js)

The code is working and when I press the button, the pop-up box appears. Now how can I get the return value and execute a function when the user press the OK button?

I am aware of dialog.confirm_dialog in remi, but since that appears in full screen rather than a pup-up box, I need to use the javascript way.

Thanks

dddomodossola commented 3 years ago

Hello @m-kareem, it is simple to code a popup with remi ;-)

import remi.gui as gui
from remi import start, App

class Popup(gui.VBox):
    def __init__(self, title, message, *args, **kwargs):
        gui.VBox.__init__(self, *args, **kwargs)
        self.css_display = "none"
        self.style['margin'] = 'auto'
        self.css_outline = "3px solid gray"

        self.append(gui.Label(title, style={'font-weight':'bold'}))
        self.append(gui.Label(message, style={'font-size':'15px'}))

        self.bt_confirm = gui.Button("Confirm")
        self.bt_confirm.onclick.do(self.onconfirm)
        self.append(self.bt_confirm)

    @gui.decorate_event
    def onconfirm(self, emitter):
        self.css_display = "none"
        return ()

    def show(self):
        self.css_position = "absolute"
        self.css_display = "flex"

class MyApp(App):
    def main(self):
        # creating a container VBox type, vertical (you can use also HBox or Widget)
        self.main_container = gui.VBox(width=300, height=200, style={'margin': '0px auto'})

        self.bt_show_popup = gui.Button("Show popup")
        self.bt_show_popup.onclick.do(lambda emitter:self.popup.show())

        self.main_container.append(self.bt_show_popup)

        self.popup = Popup("title", "message")
        self.main_container.append(self.popup)

        # returning the root widget
        return self.main_container

if __name__ == "__main__":
    # starts the webserver
    start(MyApp, address='0.0.0.0', port=0, start_browser=True, username=None, password=None)
m-kareem commented 3 years ago

Thanks a lot @dddomodossola for your prompt reply and kind support. This is exactly what I needed. However it would be very nice to see what solution you have for execute_javascript method and how you can return values from JS to remi.

Best,

dddomodossola commented 3 years ago

@m-kareem in order to send data back to python server you have to use the remi.sendCallbackParam from javascript. sendCallbackParams requires 3 arguments:

  1. The identifier of the widget or app that will handle the callback
  2. The callback name
  3. The parameters dictionary that will be send back to the callback.

Here is a working example ;-)

import remi.gui as gui
from remi import start, App

class MyApp(App):
    def main(self):
        # creating a container VBox type, vertical (you can use also HBox or Widget)
        main_container = gui.VBox(width=300, height=200, style={'margin': '0px auto'})

        bt = gui.Button("pressMe")
        bt.onclick.do(self.pressMe)

        main_container.append(bt)

        # returning the root widget
        return main_container

    def pressMe(self, emitter):
       js = '''var txt; if (confirm("Press a button!")) {txt = "You pressed OK!"; console.log(txt)} else {txt = "You pressed Cancel!";} var params={}; params['text_value'] = txt; remi.sendCallbackParam('%(listener_id)s','%(listener_function)s',params);'''%{'listener_id':str(id(self)),'listener_function':'mylistener'}
       self.execute_javascript(js)

    def mylistener(self, text_value):
        print("received: " + text_value)

if __name__ == "__main__":
    # starts the webserver
    start(MyApp, address='0.0.0.0', port=0, start_browser=True)
m-kareem commented 3 years ago

Thanks a lot @dddomodossola , this is a genius idea. However for some reason I did not get anything back in the terminal from "mylistener" function. The code is running without error.

Best,

dddomodossola commented 3 years ago

Maybe you are not using the latest remi version. Please try to update remi. Furthermore, please check for errors in the browser console. Kind Regards