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

how to access events of a specific QML control from c++

本文关键字:QML 控件 事件 C++ 访问      更新时间:2023-10-16

有没有办法从 c++ 访问 QML 控件(如按钮)的信号(如clicked())。假设我有该特定控件的内存地址。我只想模拟来自 c++ 代码的单击事件。

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

例:

我有一个矩形,我想从中获取 onWidthChanged 信号,并在我的类 ShapeTracker 中使用它,它可以跟踪形状何时更改或其他任何内容

在主.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();
 } 

然后在

主.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事件中发生的任何事情都可以很容易地放入任何其他属性中,例如

Rectangle {
       id: rect
       signal customClick(var var1)
       onCustomClick : {  console.log(var1);   } 
    }
   Item {
         rect.customClick(1);   
    }

简单的方法是手动调用所有接收插槽。但那将是乏味且容易出错的。

您可以尝试实现 QObject 的子类,该子类具有一个发出信号clicked()的插槽onClicked()并将其用作按钮和按钮控制的元素之间的填充程序。将按钮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();
};

纽扣希姆.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();
}

一些文件.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();

一些文件.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();
  }
}
我认为

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

如果你有一个QObject,你可以只打电话给信号,因为,AFAIR

public signals:
  void clicked();

由 MOC 扩展为

public:
  void clicked();