如何从 C++ 访问特定 QML 控件的事件

Posted

技术标签:

【中文标题】如何从 C++ 访问特定 QML 控件的事件【英文标题】:how to access events of a specific QML control from c++ 【发布时间】:2013-11-13 15:48:10 【问题描述】:

有没有办法从 c++ 访问 QML 控件(例如按钮)的信号(例如 clicked())。假设我有那个特定控件的内存地址。我只是想从 C++ 代码中模拟一个点击事件。

【问题讨论】:

【参考方案1】:

简单。您只需在具有 QObject 基础的 C++ 对象中创建一个插槽,确保其注册为 QML 类型,然后在 QML 文档中“实例化”它,并使用 connect 通过 QML 将所需信号连接到 C++ 对象() 并从 C++ 端处理逻辑。

例子:

我有一个 Rectangle,我想从中获取 onWidthChanged 信号并在我的类 ShapeTracker 中使用它,该类跟踪形状何时发生变化或其他情况

在 main.cpp 中:

#include "shapetracker.h"

int main(int argc, char *argv[])


    QGuiApplication app(argc, argv);



  QQmlApplicationEngine engine;



   /*  this is where you register ShapeTracker as a QML type that can be 
        accessed through the QML engine even though its a C++ QObject */

    qmlRegisterType<ShapeTracker>("my_cpp_classes", 1, 0, "ShapeTracker");




  engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
   return app.exec();
  

然后在

main.qml

import QtQuick 2.6



/* import the C++ class you registered into this QML document */

import my_cpp_classes 1.0


Window 
    visible: true
    Item 


        /*  set up a parent item that has a signal which is exposed to 
             the ShapeTracker object and the Rectangle you want to track */
        id:  myRootItem    
        signal  rectangleChanged(var newWidth)



        /* Heres our special Rectangle from QML that will send a signal when
           its width changes */
        Rectangle 
           id: specialRectangle
            width:  250


         /*  send the signal rectangleChanged(width) from our parent item 
             root item whenever width changes
            */
            onWidthChanged:  function()  rectangleChanged(width);  

        


        /*  Special Button that when clicked enlarges the Rectangle but 
             10px */
        Button 
             id: mySpecialButton
             onClicked:    click_handler(mouse); 

             function click_handler(mouse): 
                  if (specialRectangle.width < 500) 
                      specialRectangle.width += 10;
             
        



        /* Heres the actual ShapeTracker instance, which exists 
          as a  QObject inside of the QML context, but has its methods 
          which   are declared in C++, and exposed to the QML engine */
        ShapeTracker 
            id: myShapeTracker


            /* similar to a constructor, but more like a callback */
           Component.onCompleted: 


           /*  connect our signal from the parent root Item  called 
              "rectangleChanged"  to the ShapeTracker's  Slot  called 
             "myRectangleChangeHandler"      */
                   myRootItem.rectangleChanged.connect(myShapeTracker.myRectangleChangeHandler);


              /* connect send_mouse_click to the click_handler of 
                 the button */                        
               myShapeTracker.send_mouse_click.connect(mySpecialButton.click_handler)

                

       

    

在 shapetracker.h 中,您只需添加一个名为 myRectangleChangeHandler 的新插槽,它就会在通过 QML 发送并通过 C++ 处理时接收该信号

class ShapeTracker : public QObject 
    Q_OBJECT
public:
    ShapeTracker(QObject *parent = 0 );

signal: 

    void send_mouse_click(QMouseEvent *event);


public slots:
    void myRectangleChangeHandler(QVariant newWidth) 
           /*  Perform a mouse click on our QML Object mySpecialButton 
               using QQuickItem::mousePressEvent  and sending it via 
               signal back to QML */


           QMouseEvent myEvent(QEvent::MouseButtonPress, QPointF(1,1), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);

          QMouseEvent* pressEvent = QQuickItem::mousePressEvent(&myEvent);

           emit send_mouse_click(pressEvent);


    

;


总之,

您将 C++ QObject 公开给 QML,然后您使用
  object.signal.connect(cppObject.desired_slot)  

为了连接它们——所有额外的东西都是为了一个功能示例,以防以后有人需要它

实际上,您甚至不需要此功能,因为 onClick 事件中发生的任何事情都可以很容易地放入任何其他属性中,例如 on

Rectangle 
       id: rect
       signal customClick(var var1)
       onCustomClick :   console.log(var1);    
    

   Item 
         rect.customClick(1);   
    

【讨论】:

【参考方案2】:

简单的方法是手动调用所有接收 SLOTS。但这会很乏味且容易出错。

您可以尝试实现一个 QObject 的子类,它有一个插槽onClicked(),它发出信号clicked(),并将其用作按钮和按钮控制的元素之间的垫片。将按钮clicked() 连接到新对象onClicked(),然后将新对象连接到原始接收器。然后调用onClicked() 将触发该行为。

这是一个很简单的例子,我没有通过编译器运行过。

ButtonShim.hpp

#include <QObject>
class ButtonShim : public QObject 
  Q_OBJECT

public:
  ButtonShim(QObject *parent = 0);
  virtual ~ButtonShim();

public slots:
  void onClicked();

signals:
  void clicked();
;

ButtonShim.cpp

#include "ButtonShim.hpp"

ButtonShim::ButtonShim(QObject *parent) : QObject(parent) 


ButtonShim::~ButtonShim() 


void ButtonShim::onClicked() 
  // All we do here is emit the clicked signal.
  emit clicked();

SomeFile.cpp

#include <bb/cascades/Button>
#include "ButtonShim.hpp"

...

ButtonShim * pButtonShim = new ButtonShim(pButton); // pButtonShim will live as long as pButton

bool c = connect(pButton, SIGNAL(clicked()), pButtonShim, SLOT(onClicked()));

c = connect(pButtonShim, SIGNAL(clicked()), pSomeObject, SLOT(onButtonClicked()));

...

// to simulate a click of pButton
pButtonShim->onClicked();

SomeFile.qml

// assuming ButtonShim has been exposed to QML from your application

...

attachedObjects: [
  ButtonShim 
    id: buttonShim
    onClicked: 
      clickedLabel.text = "I've been clicked";
    
  
]

...

Label 
  id: clickedLabel
  text: "I haven't been clicked"


Button 
  text: "Click Me"
  onClicked: 
    buttonShim.onClicked();
  

【讨论】:

感谢您的回复。我正在努力。但是如何从 C++ 代码中发出“clicked()”信号? 我添加了一些粗略的示例代码,省略了错误检查。可能存在一些语法错误。【参考方案3】:

我认为您可以查看tests 的代码。他们在那里从加载到引擎中的 QML 文件中获取对象。

如果你有一个 QObject,你可以调用 signal,因为,AFAIR

public signals:
  void clicked();

被moc扩展成

public:
  void clicked();

【讨论】:

以上是关于如何从 C++ 访问特定 QML 控件的事件的主要内容,如果未能解决你的问题,请参考以下文章

如何从 C++ 端连接到 QML 项目的 onClick 事件

如何从 C++ 访问 QML ListView 委托项目?

Qt 创建一个对触摸事件敏感的 QML 滑块

如何从 C++ 代码将 QML 对象转换为 QQuickWindow?

如何从 QML 访问嵌套的 QStandardItemModel 的项目?

如何创建仅允许访问 C++ 中的特定用户帐户的手动重置事件?