2

有人可以告诉我在第 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()

我知道我可能使用了错误的语法,但我已经尝试了大约 15 种变体,但仍然无法弄清楚。每条路由要么产生错误,要么更新失败。

(为了清楚起见,当在 JavaScript 中按下“w”时,我试图用字符串定义一个变量,它也会围绕一个蓝色方块移动。)

4

2 回答 2

1

关于错误

可能是您没有正确设置一些变量,即python_propertypy_callback,而是在代码中使用它们。

在此代码中,您将看到 python_property 正在使用,

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

这里还定义了一些其他函数py_callback,例如在您的代码上调用但从未定义过。

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

我怎么知道的?只需将您的所有代码复制到一个纯 html 文件中,然后打开控制台。然后当我遇到一些错误时,我知道在哪里寻找。这不是调试的好方法,但也要将其保留在 15 种以上的变体中:D。

在此处输入图像描述

在深入查看您的代码后,它表明您正在尝试访问一个名为py_callback的变量,该变量是js_callback_2函数的局部变量。因此它产生了错误。

将数据传递给python

要传递数据,可以使用 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])

现在,如果我之前在某个地方的 python 代码中定义了 somevar,那么一旦我调用该set_variable_value函数,它就会得到更新。如果我使用globals(),我可以从该函数修改任何全局变量。

要调用它,我们必须在 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;

结果如预期, 在此处输入图像描述

于 2018-04-14T23:00:56.550 回答
0

如果您粘贴第 118 行,它会有所帮助:

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

问题是 Python 端没有绑定到 javascript 的此类函数。您必须在 Python 中定义这样的函数并set_javascript_bindings通过调用将其绑定到函数中bindings.SetFunction

于 2018-04-15T03:24:17.893 回答