✿4-The Basics-Qt Quick and QML

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了✿4-The Basics-Qt Quick and QML相关的知识,希望对你有一定的参考价值。

Qt由两个不同的模块组成,用于开发图形用户界面(GUI)应用程序。第一种方法是使用Qt小部件和C++,这是我们在前一章中了解的。第二种方法是使用Qt Quick控件和Qt建模语言(Qt Modeling language:QML),我们将在本章中介绍。

让我们熟悉QML类型系统和各种QML类型。QML文件中的类型可以来自不同的来源。QML文件中使用的不同类型概述如下:

  • 本地提供的QML基础类型,如intboolreallist
  • JS类型,如varDateArray
  • QML对象类型,如ItemRectangleImageComponent
  • QML模块通过C++注册的类型,如BackendLogic
  • 作为QML文件提供的类型,如MyPushButton

基本类型可以包含简单的值,例如int或bool类型。除了本机基本类型之外,Qt Quick模块还提供其他基本类型。QML引擎还支持JS对象和数组。任何标准JS类型都可以使用泛型var类型创建和存储。请注意,变体类型(variant type)已过时,仅用于支持较旧的应用程序。QML对象类型是可以从中创建QML对象的类型。自定义QML对象类型可以通过创建定义该类型的.qml文件来定义。QML对象类型可以具有属性、方法、信号等。

要在QML文件中使用基本QML类型,请使用以下代码行导QtQml模块:

import QtQml

Item是Qt Quick中所有视觉元素的基本类型。Qt Quick中的所有可视项都继承自Item,Item是一个透明的可视元素,可以用作容器。Qt Quick提供Rectangle作为绘制矩形的视觉类型,以及显示图像的Image类型。Item为视觉元素提供一组公共属性。

Qt Quick Controls提供了一组UI元素,可用于使用Qt Quick构建流畅的UI。为了避免与小部件(widgets)的歧义,我们将使用术语控件(controls)来表示UI元素。

可以使用.qml文件中的以下导入语句将QML类型导入到应用程序中:

import QtQuick.Controls

Qt 6引入了自动导入功能,该功能被编写为import <module> auto。这将确保导入的模块和正在导入的模块具有相同的版本号。

要配置Qt快速控制模块以使用qmake进行构建,请在项目的.pro文件中添加以下行:

QT += quickcontrols2

Qt Quick Controls附带一套标准样式。它们列在这里:

  • Basic
  • Fusion
  • Imagine
  • Material
  • Universal

有两种方式在Qt Quick控件中应用样式:

  • 编译时(Compile time)
  • 运行时(Runtime)

你可以通过导入相应的样式模块来应用编译时样式,如下所示:

import QtQuick.Controls.Universal

可以使用以下方法之一应用运行时样式:

创建一个简单的Qt Qucik应用程序:Application->Qt Quick Application

QML运行时在QtQml模块中用C++实现。它包含一个负责执行QML的QML引擎。它还保存QML元素可以访问的上下文和属性。Qt提供了一个QQmlEngine类来实例化QML组件。你还可以使用QQmlApplicationEngine类以方便的方式使用单个QML文件加载应用程序。
main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[]) 
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
    	return -1;
    return app.exec();

你还可以使用QQuickView类,它提供了一个显示Qt Quick UI的窗口。这种方法有点过时。QQmlApplicationEngine使用QML提供了方便的中央应用程序功能,而QQuickView通常由C++控制。下面的代码片段显示了如何使用QQuickView加载.qml文件:

#include <QGuiApplication> 
#include <QQuickView> 
int main(int argc, char *argv[])  
	QGuiApplication app(argc, argv); 
	QQuickView view; 
	view.setResizeMode(QQuickView::SizeRootObjectToView); 
	view.setSource(QUrl("qrc:/main.qml")); 
	view.show(); 
	return app.exec(); 

QQuickView不支持将Window用作根项目。如果你想从QML创建根窗口,那么选择QQmlApplicationEngine。
在使用QQuickView时,可以直接使用任何Qt Quick元素,如以下代码段所示:

import QtQuick
Item 
	width:400
	height:400
	Text 
		anchors.centerIn:parent
		text:"Hello World"
	

运行程序,效果如下:

请记住,在对.pro文件进行更改后,需要运行qmake。

创建一个Qt Quick 2 UI工程:Other Project->Qt Quick UI Prototype

与Qt小部件中的.ui文件类似,你也可以在QML中创建UI文件。该文件具有.ui.qml文件扩展名。QML文件有两种类型:一种扩展名为.qml,另一种扩展名为.ui.qml。QML引擎将其视为标准.qml文件,但它禁止其内部的逻辑实现。它为多个.qml文件创建了一个可重用的UI定义。通过将用户界面定义和逻辑实现分离,增强了QML代码的可维护性。

在QML中定位与布局
下面的代码片段显示了如何将Rectangle项放置在位置(50,50)处:

import QtQuick
Rectangle 
	// Manually positioned at 50,50
	x: 50// x position
	y: 50// y position
	width: 100; height: 80
	color: "blue"

Qt Quick提供了一种将控件锚定到另一个控件的方法。每个项目有七条不可见的锚定线:left、right、top、bottom、baseline、horizontalCenter、verticalCenter。你可以为每一侧设置边距或不同的边距。如果一个特定项目有多个锚点,则可以对它们进行分组。
让我们看看下面的例子:

import QtQuick
import QtQuick.Window
Window 
	width: 400; height: 400
	visible: true
	title: qsTr("Anchoring Demo")
	Rectangle 
		id: blueRect
		anchors 
			left: parent.left; leftMargin:10
			right: parent.right; rightMargin: 40
			top: parent.top; topMargin: 50
			bottom: parent.bottom; bottomMargin: 100
		
		color: "blue"
		Rectangle 
			id: redRect
			anchors.centerIn: blueRect
			color:"red"
			width: 150; height: 100
		
	


你还可以编写代码,将控件放置在定位器内。如果使用Qt Quick Designer,Qt Creator会自动生成代码。生成的代码可以通过表单编辑器旁边的文本编辑器选项卡查看和修改。代码显示在以下代码片段中:

Row  
	id: row      
	Rectangle  
		id: yellowRect 
		width: 150; height: 100 
		color: "yellow" 
		border.color: "black" 
	 
	Rectangle  
		id: redRect 
		width: 150; height: 100 
		color: "red" 
		border.color: "black" 
	 
	Rectangle  
		id: greenRect 
		width: 150; height: 100 
		color: "green" 
		border.color: "black" 
	

重复器(Repeater)使用提供的模型创建许多可视元素,以及模板中的元素以与定位器一起使用,并使用模型中的数据。在定位器内放置一个中继器,并创建符合定义定位器布置的视觉元素。如果有许多类似的项目,则带有中继器的定位器在按常规布局布置时更易于维护。

import QtQuick 
import QtQuick.Window 
Window  
	width: 400; height: 200 
	visible: true 
	title: qsTr("Repeater Demo") 
	Row 
		anchors.centerIn: parent 
		spacing: 10 
		Repeater 
			model: 5 
			Rectangle  
				width: 60; height: 40 
				border width: 1; color: "black"; 
				color: "green" 
			 
		 
	 


Qt Quick布局
可以使用以下导入语句将Qt Quick布局导入QML文件:

import QtQuick.Layouts

在QML中有5种不同类型的布局类型:RowLayout、ColumnLayout、GridLayout、Layout、StackLayout。其中Layout是为推送到ColumnLayout、RowLayout或GridLayout类型上的项目提供附加属性。
让我们看看下面的RowLayout示例:

import QtQuick 
import QtQuick.Window 
import QtQuick.Layouts 
Window  
	width: 640; height: 480 
	visible: true 
	title: qsTr("Layout Demo") 
	RowLayout  
		id: layout 
		anchors.fill: parent 
		spacing: 6 
		Rectangle  
			color: 'yellow' 
			Layout.fillWidth: true 
			Layout.minimumWidth: 50
			Layout.preferredWidth: 150 
			Layout.maximumWidth: 200 
			Layout.minimumHeight: 100 
			Layout.margins: 10 
		 
		Rectangle  
			color: 'red' 
			Layout.fillWidth: true 
			Layout.minimumWidth: 50 
			Layout.preferredWidth: 100 
			Layout.preferredHeight: 80 
			Layout.margins: 10 
		 
	 

请注意,Row类型是定位器,而RowLayout类型是布局。和往常一样,何时使用它们主要取决于你的目标。

QML与C++集成
QML应用程序通常需要在C++中处理更高级、更高性能的任务。最常见、最快捷的方法是将C++类公开给QML运行时,前提是C++实现是从QObject派生的。QML可以很容易地与C++代码集成。QML对象可以从C++加载和操作。QML与Qt的元对象(meta-object)系统的集成允许从QML调用C++功能。这有助于构建混合了C++、QML和JS的混合应用程序。要向QML公开C++数据、属性或方法,它应该从QObject类派生。这是可能的,因为所有QML对象类型都是使用QObject派生类实现的,允许QML引擎通过Qt元对象系统加载和检查对象。可以通过以下方式将QML与C++集成:

  • 使用上下文属性将C++对象嵌入QML
  • 向QML引擎注册类型
  • 创建QML扩展插件

我们导出radius到QML环境:

#include <QGuiApplication> 
#include <QQmlApplicationEngine> 
#include <QQmlContext> 
int main(int argc, char *argv[])  
	QGuiApplication app(argc, argv); 
	QQmlApplicationEngine engine; 
	engine.rootContext()->setContextProperty("radius", 50); 
	const QUrl url(QStringLiteral("qrc:/main.qml")); 
	engine.load(url); 
	return app.exec(); 

我们现在可以在QML中直接使用导出的值:

import QtQuick 
import QtQuick.Window 
Window  
	width: 640; height: 480 
	visible: true 
	title: qsTr("QML CPP integration") 
	Text  
		anchors.centerIn: parent 
		text: "C++ Context Property Value: "+ radius 
	 

用QML引擎注册C++类
这些方法可以是公共插槽,也可以是带有Q_INVOKABLE标记的公共方法。
现在,让我们将C++类导入QML文件。看看下面的C++类:
backend.h

#ifndef BACKENDLOGIC_H
#define BACKENDLOGIC_H
#include <QObject>
class BackendLogic : public QObject 
    Q_OBJECT
public:
    explicit BackendLogic(QObject *parent = nullptr)  Q_UNUSED(parent);
    Q_INVOKABLE int getData()  return mValue; 
private:
    int mValue = 100;
;
#endif // BACKENDLOGIC_H

可以在main.cpp中注册C++类,使用qmlRegister()函数:
main.cpp

#include "backend.h"
	...
	qmlRegisterType<BackendLogic>("backend.logic", 1, 0, "BackendLogic");
	...

template<typename T>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);

任何QObject派生的C++类都可以注册为QML对象类型。一旦类在QML类型系统中注册,该类就可以像任何其他QML类型一样使用。现在,C++类已经准备好在.qml文件中实例化。你必须导入模块并创建一个对象,如以下代码段所示:
main.qml

import QtQuick
import QtQuick.Window
import backend.logic
Window 
    width: 640; height: 480
    visible: true
    title: qsTr("QML CPP integration")
    BackendLogic 
        id: backend
    
    Text 
        anchors.centerIn: parent
        text: "From Backend Logic : " + backend.getData()
    


还可以使用qmlRegisterSingletonType()将C++类公开为QML单例。通过使用QML单例,可以防止全局命名空间中出现重复的对象。

在Qt6中,可以通过使用QML_ELEMENT宏实现C++集成。此宏使用其类或命名空间名称作为QML元素名称,声明封闭类型在QML中可用。要在C++头文件中使用此宏,必须包含头文件#include<QtQml>。
让我们看看下面的例子:

#ifndef USINGELEMENT_H 
#define USINGELEMENT_H 
#include <QObject> 
#include <QtQml> 
class UsingElements : public QObject  
	Q_OBJECT 
	QML_ELEMENT 
public: 
	explicit UsingElements(QObject *parent = nullptr)  Q_UNUSED(parent);  
	Q_INVOKABLE int readValue()  return mValue;  
private: 
	int mValue = 500; 
; 
#endif // USINGELEMENT_H

.pro文件中,你得添加qmltypes选项到CONFIG变量,同时指定QML_IMPORT_NAME和QML_IMPORT_MAJOR_VERSION:

CONFIG += qmltypes
QML_IMPORT_NAME = backend.element
QML_IMPORT_MAJOR_VERSION = 1

但实测,在.pro文件中添加上述代码,会报错如下(不添加则正常运行):

创建一个QML扩展插件
QML扩展插件提供了与C++集成的最灵活的方式。它允许你在第一个QML文件调用导入标识符时加载的插件中注册类型。你可以跨项目使用插件,这在构建复杂项目时非常方便。

Library->Qt Quick 2 Extension Plugin
plugin类必须从QqmlExtensionPlugin派生,并且应该实现registerTypes()函数。需要Q_PLUGIN_METADATA宏来将插件标识为QML扩展插件:

#include <QQmlExtensionPlugin>
class MyQtQuickPlugin2Plugin: public QQmlExtensionPlugin 
    Q_OBJECT
    Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
    void registerTypes(const char *uri) override;
;

这是一个高级Qt主题部分。要创建自己的QML扩展插件,你需要深入了解Qt。如果你是初学者,可以跳过这一节。

在C++类中调用QML函数
所有QML方法都公开给元对象系统,可以使用QMetaObject::invokeMethod()从C++调用。可以为参数指定类型,并在冒号字符后指定返回值,如下一个代码段所示。例如,当你想要将C++中具有特定签名的信号连接到QML定义的方法时,这可能很有用。如果省略类型,C++签名将使用QVariant。让我们来看一个使用QMetaObject::invokeMethod()调用QML方法的应用程序。
在QML文件中,我们添加一个名为qmlMethod()的方法,如下所示:

import QtQuick 
Item  
	function qmlMethod(msg: string) : string  
		console.log("Received message:", msg) 
		return "Success" 
	 
	Component.onCompleted:  
		console.log("Component created successfully.") 
	 

在main.cpp文件中,调用QMetaObject::invokeMethod()函数:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
int main(int argc, char *argv[]) 
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    QQmlComponent component(&engine, "qrc:/CustomItem.qml");
    QObject *myObject = component.create();

    QString retValue = "";
    QString msg = "Message from C++";

    QMetaObject::invokeMethod(myObject, "qmlMethod", Q_RETURN_ARG(QString, retValue), Q_ARG(QString, msg));

    qDebug() << "QML method returned:" << retValue;
    delete myObject;
    return app.exec();

运行程序,控制台打印:

qml: Component created successfully.
qml: Received message: Message from C++
QML method returned: "Success"

如果你不需要返回值,你可以调用只有两个参数的invokeMethod()函数:

QMetaObject::invokeMethod(myObject, "qmlMethod");

向C++公开QML对象指针
MyCustomObject.h

#ifndef CUSTOMOBJECT_H
#define CUSTOMOBJECT_H
#include <QObject>
#include <QVariant>
class CustomObject: public QObject 
	Q_OBJECT
public:
	explicit CustomObject(QObject *parent = nullptr)  Q_UNUSED(parent); ;
	Q_INVOKABLE void setObject(QObject* object) 
		object->setProperty("text", QVariant("Clicked!"));
	
;
#endif

bool QObject::setProperty(const char *name, const QVariant &value)

main.qml

import QtQuick
import QtQuick.Window
import QtQuick.Controls
import MyCustomObject
Window 
	width: 640; height: 480;
	visible: true
	title: qsTr("QML Object in C++")
	CustomObject 
		id: customObject
	
	Button 
		id: button
		anchors.centerIn: parent
		text: qsTr("Click Me!")
		onClicked: 
			customObject.setObject(button);
		
	

重要提示:
Qt QML模块提供几种宏去注册非实例化类型。QML_ANONYMOUS注册一个非实例化的C++类型并且可以被QML引用。QML_INTERFACE注册一个存在的Qt接口类型。这个类型是不能从QML实例化的,并且不能用它来声明QML属性。QML_UNCREATABLE注册一个名称化的C++类型,它不能实例化但对于QML类型系统它应该是唯一的。QML_SINGLETON注册一个可以从QML导入的单例类型。

QML与Javascript集成

import QtQuick
import QtQuick.Window
import QtQuick.Controls
Window 
    width: 640; height: 480;
    visible: true
    title: qsTr("QML Object in JS")
    function btnClicked(controlName) 
        controlName.text= "JS called"
    
    Column 
        anchors.centerIn: parent
        Button 
            text: "Call JS!"
            onClicked:btnClicked(displayText)
        
        Text 
            id: displayText
        
    

如果你的逻辑非常长,可以使用单独的JS文件。你可以用下面语句导入JS文件:

import "helloworld.js" as HelloWorld// HelloWorld is an identifier for our JS file

你也可以创建共享库,你得将下面语句包含在JS文件的开头:

.pragma library

在QML中导入目录
通用的导入形式,如下:

import "<DirectoryPath>" [as <Qualifier>]

例如,你的目录名是customqmlelements,那么你可以这样导入:

import ".../customqmlelements"

你也可以将导入的目录作为一个合适的命名空间:

import ".../customqmlelements" as CustomQMLElements

你还可以从资源路径导入文件:

import "qrc:/qml/customqmlelements"

你也可以从远程服务器导入一个目录。

处理鼠标与触摸事件
MouseArea

import QtQuick
import QtQuick.Window
Window 
	width: 640; height: 480
	visible: true
	title: qsTr("Mouse Area Demo")
	Rectangle 
		anchors.centerIn: parent
		width: 100; height: 100
		color: "green"
		MouseArea 
			anchors.fill: parent
			onClicked:  parent.color = 'red' 
		
	

MultiPointTouchArea

import QtQuick
import QtQuick.Window
Window 
	width: 640; height: 480
	visible: true
	title: qsTr("Multitouch Example")
	MultiPointTouchArea 
		anchors.fill: parent
		touchPoints: [
			TouchPoint id: tp1,
			TouchPoint id: tp2
		]
	
	Rectangle 
		width: 100; height: 100
		color: "blue"
		x: tp1.x; y: tp1.y
	
	Rectangle 
		width: 100; height: 100
		color: "red"
		x: tp2.x; y: tp2.y
	

TapHandler
TapHandler是用来在触摸屏上处理鼠标点击事件和触拍事件的。

import QtQuick
import QtQuick.Window
Window 
	width: 640; height: 480
	visible: true
	title: qsTr("Tap Demo")
	Item 
		anchors.fill: parent
		TapHandler 
			acceptedButtons: Qt.LeftButton
			onTapped: console.log("Left Button Clicked")
		
		TapHandler 
			acceptedButtons: Qt✿4-The Basics-Qt Quick and QML

PAT (Advanced Level) 1101. Quick Sort (25)

A Quick Guide to Alibaba “双十一”来袭!阿里巴巴的业务不止是天猫淘宝

广义快速幂

ssemble JavaBeans components into an application without having to write any code

instant的近义词辨析