-
演示程序(A Boilerplate Application)
-
QObject对象(The QObject)
-
编译系统(Build Systems)
-
QMake
-
CMake
-
Qt通用类(Common Qt Classes)
-
QString
-
顺序容器(Sequential Containers)
-
组合容器(Associative Containers)
-
文件IO(File IO)
-
C++数据模型(Models in C++)
-
一个简单的模型(A simple model)
-
更复杂的数据(More Complex Data)
-
动态数据(Dynamic Data)
-
进阶技巧(Advanced Techniques)
Qt是QML与JavaScript的C++扩展工具包。有许多语言与Qt绑定,但是由于Qt是由C++开发的,C++的精神贯穿了整个Qt。在这一节中,我们着重从C++的角度介绍Qt,使用C++开发的本地插件来了解如何更好的扩展QML。通过C++,可以扩展和控制提供给QML的执行环境。
这章将讲解Qt,正如Qt所要求的一样,需要读者有一定的C++基础知识。Qt不依赖于先进的C++特性,我认为Qt风格的C++代码可读性非常高,所以不要担心你的C++方面比较差。
从C++的角度分析Qt,你会发现Qt通过内省数据的机制实现了许多现代语言的特性。这是通过使用基础类QObject实现的。内省数据,源数据,类运行时的数据维护。原生的C++是不会完成这些事情的。这使得动态查询对象信息,例如它们的属性成为可能。
Qt使用源对象信息实现了信号与槽的回调绑定。每个信号能够连接任意数量的槽函数或者其它的信号。当一个信号从一个对象实例从发送后,会调用连接信号的槽函数。发送信号的对象不需要知道接收槽对象的任何信息,反之亦然。这一机制可以创建复用性非常高的组件,并减少组件之间的依赖。
内省特性也用于创建动态语言的绑定,使得QML可以调用暴露的C++对象实例,并且可以从JavaScript中调用C++函数。除了绑定Qt C++, 绑定标准的JavaScript也是一种非常流行的方式,还有Python的绑定,叫做PyQt。
除了这些核心概念,Qt可以使用C++开发跨平台应用程序。Qt C++在不同的操作系统上提供了一套平台抽象,允许开发者专注于手上的任务,不需要你去了解如何在不同的操作系统上打开一个文件。这意味着你可以在Windows,OS X和Linux重复编译相同的代码,由Qt去解决在不同平台上的适配问题。最终保持本地构建的应用程序与目标平台的窗口风格上看起来一致。随着移动平台的桌面更新,Qt也提供相同的代码在不同的移动平台上编译,例如IOS,Android,Jolla,BlackBerry,Ubuntu Phone,Tizen。
这样不仅仅是代码可以重用,开发者的技能也可以重用。了解Qt的团队比只专注于单平台特定技能的团队可以接触更多的平台,由于Qt的灵活性,团队可以使用相同的技术创建不同平台的组件。
对于所有平台,Qt提供了一套基本类,例如支持完整unicode编码的字符串,链表容器,向量容器,缓冲容器。它也提供了目标平台的通用主循环抽象和跨平台的线程支持,网络支持。Qt的主旨是为Qt的开发者提供所有必须的功能。对于特定领域的任务,例如本地库接口,Qt也提供了一些帮助类来使得这些操作更加简单。
QtCore class list或者QtCore overview获取更多的信息。
使用qmake和make来构建程序。QMake读取项目文件(project file)并生成一个Makefile供make使用。项目文件(project file)独立于平台,qmake会根据特定平台的设置应用一些规则来生成Makefile。在有特殊需求的项目中,项目文件也可以包含特定平台规则的平台作用域。下面是一个简单的项目文件(project file)例子。
# build an application
TEMPLATE = app
# use the core module and do not use the gui module
QT += core
QT -= gui
# name of the executable
TARGET = CoreApp
# allow console output
CONFIG += console
# for mac remove the application bundling
macx {
CONFIG -= app_bundle
# sources to be build
SOURCES += main.cpp
我们不会再继续深入这个话题,只需要记住Qt项目会使用特定的项目文件(project file),qmake会根据这些项目文件和指定平台生成Makefile。
上面简单的例子只是在应用程序中写入文本。一个命令行工具这是不够的。对于一个用户界面我们需要一个事件循环来等待用户的输入并安排刷新绘制操作。下面这个相同的例子使用一个桌面按钮来触发写入。
令人惊奇的是我们的main.cpp依然很小。我们将代码移入到我们的类中,并使用信号槽(signal/slots)来连接用用户的输入,例如按钮点击。信号槽(signal/slot)机制通常需要一个对象,你很快就会看到。
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
#include "mainwindow.h"
int main(int argc, char** argv)
QApplication app(argc, argv);
MainWindow win;
win.resize(320, 240);
win.setVisible(true);
return app.exec();
在main函数中我们简单的创建了一个应用程序对象,并使用exec()开始事件循环。现在应用程序放在了事件循环中,并等待用户输入。
int main(int argc, char** argv)
QApplication app(argc, argv); // init application
// create the ui
return app.exec(); // execute event loop
Qt提供了几种UI技术。这个例子中我们使用纯Qt C++的桌面窗口用户界面库。我们需要创建一个主窗口来放置一个触发功能的按钮,同事由主窗口来实现我们的核心功能,正如我们在上面例子上看到的。
主窗口本身也是一个窗口,它是一个没有父对象的窗口。这与Qt如何定义用户界面为一个界面元素树类似。在这个例子中,主窗口是我们的根元素,按钮是主窗口上的一个子元素。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets>
class MainWindow : public QMainWindow
public:
MainWindow(QWidget* parent=0);
~MainWindow();
public slots:
void storeContent();
private:
QPushButton *m_button;
#endif // MAINWINDOW_H
此外我们定义了一个公有槽函数storeContent()
,当点击按钮时会调用这个函数。槽函数是一个C++方法,这个方法被注册到Qt的源对象系统中,可以被动态调用。
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
m_button = new QPushButton("Store Content", this);
setCentralWidget(m_button);
connect(m_button, &QPushButton::clicked, this, &MainWindow::storeContent);
MainWindow::~MainWindow()
void MainWindow::storeContent()
qDebug() << "... store content";
QString message("Hello World!");
QFile file(QDir::home().absoluteFilePath("out.txt"));
if(!file.open(QIODevice::WriteOnly)) {
qWarning() << "Can not open file with write access";
return;
QTextStream stream(&file);
stream << message;
在主窗口中我们首先创建了一个按钮,并将clicked()
信号与storeContent()
槽连接起来。每点击信号发送时都会触发调用槽函数storeContent()
。就是这么简单,通过信号与槽的机制实现了松耦合的对象通信。
QMake Manual - qmake手册目录。
QMake Language - 赋值,域和相关语法
QMake Variables - TEMPLATE,CONFIG,QT等变量解释
CMake Help - CMake在线帮助文档
Running CMake
KDE CMake Tutorial
CMake Book
CMake and Qt
隐式共享,在Qt的很多类中都用到了它。
QString data("A,B,C,D"); // create a simple string
// split it into parts
QStringList list = data.split(",");
// create a new string out of the parts
QString out = list.join(",");
// verify both are the same
QVERIFY(data == out);
// change the first character to upper case
QVERIFY(QString("A") == out[0].toUpper());
这里我们将展示如何将一个字符串转换为数字,将一个数字转换为字符串。也有一些方便的函数用于float或者double和其它类型的转换。只需要在Qt帮助文档中就可以找到这些使用方法。
// create some variables
int v = 10;
int base = 10;
// convert an int to a string
QString a = QString::number(v, base);
// and back using and sets ok to true on success
bool ok(false);
int v2 = a.toInt(&ok, base);
// verify our results
QVERIFY(ok == true);
QVERIFY(v = v2);
通常你需要参数化文本。例如使用QString("Hello" + name)
,一个更加灵活的方法是使用arg
标记目标,这样即使在翻译时也可以保证目标的变化。
// create a name
QString name("Joe");
// get the day of the week as string
QString weekday = QDate::currentDate().toString("dddd");
// format a text using paramters (%1, %2)
QString hello = QString("Hello %1. Today is %2.").arg(name).arg(weekday);
// This worked on Monday. Promise!
if(Qt::Monday == QDate::currentDate().dayOfWeek()) {
QCOMPARE(QString("Hello Joe. Today is Monday."), hello);
} else {
QVERIFY(QString("Hello Joe. Today is Monday.") != hello);
有时你需要在你的代码中直接使用unicode字符。你需要记住如何在使用QChar
和QString
类来标记它们。
// Create a unicode character using the unicode for smile :-)
QChar smile(0x263A);
// you should see a :-) on you console
qDebug() << smile;
// Use a unicode in a string
QChar smile2 = QString("\u263A").at(0);
QVERIFY(smile == smile2);
// Create 12 smiles in a vector
QVector<QChar> smilies(12);
smilies.fill(smile);
// Can you see the smiles
qDebug() << smilies;
上面这些示例展示了在Qt中如何轻松的处理unicode文本。对于非unicode文本,QByteArray类同样有很多方便的函数可以使用。阅读Qt帮助文档中QString部分,它有一些很好的示例。