简体   繁体   English

尝试在pythoncef中调用JS时我做错了什么?

[英]What am I doing wrong while trying to call JS in pythoncef?

Can someone tell me what I'm doing wrong while performing this JavaScript cefpython callback on line 118? 有人可以告诉我在第118行执行此JavaScript cefpython回调时我做错了什么吗?

# Tutorial example. Doesn't depend on any third party GUI framework.
# Tested with CEF Python v56.2+

from cefpython3 import cefpython as cef
import base64
import platform
import sys
import threading

# HTML code. Browser will navigate to a Data uri created
# from this html code.
HTML_code = """
<!DOCTYPE html>
<html>
<head>
   <style type="text/css">
   body,html { font-family: Arial; font-size: 11pt; }
   div.msg { margin: 0.2em; line-height: 1.4em; }
   b { background: #ccc; font-weight: bold; font-size: 10pt;
       padding: 0.1em 0.2em; }
   b.Python { background: #eee; }
   i { font-family: Courier new; font-size: 10pt; border: #eee 1px solid;
       padding: 0.1em 0.2em; }
   </style>

   <script>
   function js_print(lang, event, msg) {
       msg = "<b class="+lang+">"+lang+": "+event+":</b> " + msg;
       console = document.getElementById("console")
       console.innerHTML += "<div class=msg>"+msg+"</div>";
   }

   function js_callback_1(ret) {
       js_print("Javascript", "html_to_data_uri", ret);
   }

   function js_callback_2(msg, py_callback) {
       js_print("Javascript", "js_callback", msg);
       py_callback("String sent from Javascript XY5C");
   }

   window.onload = function(){

       js_print("Javascript", "python_property", python_property);
       html_to_data_uri("test", js_callback_1);
       external.test_multiple_callbacks(js_callback_2);
   };
   </script>
</head>


<body>
<div id="console"></div>
 <canvas id="myCanvas" width="800" height="800"></canvas>


   <script type="text/javascript">
   function js_callback_1(ret) {
       js_print("Javascript", "html_to_data_uri", ret);
   }
   function js_callback_2(msg, py_callback) {
       js_print("Javascript", "js_callback", msg);
       py_callback("String sent from Javascript 01XC");
   }

   window.onload = function(){
       js_print("Javascript", "window.onload", "Called");
       js_print("Javascript", "python_property", python_property);
       js_print("Javascript", "navigator.userAgent", navigator.userAgent);
       js_print("Javascript", "cefpython_version", cefpython_version.version);
       html_to_data_uri("test", js_callback_1);
       external.test_multiple_callbacks(js_callback_2);
   };
       (function() {
 var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
   window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
 window.requestAnimationFrame = requestAnimationFrame;
})();

//event listener
window.addEventListener("keydown", onKeyDown, false);
window.addEventListener("keyup", onKeyUp, false);

function onKeyDown(event) {
 var keyCode = event.keyCode;
 switch (keyCode) {
   case 68: //d
     keyD = true;
     break;
   case 83: //s
     keyS = true;
     break;
   case 65: //a
     keyA = true;
     break;
   case 87: //w
     keyW = true;

     break;
 }
}

function onKeyUp(event) {
 var keyCode = event.keyCode;

 switch (keyCode) {
   case 68: //d
     keyD = false;
     break;
   case 83: //s
     keyS = false;
     break;
   case 65: //a
     keyA = false;
     break;
   case 87: //w
     keyW = false;
     py_callback("String sent from Javascript 89JX");
     break;
 }
}

//neccessary variables
var tickX = 10;
var tickY = 10;

var keyW = false;
var keyA = false;
var keyS = false;
var keyD = false;

//main animation function
function drawStuff() {
 window.requestAnimationFrame(drawStuff);
 var canvas = document.getElementById("myCanvas");
 var c = canvas.getContext("2d");

 c.clearRect(0, 0, 800, 800);
 c.fillStyle = "blue";
 c.fillRect(tickX, tickY, 100, 100);

 if (keyD == true) {
   tickX += 1;
 }
 if (keyS == true) {
   tickY += 1;
 }
 if (keyA == true) {
   tickX--;
 }
 if (keyW == true) {
   tickY--;
 }
}
window.requestAnimationFrame(drawStuff);
   </script>

</body>

</html>
"""

#<body>
#    <h1>Tutorial example</h1>
#    <div id="console"></div>
#</body>
def main():
    check_versions()
    sys.excepthook = cef.ExceptHook  # To shutdown all CEF processes on error
    cef.Initialize()
    set_global_handler()
    browser = cef.CreateBrowserSync(url=html_to_data_uri(HTML_code),
                                    window_title="Tutorial")
    set_client_handlers(browser)
    set_javascript_bindings(browser)
    cef.MessageLoop()
    cef.Shutdown()

def check_versions():
    print("[tutorial.py] CEF Python {ver}".format(ver=cef.__version__))
    print("[tutorial.py] Python {ver} {arch}".format(
          ver=platform.python_version(), arch=platform.architecture()[0]))
    assert cef.__version__ >= "56.2", "CEF Python v56.2+ required to run this"


def html_to_data_uri(html, js_callback=None):
    # This function is called in two ways:
    # 1. From Python: in this case value is returned
    # 2. From Javascript: in this case value cannot be returned because
    #    inter-process messaging is asynchronous, so must return value
    #    by calling js_callback.
    html = html.encode("utf-8", "replace")
    b64 = base64.b64encode(html).decode("utf-8", "replace")
    ret = "data:text/html;base64,{data}".format(data=b64)
    if js_callback:
        js_print(js_callback.GetFrame().GetBrowser(),
                 "Python", "html_to_data_uri",
                 "Called from Javascript. Will call Javascript callback now.")
        js_callback.Call(ret)
    else:
        return ret


def set_global_handler():
    # A global handler is a special handler for callbacks that
    # must be set before Browser is created using
    # SetGlobalClientCallback() method.
    global_handler = GlobalHandler()
    cef.SetGlobalClientCallback("OnAfterCreated",
                                global_handler.OnAfterCreated)


def set_client_handlers(browser):
    client_handlers = [LoadHandler(), DisplayHandler()]
    for handler in client_handlers:
        browser.SetClientHandler(handler)


def set_javascript_bindings(browser):
    external = External(browser)
    bindings = cef.JavascriptBindings(
            bindToFrames=False, bindToPopups=False)
    bindings.SetProperty("python_property", "python_property defined in Python #X1HQ")
    bindings.SetProperty("cefpython_version", cef.GetVersion())
    bindings.SetFunction("html_to_data_uri", html_to_data_uri)
    bindings.SetObject("external", external)
    browser.SetJavascriptBindings(bindings)

def js_print(browser, lang, event, msg):
    # Execute Javascript function "js_print"
    browser.ExecuteFunction("js_print", lang, event, msg)

class GlobalHandler(object):
    def OnAfterCreated(self, browser, **_):
        # DOM is not yet loaded. Using js_print at this moment will
        # throw an error: "Uncaught ReferenceError: js_print is not defined".
        # We make this error on purpose. This error will be intercepted
        # in DisplayHandler.OnConsoleMessage.
        js_print(browser, "Python", "OnAfterCreated",
                 "This will probably never display as DOM is not yet loaded")
        # Delay print by 0.5 sec, because js_print is not available yet
        args = [browser, "Python", "OnAfterCreated",
                "(Delayed) Browser id="+str(browser.GetIdentifier())]
        threading.Timer(0.5, js_print, args).start()


class LoadHandler(object):
    def OnLoadingStateChange(self, browser, is_loading, **_):
        # This callback is called twice, once when loading starts
        # (is_loading=True) and second time when loading ends
        # (is_loading=False).
        if not is_loading:
            # Loading is complete. DOM is ready.
            js_print(browser, "Python", "OnLoadingStateChange",
                     "Loading is complete")


class DisplayHandler(object):
    def OnConsoleMessage(self, browser, message, **_):
        # This will intercept js errors, see comments in OnAfterCreated
        if "error" in message.lower() or "uncaught" in message.lower():
            # Prevent infinite recurrence in case something went wrong
            if "js_print is not defined" in message.lower():
                if hasattr(self, "js_print_is_not_defined"):
                    print("Python: OnConsoleMessage: "
                          "Intercepted Javascript error: "+message)
                    return
                else:
                    self.js_print_is_not_defined = True
            # Delay print by 0.5 sec, because js_print may not be
            # available yet due to DOM not ready.
            args = [browser, "Python", "OnConsoleMessage",
                    "(Delayed) Intercepted Javascript error: <i>{error}</i>"
                    .format(error=message)]
            threading.Timer(0.5, js_print, args).start()
            browser.ExecuteJavascript("alert('The value for \"python_property\" is: ' + python_property);")


class External(object):
    def __init__(self, browser):
        self.browser = browser

    def test_multiple_callbacks(self, js_callback):
        """Test both javascript and python callbacks."""
        js_print(self.browser, "Python", "test_multiple_callbacks",
                 "Called from Javascript. Will call Javascript callback now.")

        def py_callback(msg_from_js):
            js_print(self.browser, "Python", "py_callback", msg_from_js)
        js_callback.Call("String sent from Python H1T7", py_callback)

if __name__ == '__main__':
    main()

I understand that I'm likely using the wrong syntax, but I've tried about 15 variations and still can't figure it out. 我知道我可能使用了错误的语法,但是我尝试了15种变体,但仍然无法弄清楚。 Every route either produces errors or fails to update. 每个路由都会产生错误或无法更新。

(To be clear, I am trying to define a variable with a string when 'w' is pressed in JavaScript, which also moves around a blue square.) (要清楚,我正在尝试在JavaScript中按下'w'时用字符串定义一个变量,该变量也会在蓝色方块周围移动。)

About the error 关于错误

It could be that you are not setting some variables properly ie python_property and py_callback , but using them on the code. 可能是您没有正确设置一些变量, py_callback python_propertypy_callback ,但在代码上使用了它们。

Here on this code you'll see python_property is being set using, 下面这个代码,你会看到python_property正在使用设置,

bindings.SetProperty("python_property", "This property was set in Python")

Also here some other functions like py_callback are defined, which are called on your code but never defined. 同样在这里定义了其他一些函数,例如py_callback ,这些函数在您的代码上被调用但从未定义。

def py_callback(msg_from_js):
        js_print(self.browser, "Python", "py_callback", msg_from_js)

How did I know this? 我怎么知道的 Simply I copied all of your code in a plain html file and then opened the console. 只需将所有代码复制到纯HTML文件中,然后打开控制台即可。 Then when I got some errors, I knew where to look for. 然后,当我遇到一些错误时,我知道了要寻找的地方。 That's not a good way to debug, but keep that in your 15+ variations too :D . 那不是调试的好方法,但是也要把它保留在15种以上的版本中:D。

在此处输入图片说明

After a deep look into your code, it shows you are trying to access a variable called py_callback which is a local variable to the js_callback_2 function. 对代码进行深入研究后,它表明您正在尝试访问一个名为py_callback变量,该变量是js_callback_2函数的局部变量。 Thus it's producing the errors. 因此,它会产生错误。

Passing data to python 将数据传递给python

To pass data, you can use a function on the External class, which can be called and be used to pass data. 要传递数据,可以在External类上使用一个函数,该函数可以被调用并用于传递数据。

somevar = 'whatever'
class External(object):
    def __init__(self, browser):
        self.browser = browser

    def set_variable_value(self, key, value):
        somevar = value;
        print(somevar)
        # globals()[key] = value;
        # print(globals()[key])

Now, if I've defined somevar earlier in the python code somewhere, it will get updated once I call the set_variable_value function. 现在,如果我在python代码的较早地方定义了somevar,则一旦我调用set_variable_value函数,它将被更新。 If I use globals() , I can modify any global variable from that function. 如果使用globals() ,则可以从该函数修改任何全局变量。

To call it, we have to pass in 2 params in the js code rather than 3 defined, because cefpython will use up one param. 要调用它,我们必须在js代码中传入2个参数,而不是定义3个参数,因为cefpython将用完一个参数。

case 87: //w
  keyW = false;
  external.test_multiple_callbacks(js_callback_2);
  external.set_variable_value('hello','world'); // <-- notice param count
  break;

Result is as intended, 结果符合预期, 在此处输入图片说明

It would have helped if you pasted line 118: 如果您粘贴第118行,将会有所帮助:

py_callback("String sent from Javascript 89JX");

The problem is that there is no such function binded to javascript on the Python side. 问题是在Python端没有绑定到javascript的函数。 You have to define such function in Python and bind it in set_javascript_bindings function by calling bindings.SetFunction . 您必须在Python中定义此类函数,然后通过调用bindings.SetFunctionset_javascript_bindings函数中将其bindings.SetFunction

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM