0%

QML与C++通信

在qml中调用C++中的函数与属性

我们首先定义一个继承自QObject的类,参考下述代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Segment :public QObject
{
Q_OBJECT //必须要有这个宏定义
Q_PROPERTY(QString image_path READ read_image_path WRITE set_image_path)
public:
Segment();

void set_image_path(QString path);
QString read_image_path();
Q_INVOKABLE void run_segment();
signals:
void segment_finished();//信号,分割完成,用以通知界面更新
protected:
QString image_path;//待分割图片路径
};

在Segment类中,有一个私有属性,image_path,要想在qml中能够访问这个属性,我们需要使用Q_PROPERTY,这也是一个宏。同时我们需要根据需求定义一些访问或者修改image_path属性的函数,例如我们这里只需要访问这个属性,或者根据用户选择修改这个属性,那么就需要定义读和写的两个函数,在Q_PROPERTY宏中,READ属性是必须的。这里的详细信息参阅Qt文档。
然后我们就可以在qml中去操作这个属性了,例如这里我们从文件对话框中获得选择文件的路径,但在这之前,我们首先需要注册,在main函数中添加下列代码:

1
2
qmlRegisterType<Segment>("Segment",1,0,"SegmentObject");
//4个参数分别是:import时的名称,主版本号,次版本号,在qml中的名称

这里注册分很多不同的方式,但是我还不太清楚各种方式之间的区别,有待更新。需要注意这里注册时的参数。然后,我们就可以在qml中使用了,下面列出部分代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//首先需要导入
import Segment 1.0
//然后,我们需要在根元素下创建一个组件
SegmentObject{
id: segment
}
//之后,我们就可以使用了,例如获得文件对话框选择的文件路径
FileDialog {
id: file_dialog1 //对话框的id
nameFilters: [ "Image files (*.jpg *.png)", "All files (*)" ]
//通过nameFilters来实现文件类型的筛选
title: "Please choose a file"
folder: shortcuts.home
onAccepted: { //点击了接受按钮之后的动作
console.log("You chose: " + file_dialog1.fileUrl)
file_path = file_dialog1.fileUrl
segment.image_path=file_path.slice(8) //除去file:///部分
}
onRejected: { //点击了取消按钮之后的动作
console.log("Canceled")
}
}

而如果我们要在qml中调用C++ 中的函数,则需要使用Q_INVOKABLE宏,并定义成public方法,例如我们上面面定义的run_segment()函数。类似于在qml中修改C++ 类的属性一样吗,我们就可以直接调用该方法,eg:

1
2
3
4
5
6
7
8
9
10
11
Menu{
title: qsTr("Segment")
Action{
id: run_birch_action
text: qsTr("Run BIRCH")
onTriggered: {
//to_do 调用C++模块执行分割
segment.run_segment()
}
}
}

C++发送信号,qml中槽函数响应

在上面的Segment类中,我们声明了信号segment_finished(),同时,在run_segment()函数中,执行完分割过程后,由emit宏发送该信号,而qml需要使用Connections来连接信号与槽。该部分代码如下:

1
2
3
4
5
6
Connections{
target: segment
onSegment_finished:{
console.log("reload image")
}
}

这里,onSegment_finished就是相应的槽函数。

update1 将C++对象注册到QML中

前面,我们使用C++ qmlRegisterType<Segment>("Segment",1,0,"SegmentObject");将C++类注册到了qml中,这里,介绍将C++对象注册到qml中的方法。
类的定义和前面一致,改动的部分在main函数中,具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

QGuiApplication app(argc, argv);
app.setOrganizationName("somename");
app.setOrganizationDomain("somename");
// QQuickView view;
// qmlRegisterType<Segment>("Segment",1,0,"SegmentObject");
Segment* segment=new Segment();
// QQmlApplicationEngine *engine=view.engine();
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("segment",segment);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.addImageProvider(QLatin1String("MyImage"),new MyImage());//反正这里就是得用new,如果用&My_image不可以
engine.load(url);

return app.exec();
}

这里,我们在main函数中声明了一个Segment对象,然后使用C++ setContextProperty() 函数将segment对象以”segment”的名字注册到了qml中。这样,我们就不需要再qml中导入Segment并添加控件了,即不需要下述代码:

1
2
3
4
import Segment 1.0
SegmentObject{
id: segment
}

但是,要想在qml中响应该对象的槽函数,则仍然需要下述代码实现信号与槽的连接。

1
2
3
4
5
6
Connections{
target: segment
onSegment_finished:{
console.log("reload image")
}
}