• Qt与H5的混合编程


    Qt与H5的混合编程

    自从Qt WebKit 被 Qt WebEngine替换后,就无法从C++ 直接访问html的元素了。以往很多通过WebKit 类来对网页内容的操作,要改为用JavaScript了。QWebEnginView加载网页后,要从C++ 代码中调用runJavaScript方法来调用页面的javascript脚本。Qt发明了一种C++ 和Javascript 的沟通机制,这种机制不仅能从页面获得元素,也能实现:从C++ 调用Js方法,从js调用C++ 的方法,从C++ 传递变量给js,从js传递变量给C++ 。以下是应用场景。

    1. js调用C++函数

    C++的代码需要实现一个类,类里包含将被js调用方法,且这个方法必须做为插槽。然后将这个类的对象注册到类QWebChannel的对象中,再将这个QWebChannel的对象赋值给QWebEngineView类的对象的page()方法。代码如下:

    1. class WebClass : public QObject 
    2. { 
    3. Q_OBJECT 
    4. public slots: 
    5. void jscallme() 
    6. { 
    7. QMessageBox::information(NULL,"jscallme","I'm called by js!"); 
    8. } 
    9. }; 
    10. WebClass *webobj = new WebClass(); 
    11. QWebChannel *channel = new QWebChannel(this); 
    12. channel->registerObject("webobj", webobj); 
    13. view->page()->setWebChannel(channel); 

    js端应该实例化一个QWebChannel来调用C++的jscallme方法。代码如下

    1. new QWebChannel(qt.webChannelTransport, 
    2. function(channel){ 
    3. var webobj = channel.objects.webobj; 
    4. window.foo = webobj; 
    5. }); 

    QWebChannel 是在qwebchannel.js中定义的,所以这个脚本应该首先在页面中加载。这个文件可在qt安装路径下的example目录找到,你也可以从文末的demo中获得。在上面的代码中,作为第二个参数传入的function里,C++ 的对象(channel.objects.webobj)赋值给了js的变量webobj,然后又将其赋值给了window.foo,以便你在其它地方可以引用这个C++ 的对象。这段代码执行后,js可以象下面这样调用C++ 的槽函数了:

    foo.jscallme();

    2. js传数据给C++

    既然知道了如何调用C++ 的方法,那就应该找到给C++ 传值的方法,比如,给函数传参。我们将C++ 端的jscallme方法做如下改造:

    1. void jscallme(const QString &datafromjs) 
    2. { 
    3. QMessageBox::information(NULL,"jscallme","I'm called by js!"); 
    4. m_data=datafromjs; 
    5. } 

    然后在js里的调用代码为:

    	foo.jscallme(somedata);

    注意参数前的const不能省略,否则会引发如下错误:

    Could not convert argument QJsonValue(string, “sd”) to target type .

    虽然数据可以做为函数的参数传递,但如果我们象下面的js这样给属性赋值会更方便,也符合习惯:

    	foo.someattribute="somedata";

    当代码执行后,我们期望somedata这个值会被赋值给C++ 类对象所暴露出来的的成员变量中。这可以通过在C++ 的类中声明一个qtproperty来实现:

    1. class WebClass : public QObject 
    2. { 
    3. Q_OBJECT 
    4. Q_PROPERTY(QString someattribute MEMBER m_someattribute) 
    5. public slots: 
    6. void jscallme() 
    7. { 
    8. QMessageBox::information(NULL,"jscallme","I'm called by js!"); 
    9. } 
    10. private: 
    11. QString m_someattribute; 
    12. }; 

    这样你就可以在js中执行foo.someattribute="somedata",然后C++对象中的m_someattribute的值就变为"somedata"了。

    3. C++传数据给js

    可以通过信号将数据从C++ 传给js。我们触发一个带参数的信号,这个参数就是我们将要传递的数据。js必须将这个信号连接到一个function以接收这个数据。

    1. class WebClass : public QObject 
    2. { 
    3. Q_OBJECT 
    4. Q_PROPERTY(QString someattribute MEMBER m_someattribute) 
    5. public slots: 
    6. void jscallme() 
    7. { 
    8. QMessageBox::information(NULL,"jscallme","I'm called by js!"); 
    9. } 
    10. void setsomeattribute(QString attr) 
    11. { 
    12. m_someattribute=attr; 
    13. emit someattributeChanged(m_someattribute); 
    14. } 
    15. signals: 
    16. void someattributeChanged(QString & attr); 
    17. private: 
    18. QString m_someattribute; 
    19. }; 

    javascript代码:

    1. var updateattribute=function(text) 
    2. { 
    3. $("#attrid").val(text); 
    4. } 
    5. new QWebChannel(qt.webChannelTransport, 
    6. function(channel){ 
    7. var webobj = channel.objects.webobj; 
    8. window.foo = webobj; 
    9. webobj.someattributeChanged.connect(updateattribute); 
    10. }); 

    这行代码webobj.someattributeChanged.connect(updateattribute) 将C++的信号函数someattributeChanged 和js的函数updateattribute连接起来了。注意,updateattribute只接收了一个text参数,而且没有在connet方法中提供这个参数值。实际上,在信号接收到这个值前,我们都不知道传递给updateattribute的参数值。信号伴随其参数"attr"出现,而其参数会传递给updateattribute的参数"text"。这时,如果调用 webobj->setsomeattribute(“hello”),就会在html端看到id为 “#attrid” 的html元素的值被改为“hello”。虽然在上面的例子中,将变量m_someattribute绑定到了qt的属性someattribute,但这其实并不是必须的。信号机制自己就能发现传递的数据。
    这里可以在声明someattribute属性时,通过添加NOTIFY 参数来简化一些事情。

    1. class WebClass : public QObject 
    2. { 
    3. Q_OBJECT 
    4. Q_PROPERTY(QString someattribute MEMBER m_someattribute NOTIFY someattributeChanged) 
    5. public slots: 
    6. void jscallme() 
    7. { 
    8. QMessageBox::information(NULL,"jscallme","I'm called by js!"); 
    9. } 
    10.  
    11. signals: 
    12. void someattributeChanged(QString & attr); 
    13. private: 
    14. QString m_someattribute; 
    15. }; 

    这样,在某段C++ 代码中调用 weobj->setProperty("someattribute","hello") 时,信号“someattributeChanged”就会自动触发,并且我们的页面也会得到更新。

    4. C++调用js的function

    相对于从js中调用C++ 方法,从C++ 中调用js的function显得更直接一些。直接运行“runJavaScript”方法并将function作为参数传递,如下:

    view->page()->runJavaScript("jsfun();",[this](const QVariant &v) { qDebug()<<v.toString();});

    假定jsfun已经在页面中定义了,否则,应该直接在字符串参数中定义。返回值异步传递给了作为参数v的lambda表达式。
    现在回到文章开始提出的问题:在C++中如何获得页面的元素?答案如下:

    1. view->page()->runJavaScript("function getelement(){return $('#elementid').val();} getelement();",[this](const QVariant &v) { qDebug()<<v.toString();}); 

    脚本中用了jQuery方法,所以必须保证jQuery在页面中正常被引入。当然,也可以使用其它js框架,例如vue。

    示例下载 (代码是Qt在Deepin 下实现的,Windows用户请自行转换工程)

    本文参考:Communication between C++ and Javascript in Qt WebEngine

  • 相关阅读:
    C++中两种获取UUID的方法(编程)
    在python中发送自定义消息
    lib,dll的位置
    GetWindowText
    SuspendThread and ResumeThread
    创建线程检查按钮的状态
    C++检测句柄的权限
    POJ2186 强联通
    POJ2186 强联通
    POJ 1201 差分约束(集合最小元素个数)
  • 原文地址:https://www.cnblogs.com/sammy621/p/15706514.html
Copyright © 2020-2023  润新知