# 常见的Qt类
大多数Qt类都派生自QObject
类,它封装了Qt的核心概念。 但是框架中还有更多的类。 在继续了解QML以及如何扩展它之前,将先了解一些有用的基本的Qt类。
本节中显示的代码示例是使用Qt测试库编写的。 这样,可以确保代码正常工作,而无需围绕它构建整个程序。 测试库中的QVERIFY
和QCOMPARE
函数用于断言某个条件。 这里将使用{}
作用域来避免命名冲突。 希望这不要让人感到困惑。
# QString
通常,Qt中是基于Unicode进行文本处理的。 为此,使用QString
类。 它具有用户期望的从现代框架中获得的各种出色的功能。 对于8位数据,通常会使用QByteArray
类;而对于ASCII标识符,则使用QLatin1String
来维护内存;对于字符串列表,可以使用QList<QString>
或简单的QStringList
类(它派生自QList<QString>
)。
下面是一些如何使用QString
类的示例。 QString可以在栈上创建,但它将数据存储在堆上。 此外,当将一个字符串分配给另一个字符串时,不会复制数据——只是复制对数据的引用。 所以这真的很方便,让开发人员可以专注于代码逻辑而不是内存处理。 QString
使用引用计数器来了解何时可以安全地删除数据。 此功能称为隐式共享(Implicit Sharing) (opens new window),它在许多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());
可以在下面看到如何将数字转换为字符串并转换回来。还存在浮点或双精度等类型的转换函数,只需在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
类也有许多用于转换的辅助函数。 请阅读QString
的Qt文档,它包含大量优质的示例。
# 顺序容器
列表(list)、队列(queue)、向量(vector)或链表(linked-list)是顺序容器。 最常用的顺序容器是QList
类,它是一个基于模板的类,需要使用类型进行初始化。 它也是隐式共享的,并将内部数据存储在堆上。 所有容器类都应该在栈上创建。 通常永远不想使用new QList<T>()
,这也意味着永远不要在容器中使用new
。
QList
与QString
类一样通用,并提供了一套很棒的API来检索数据。 下面是一个小示例,说明如何使用一些新的C++ 11功能来使用和迭代列表。
// Create a simple list of ints using the new C++11 initialization
// for this you need to add "CONFIG += c++11" to your pro file.
QList<int> list{1,2};
// append another int
list << 3;
// We are using scopes to avoid variable name clashes
{ // iterate through list using Qt for each
int sum(0);
foreach (int v, list) {
sum += v;
}
QVERIFY(sum == 6);
}
{ // iterate through list using C++ 11 range based loop
int sum = 0;
for(int v : list) {
sum+= v;
}
QVERIFY(sum == 6);
}
{ // iterate through list using JAVA style iterators
int sum = 0;
QListIterator<int> i(list);
while (i.hasNext()) {
sum += i.next();
}
QVERIFY(sum == 6);
}
{ // iterate through list using STL style iterator
int sum = 0;
QList<int>::iterator i;
for (i = list.begin(); i != list.end(); ++i) {
sum += *i;
}
QVERIFY(sum == 6);
}
// using std::sort with mutable iterator using C++11
// list will be sorted in descending order
std::sort(list.begin(), list.end(), [](int a, int b) { return a > b; });
QVERIFY(list == QList<int>({3,2,1}));
int value = 3;
{ // using std::find with const iterator
QList<int>::const_iterator result = std::find(list.constBegin(), list.constEnd(), value);
QVERIFY(*result == value);
}
{ // using std::find using C++ lambda and C++ 11 auto variable
auto result = std::find_if(list.constBegin(), list.constBegin(), [value](int v) { return v == value; });
QVERIFY(*result == value);
}
# 关联容器
映射(map)、字典(dictionary)或集合(set)都是关联容器的示例。 它们使用键来存储值,以快速查找而闻名。 这里演示了使用最常用的关联容器QHash
,还演示了一些新的C++ 11特性。
QHash<QString, int> hash({{"b",2},{"c",3},{"a",1}});
qDebug() << hash.keys(); // a,b,c - unordered
qDebug() << hash.values(); // 1,2,3 - unordered but same as order as keys
QVERIFY(hash["a"] == 1);
QVERIFY(hash.value("a") == 1);
QVERIFY(hash.contains("c") == true);
{ // JAVA iterator
int sum =0;
QHashIterator<QString, int> i(hash);
while (i.hasNext()) {
i.next();
sum+= i.value();
qDebug() << i.key() << " = " << i.value();
}
QVERIFY(sum == 6);
}
{ // STL iterator
int sum = 0;
QHash<QString, int>::const_iterator i = hash.constBegin();
while (i != hash.constEnd()) {
sum += i.value();
qDebug() << i.key() << " = " << i.value();
i++;
}
QVERIFY(sum == 6);
}
hash.insert("d", 4);
QVERIFY(hash.contains("d") == true);
hash.remove("d");
QVERIFY(hash.contains("d") == false);
{ // hash find not successfull
QHash<QString, int>::const_iterator i = hash.find("e");
QVERIFY(i == hash.end());
}
{ // hash find successfull
QHash<QString, int>::const_iterator i = hash.find("c");
while (i != hash.end()) {
qDebug() << i.value() << " = " << i.key();
i++;
}
}
// QMap
QMap<QString, int> map({{"b",2},{"c",2},{"a",1}});
qDebug() << map.keys(); // a,b,c - ordered ascending
QVERIFY(map["a"] == 1);
QVERIFY(map.value("a") == 1);
QVERIFY(map.contains("c") == true);
// JAVA and STL iterator work same as QHash
# 文件读写
通常需要从文件中读取内容和写入文件。 QFile
实际上是一个QObject
类,大多数情况下,它是在栈上创建的。 QFile
包含通知用户何时可以读取数据的信号。 这允许异步读取数据块,直到读取整个文件。 为方便起见,它还允许以阻塞模式读取数据,这应该只用于数据量小的文件而非大文件。 幸运的是,在这些示例中,只使用了少量数据。
除了将文件中的原始数据读取到QByteArray
中,还可以使用QDataStream
读取数据类型,还可以使用QTextStream
读取Unicode字符串。 这里将展示如何使用。
QStringList data({"a", "b", "c"});
{ // write binary files
QFile file("out.bin");
if(file.open(QIODevice::WriteOnly)) {
QDataStream stream(&file);
stream << data;
}
}
{ // read binary file
QFile file("out.bin");
if(file.open(QIODevice::ReadOnly)) {
QDataStream stream(&file);
QStringList data2;
stream >> data2;
QCOMPARE(data, data2);
}
}
{ // write text file
QFile file("out.txt");
if(file.open(QIODevice::WriteOnly)) {
QTextStream stream(&file);
QString sdata = data.join(",");
stream << sdata;
}
}
{ // read text file
QFile file("out.txt");
if(file.open(QIODevice::ReadOnly)) {
QTextStream stream(&file);
QStringList data2;
QString sdata;
stream >> sdata;
data2 = sdata.split(",");
QCOMPARE(data, data2);
}
}
# 更多的类
Qt是一个丰富的应用程序框架。 因此,它有数千个类。 习惯所有这些类以及如何使用它们需要一些时间。 幸运的是,Qt有一个非常棒的文档,其中包含许多有用的示例。 大多数时候,搜索一个类,最常见的用例已经作为代码片段提供。 这意味着只需复制和调整这些片段就可以了。 此外,Qt源代码中的Qt示例也很有帮助。 确保这些内容可用且可搜索,能提高生活效率。 不要浪费时间。 Qt社区总是很有帮助,当需求帮助时,提出确切的问题并提供一个简单的示例来显示需求是非常有帮助的,这将大大改善其他人的响应时间。 因此,请花一点时间让其他想要提供帮助的人的生活更轻松 😃 。
以下是作者认为必须阅读的一些类的文档:
- QObject (opens new window), QString (opens new window), QByteArray (opens new window)
- QFile (opens new window), QDir (opens new window), QFileInfo (opens new window), QIODevice (opens new window)
- QTextStream (opens new window), QDataStream (opens new window)
- QDebug (opens new window), QLoggingCategory (opens new window)
- QTcpServer (opens new window), QTcpSocket (opens new window), QNetworkRequest (opens new window), QNetworkReply (opens new window)
- QAbstractItemModel (opens new window), QRegExp (opens new window)
- QList (opens new window), QHash (opens new window)
- QThread (opens new window), QProcess (opens new window)
- QJsonDocument (opens new window), QJSValue (opens new window)
这应该足够开始使用了。