到手一套qt项目,不同于以往Qt Designer使用的ui文件.这个项目的界面是直接用QML完成的.
什么是QML
从 Qt 4.7 开始,Qt 引入了一种声明式脚本语言,称为 QML(Qt Meta Language 或者 Qt Modeling Language),作为 C++ 语言的一种替代。而 Qt Quick 就是使用 QML 构建的一套类库。
QML 是一种基于 JavaScript 的声明式语言。在 Qt 5 中,QML 有了长足进步,并且同 C++ 并列成为 Qt 的首选编程语言。也就是说,使用 Qt 5,我们不仅可以使用 C++ 开发 Qt 程序,而且可以使用 QML。虽然 QML 是解释型语言,性能要比 C++ 低一些,但是新版 QML 使用 V8,Qt 5.2 又引入了专为 QML 优化的 V4 引擎,使得其性能不再有明显降低。在 Nokia 发布 Qt 4.7 的时候,QML 被用于开发手机应用程序,全面支持触摸操作、流畅的动画效果等。但是在 Qt 5 中,QML 已经不仅限于开发手机应用,也可以用户开发传统的桌面程序。
QML 文档描述了一个对象树。QML 元素包含了其构造块、图形元素(矩形、图片等)和行为(例如动画、切换等)。这些 QML 元素按照一定的嵌套关系构成复杂的组件,供用户交互。
《Qt学习之路2》https://www.kancloud.cn/kancloud/qt-study-road-2/99515
QML 文档
分为 import 和 declaration 两部分。前者用于引入文档中所需要的组件(有可能是类库,也可以是一个 JavaScript 文件或者另外的 QML 文件);后者用于声明本文档中的 QML 元素。
教程实例:
import QtQuick 2.0
Rectangle {
width: 360
height: 360
Text {
text: qsTr("Hello World")
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
}
import
语句引入 QtQuick 2.0
每一个 QML 有且只有一个根元素,类似于 XML 文档。这个根元素就是这个 QML 文档中定义的 QML 元素,
在这个例子中就是一个Rectangle
对象。使用键值对的形式区分元素属性。定义了一个矩形,宽度为 360 像素,高度为 360 像素。QML 元素使用 {} 包围起来。{} 之中是该元素的属性;属性以键值对name : value
的形式给出。这十分类似与 JSON 语法。QML 元素可以有一个id
属性,作为该元素的名字。以后我们可以直接用这个名字指代该元素,相当于该元素的指针。
id
属性在整个 QML 文档中必须是唯一的。QML 元素允许嵌套,一个 QML 元素可以没有、可以有一个或多个子元素。子元素可以使用parent
关键字访问其父元素。我们可以用 id,也可以用parent
关键字访问其他元素。一个最佳实践是,将根元素的 id 命名为 root。这样我们就可以很方便地访问到根元素。
id的命名规范:必须小写字母开头,不可以使用大写字母开头,支持utf-8字符(非英文运算符)。
否则报错:IDs cannot start with an uppercase letter
more:https://blog.csdn.net/qyvlik/article/details/45046253
QML 文档定义了一个对象树,所以 QML 文档中元素是可以嵌套的。在这个矩形中,我们又增加了一个Text
元素,就是一个文本。Text
显示的是 Hello World 字符串,而这个字符串是由qsTr()
函数返回的。
qsTr()
函数就是QObject::tr()
函数的 QML 版本,用于返回可翻译的字符串。Text
的位置则是由锚点(anchor)定义。示例中的Text
位置定义为 parent 中心,其中parent
属性就是这个元素所在的外部的元素。同理,我们可以看到MouseArea
是充满父元素的。MouseArea
还有一个onClicked
属性。这是一个回调,也就是鼠标点击事件。MouseArea
可以看作是可以相应鼠标事件的区域。当点击事件发出时,就会执行onClicked
中的代码。这段代码其实是让整个程序退出。注意我们的MouseArea
充满整个矩形,所以整个区域都可以接受鼠标事件。
当我们运行这个项目时,我们就可以看到一个宽和高都是 360 像素的矩形,中央有一行文本,鼠标点击矩形任意区域就会使其退出。
改变 main.qml 文件中的“Hello World”字符串,不重新编译直接运行,就会看到运行结果也会相应的变化。
这说明 QML 文档是运行时解释的,不需要经过编译。所以,利用 QML 的解释执行的特性,QML 尤其适合于快速开发和原型建模。另外,由于 QML 比 C++ 简单很多,所以 QML 也适用于提供插件等机制。
按照原例子增加了一个下注功能,认识了emit关键字以及qml中property属性。
当系统提供的属性不够用时,我们可以在 QML 自定义属性。使用property
关键字声明一个自定义属性,后面是属性类型和属性名,最后是属性值。声明自定义属性的语法是property <type> <name> : <value>
。如果没有默认值,那么将给出系统类型的默认值。
property属性属于一个对象 ,可以被赋为静态值或者是绑定到动态表达式上。它的值可以被其它的对象读取。一般情况下,property属性也可以被其它对象修改,除非该QML类型明确指定该property属性不能被修改。
【定义property属性】
一个property属性可以在C++中定义,并且通过Q_PROPERTY注册到QML类型系统。
以下材料摘自:https://blog.csdn.net/wzs250969969/article/details/78418124
Q_PROPERTY()是一个宏,用来在一个类中声明一个属性property,由于该宏是qt特有的,需要用moc进行编译,故必须继承于QObject类。
Q_PROPERTY(type name
READ getFunction
[WRITE setFunction]
[RESET resetFunction]
[NOTIFY notifySignal]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
下面是一些典型的声明属性的示例:
Q_PROPERTY(double minValue READ getMinValue WRITE setMinValue)
Q_PROPERTY(bool animation READ getAnimation WRITE setAnimation)
Q_PROPERTY(QColor barColor READ getBarColor WRITE setBarColor)
- 一个属性的行为就像类的数据成员,但是它还具有附加的特性,这些特性可以被元数据对象系统操作。这些特性是:
需要一个READ访问器函数。用于读属性的值。理想情况下,有一个不变的函数用于此目的,并且它必须返回属性的类型的值或指针或引用。例如,QWidget::focus是一个只读的属性,它对应一个读函数:QWidget::hasFocus()。 - 一个可选的WRITE访问器函数。它用于设置属性的值。它必须返回空并且至少具有一个参数,参数是属性类型的值或指针或引用。例如:QWidget::enabled具有WRITE函数QWidget::setEnable()。只读属性不需要写函数。例如,QWidget::focus没有对应的写函数。
- 一个可选的RESET函数。用于设置属性的值到它的默认值。例如:QWidget::cursor具有典型的READ和WRITE函数,QWidget::cursor()和QWidget::setCursor(),并且它也具有一个RESET函数,QWidget::unsetCursor()。RESET函数必须返回void并且不带有任何参数。
- 一个可选的NOTIFY信号。如果被定义了,信号将在属性的值改变时发出。信号必须带有一个参数,这个参数的类型必须与属性相同;参数保存的是属性的新值。
- 一个DESIGNABLE变量表明此属性是否在界面设计器的属性编辑器中出现。大多数属性是可见的,除了为这个变量传入true或false,你还可以指定一个bool型的成员函数。
- SCRIPTABLE变量表明这个属性是否可以被一个脚本引擎操作(默认是true)。你也可以赋予它true或false或bool型函数。
- STORED变量表明了属性是否被认为是独立存在还是依赖于其它的值而存在。它也表明是否在保存对象状态时保存此属性的值。大多数属性都是需要保存的,但是,如QWidget::minimumWidth()就是不被保存的,因为它的值是从另一个属性QWidget::minimumSize()得来的。
- USER变量表明属性是否被设计为面向用户的或用户可修改的类属性。通常,每个类只有一个USER属性。例如,QAbstractButton::checked是按钮类的用户可修改属性。注意QItemDelegate获取和设置widget的USER属性。
- CONSTANT的出现表明属性的值是不变的。对于一个object实例,常量属性的READ方法在每次被调用时必须返回相同的值。此常量值可能在不同的object实例中不相同。一个常量属性不能具有WRITE方法或NOYIFY信号。
- FINAL变量的出现表明属性不能被派生类所重写。有些情况下,这可以用于效率优化,但不是被moc强制的。程序员必须永远注意不能重写一个FINAL属性。
READ,WRITE和RESET函数都可以被继承。它们也可以是虚函数。当它们在被多重继承中被继承时,它们必须出现在第一个被继承的类中。
属性的类型可以是被QVariant支持的所有类型,也可以是用户定义的类型。在下面的例子中,类QDate被当作用户自定义类型。
Q_PROPERTY(QDate data READ getDate WRITE setDate)
因为QDate是用户定义的,你必须包含<QDate>头文件。
对于QMap,QList和QValueList属性,属性的值是一个QVariant,它包含整个list或map。注意Q_PROPERTY字符串不能包含逗号,因为逗号会划分宏的参数。因此,你必须使用QMap作为属性的类型而不是QMap<QString,QVariant>。为了保持一致性,也需要用QList和QValueList而不是QList<QVariant>和QValueList<QVariant>。
代码调用的例子:
类定义 头文件:(节选相关)
class Simulator : public QObject
{
Q_OBJECT
Q_PROPERTY(double userMoney READ getUserMoney NOTIFY userMoneyUpdated)
Q_PROPERTY(bool betting READ isBet NOTIFY isbetChanged)
……
public:
double userMoney = 1.0;
double getUserMoney() const;
bool betting = false;
bool isBet() const;
……
signals:
void isbetChanged();
void userMoneyUpdated();
public slots:
void bet(int op);
};
类实现 cpp文件(节选相关)
void Simulator::gameFinished(const Game &game) {
mostRecentGame = game;
++gamePlayedCount;
result();//在result中有对userMoney值的修改,对betting状态的变更
emit userMoneyUpdated();
emit isbetChanged();
emit gameCountUpdated();
emit mostRecentGameUpdated();
}
double Simulator::getUserMoney() const{
return userMoney;
}
bool Simulator::isBet() const{
return betting;
}
ui相关:
Text {
x: 0
y: 59
width: parent.width
font.pointSize: 20
color: "white"
text: " money: " + simulator.userMoney//变量
horizontalAlignment: Text.AlignHCenter
}
RoundButton {
id: betPlayerButton
anchors.left: parent.left
anchors.top: parent.top
property double calculatedWidth: parent.width * 0.8
property double minWidth: 75
width: (calculatedWidth > minWidth) ? calculatedWidth : minWidth
height: parent.height * 0.15
text: simulator.betting ? "PLAYER'S HAND":"BetPlayer"//变量
anchors.leftMargin: 21
anchors.topMargin: 103
radius: height * .1
onClicked: {
simulator.bet(0)
}
}
如果不注册的话,就会显示undefined。