0
点赞
收藏
分享

微信扫一扫

Qt6中C++与QML混合编程--教程(2)

颜路在路上 2022-08-25 阅读 43

参考文档:​​https://doc.qt.io/qt-6/qtqml-tutorials-extending-qml-example.html#chapter-2-connecting-to-c-methods-and-signals​​

回顾

上一章,介绍了如何从C++中导出新类型给QML使用!实现步骤:

  • 定义C++类型,此类要求继承自QObject类,并使用Q_OBJECT宏,用Q_PROPERTY声明QML属性
  • 设置qmake : CONFIG += qmltypes QML_IMPORT_NAME = Charts ...
  • 在QML中导入与使用 Charts

import Charts 1.0
...
PieChart {
id: aPieChart
anchors.centerIn: parent
width: 100; height: 100
name: "A simple pie chart"
color: "red"
}
...

因为第2步中要设置qmake,所以中途去做了个支线任务--使用qmake: ​​https://blog.51cto.com/u_12072082/5616418​​

连接到C++的函数与信号

实现目标

  • 调用函数​​clearChart()​​​删除图形,发送​​chartCleared​​信号
  • 在qml中可以访问​​clearChart​​​函数,并接收到 ​​chartCleared​​信号

import Charts 1.0
import QtQuick 2.0

Item {
width: 300; height: 200

PieChart {
id: aPieChart
anchors.centerIn: parent
width: 100; height: 100
color: "red"

onChartCleared: console.log("The chart has been cleared")
}

MouseArea {
anchors.fill: parent
onClicked: aPieChart.clearChart()
}

Text {
anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter; bottomMargin: 20 }
text: "Click anywhere to clear the chart"
}
}

Qt6中C++与QML混合编程--教程(2)_qt

实现

在C++类中添加​​clearChart函数和 chartCleared​​信号

class PieChart : public QQuickPaintedItem
{
...
public:
...
Q_INVOKABLE void clearChart();

signals:
void chartCleared();
...
};

Q_INVOKABLE的使用使得cleararchart()方法可用于Qt元对象系统,反过来,也可用于QML。

另外你也可以声明其为槽函数,因为槽函数在QML中也可以被调用。

实现clearChart函数:

void PieChart::clearChart()
{
setColor(QColor(Qt::transparent));
update();

emit chartCleared();
}

信号函数是不需要实现的...

现在可以在QML代码调用clearChart函数,处理​​chartCleared​​信号了

添加属性绑定

import Charts 1.0
import QtQuick 2.0

Item {
width: 300; height: 200

Row {
anchors.centerIn: parent
spacing: 20

PieChart {
id: chartA
width: 100; height: 100
color: "red"
}

PieChart {
id: chartB
width: 100; height: 100
color: chartA.color
}
}

MouseArea {
anchors.fill: parent
onClicked: { chartA.color = "blue" }
}

Text {
anchors { bottom: parent.bottom; horizontalCenter: parent.horizontalCenter; bottomMargin: 20 }
text: "Click anywhere to change the chart color"
}
}

第26行,点击之后,希望可以把颜色改成蓝色...

现在的属性是这样的:

class PieChart : public QQuickPaintedItem
{
//![0]
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(QColor color READ color WRITE setColor)
QML_ELEMENT
...
}

要改成这样:

class PieChart : public QQuickPaintedItem
{
...
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
public:
...
signals:
void colorChanged();
...
};

  1. color 属性添加了 NOTIFY 选项
  2. 声明信号 colorChanged

void PieChart::setColor(const QColor &color)
{
if (color != m_color) {
m_color = color;
update(); // repaint with the new color
emit colorChanged();
}
}

检查color != m_color之后再发出信号这很重要,这确保了信号不会不必要地发出,也防止了在其他类型响应值变化时发生循环。

绑定对QML来说非常重要,所以如果属性有写操作,NOTIFY 必须加上。

使用自定义的属性类型

可以导出一个int类型:

// C++
class PieChart : public QQuickPaintedItem
{
Q_PROPERTY(int chartId READ chartId WRITE setChartId NOTIFY chartIdChanged)
...

public:
void setChartId(int chartId);
int chartId() const;
...

signals:
void chartIdChanged();
};

// QML
PieChart {
...
chartId: 100
}

除了int外,还可导出处理类型的属性,如QColor,QSize...都支持自动转换, ​​Data Type Conversion Between QML and C++​​

使用自定义类型 PieSlice:

import Charts 1.0
import QtQuick 2.0

Item {
width: 300; height: 200

PieChart {
id: chart
anchors.centerIn: parent
width: 100; height: 100

pieSlice: PieSlice {
anchors.fill: parent
color: "red"
}
}

Component.onCompleted: console.log("The pie is colored " + chart.pieSlice.color)
}

如果是自定类型,不支持默认的QML的默认转换所以我们需要把这个类型注册到QML引擎上。

PieSlice是一个C++中定义的类型,和PieChart一样,PieSlice继承自QQuickPaintedItem并且用Q_PROPERTY声明属性

class PieSlice : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor)
QML_ELEMENT

public:
PieSlice(QQuickItem *parent = nullptr);

QColor color() const;
void setColor(const QColor &color);

void paint(QPainter *painter) override;

private:
QColor m_color;
};
// 重载函数的实现
void PieSlice::paint(QPainter *painter)
{
QPen pen(m_color, 2);
painter->setPen(pen);
painter->setRenderHints(QPainter::Antialiasing, true);
painter->drawPie(boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16);
}

PieChart中把pPieSlice作为PieChart的属性暴露给QML

class PieSlice;

//![0]
class PieChart : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(PieSlice* pieSlice READ pieSlice WRITE setPieSlice)
//![0]
Q_PROPERTY(QString name READ name WRITE setName)
Q_MOC_INCLUDE("pieslice.h")
QML_ELEMENT
//![1]
public:
//![1]

PieChart(QQuickItem *parent = nullptr);

QString name() const;
void setName(const QString &name);

//![2]
PieSlice *pieSlice() const;
void setPieSlice(PieSlice *pieSlice);
//![2]

private:
QString m_name;
PieSlice *m_pieSlice;

//![3]
};
//![3]

//![0]
void PieChart::setPieSlice(PieSlice *pieSlice)
{
m_pieSlice = pieSlice;
pieSlice->setParentItem(this);
}
//![0]

  • color 属性被替换成了PieSlice* pieSlice
  • paint函数没了
  • 第38行pieSlice->setParentItem(this);很重要。

qmake设置

CONFIG += qmltypes
QML_IMPORT_NAME = Charts
QML_IMPORT_MAJOR_VERSION = 1

和上一小节一样,没有变化...

在同一个工程中,qmake导出qml类型的配置只需要写一次,同一个工程中导出的类型都在同一个命名空间下。

使用列表属性

import Charts 1.0
import QtQuick 2.0

Item {
width: 300; height: 200

PieChart {
anchors.centerIn: parent
width: 100; height: 100

slices: [
PieSlice {
anchors.fill: parent
color: "red"
fromAngle: 0; angleSpan: 110
},
PieSlice {
anchors.fill: parent
color: "black"
fromAngle: 110; angleSpan: 50
},
PieSlice {
anchors.fill: parent
color: "blue"
fromAngle: 160; angleSpan: 100
}
]
}
}


class PieChart : public QQuickItem
{
Q_OBJECT
// 只读的列表,虽然是只读的,但可以执行添等操作
Q_PROPERTY(QQmlListProperty<PieSlice> slices READ slices)
...
public:
...
QQmlListProperty<PieSlice> slices();

private:
static void append_slice(QQmlListProperty<PieSlice> *list, PieSlice *slice);

QString m_name;
QList<PieSlice *> m_slices;
};

QQmlListProperty<PieSlice> PieChart::slices()
{
return QQmlListProperty<PieSlice>(this, nullptr, &PieChart::append_slice, nullptr,
nullptr, nullptr, nullptr, nullptr);
}

void PieChart::append_slice(QQmlListProperty<PieSlice> *list, PieSlice *slice)
{
PieChart *chart = qobject_cast<PieChart *>(list->object);
if (chart) {
slice->setParentItem(chart);
chart->m_slices.append(slice);
}
}

​​QQmlListProperty​​ 虽然是只读的,但是它可以修改(添加元素等)。

关于​​QQmlListProperty​​

上例中还有很多地方没说清楚,可以参考另外一个例子:​​https://doc.qt.io/qt-6/qtqml-referenceexamples-properties-example.html​​

Qt6中C++与QML混合编程--教程(2)_qml与c++_02

构造函数的第一个参数赋值给了object,第二个参数给了data,它们都是公有属性可以直接访问(这个文档上没有或都是我没找到,是从源码中得到的信息)。

构造时要提供5个操作函数:

  • append
  • count
  • at
  • clear
  • replace
  • removeLast

这些操作函数可以传null,几个重载都是根据特定情况给某个操作函数赋值为null,比如:

QQmlListProperty(QObject *o, void *d, CountFunction c, AtFunction a)
: object(o), data(d), count(c), at(a)
{}

这个列表它就是只读的...

还有一个特殊的构造函数

QQmlListProperty(QObject *o, QList<T *> *list)
: object(o), data(list), append(qlist_append), count(qlist_count), at(qlist_at),
clear(qlist_clear), replace(qlist_replace), removeLast(qlist_removeLast)
{}

内部提供了默认的操作函数集

插件化

PieChart必须插件化之后才能给不同的项目使用。

又要去做支线任务了....

  • 创建插化的步骤: ​​https://doc.qt.io/qt-6/qtqml-modules-cppplugins.html​​
  • 如何创建Qt插件: ​​https://doc.qt.io/qt-6/plugins-howto.html​​


举报

相关推荐

0 条评论