# 常见的Qt类

大多数Qt类都派生自QObject类,它封装了Qt的核心概念。 但是框架中还有更多的类。 在继续了解QML以及如何扩展它之前,将先了解一些有用的基本的Qt类。

本节中显示的代码示例是使用Qt测试库编写的。 这样,可以确保代码正常工作,而无需围绕它构建整个程序。 测试库中的QVERIFYQCOMPARE函数用于断言某个条件。 这里将使用{}作用域来避免命名冲突。 希望这不要让人感到困惑。

# 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字符。 为此,需要记住如何为QCharQString类标识它们。

// 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

QListQString类一样通用,并提供了一套很棒的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社区总是很有帮助,当需求帮助时,提出确切的问题并提供一个简单的示例来显示需求是非常有帮助的,这将大大改善其他人的响应时间。 因此,请花一点时间让其他想要提供帮助的人的生活更轻松 😃 。

以下是作者认为必须阅读的一些类的文档:

这应该足够开始使用了。

最后更新: 2/3/2022, 4:21:42 PM