Qt5开发及实例V2.0-第二章Qt模板库工具类及控件
- 第2章 Qt 5模板库、工具类及控件
-
- 2.1 字符串类
-
- 2.1.1 操作字符串
- 2.1.2 查询字符串数据
- 2.1.3 字符串的转换
- 2.2 容器类
-
- 2.2.1 QList类、QLinkedList类和QVector类
- 2.2.2 QMap类和QHash类
- 2.3 QVariant类
- 2.4 算法及正则表达式
-
- 2.4.1 Qt 5常用算法
- 2.4.2 基本的正则表达式
- 2.5 控件
-
- 2.5.1 按钮组(Buttons)
- 2.5.2 输入部件组(Input Widgets)
- 2.5.3 显示控件组(Display Widgets)
- 2.5.4 空间间隔组(Spacers)
- 2.5.5 布局管理组(Layouts)
- 2.5.6 容器组(Containers)
- 2.5.7 项目视图组(Item Views)
- 2.5.8 项目控件组(Item Widgets)
- L2.1 字符串类QString:概念解析
- L2.5 Qt 5控件:概念解析
- 本章相关例程源码下载
-
- 1.Qt5开发及实例_CH201.rar 下载
- 2.Qt5开发及实例_CH202.rar 下载
- 3.Qt5开发及实例_CH203.rar 下载
- 4.Qt5开发及实例_CH204.rar 下载
- 5.Qt5开发及实例_CH205.rar 下载
- 6.Qt5开发及实例_CH206.rar 下载
- 7.Qt5开发及实例_CH207.rar 下载
- 8.Qt5开发及实例_CH208.rar 下载
- 9.Qt5开发及实例_CH209.rar 下载
- 10.Qt5开发及实例_CH210.rar 下载
第2章 Qt 5模板库、工具类及控件
2.1 字符串类
2.1.1 操作字符串
字符串有如下几个操作符。
(1)QString提供了一个二元的“+”操作符用于组合两个字符串,并提供了一个“+=”操作符用于将一个字符串追加到另一个字符串的末尾,例如:
QString str1 = "Welcome ";
str1=str1+"to you! ";
QString str2="Hello, ";
str2+="World! ";
其中,QString str1 = "Welcome "传递给QString一个const char*类型的ASCII字符串“Welcome”,它被解释为一个典型的以“\0”结尾的C类型字符串。这将会导致调用QString构造函数,来初始化一个QString字符串。其构造函数原型为:
QT_ASCII_CAST_WARN_CONSTRUCTOR QString::QString(const char* str)
(2)QString::append()函数具有与“+=”操作符同样的功能,实现在一个字符串的末尾追加另一个字符串,例如:
QString str1 = "Welcome ";
QString str2 = "to ";
str1.append(str2);
str1.append("you! ");
(3)组合字符串的另一个函数是QString::sprintf(),此函数支持的格式定义符和C++库中的函数sprintf()定义的一样。例如:
QString str;
str.sprintf("%s"," Welcome ");
str.sprintf("%s"," to you! ");
str.sprintf("%s %s"," Welcome ", "to you! ");
(4)Qt还提供了另一种方便的字符串组合方式,使用QString::arg()函数,此函数的重载可以处理很多的数据类型。此外,一些重载具有额外的参数对字段的宽度、数字基数或者浮点数精度进行控制。通常,相对于函数QString::sprintf(),函数QString::arg()是一个比较好的解决方案,因为它类型安全,完全支持Unicode,并且允许改变"%n"参数的顺序。例如:
QString str;
str=QString("%1 was born in %2.").arg("John").arg(1998);
(5)QString也提供了一些其他组合字符串的方法,包括如下几种。
① insert()函数:在原字符串特定的位置插入另一个字符串。
② prepend()函数:在原字符串的开头插入另一个字符串。
③ replace()函数:用指定的字符串代替原字符串中的某些字符。
(6)很多时候,去掉一个字符串两端的空白(空白字符包括回车字符“\n”、换行字符“\r”、制表符“\t”和空格字符“ ”等)非常有用,如获取用户输入的账号时。
① QString::trimmed()函数:移除字符串两端的空白字符。
② QString::simplified()函数:移除字符串两端的空白字符,使用单个空格字符“ ”代替字符串中出现的空白字符。
例如:
QString str=" Welcome \t to \n you! ";
str=str.trimmed();
2.1.2 查询字符串数据
查询字符串数据有多种方式,具体如下。
(1)函数QString::startsWith()判断一个字符串是否以某个字符串开头。此函数具有两个参数。第一个参数指定了一个字符串,第二个参数指定是否大小写敏感(默认情况下,是大小写敏感的),例如:
QString str="Welcome to you! ";
str.startsWith("Welcome",Qt::CaseSensitive);
str.startsWith("you",Qt::CaseSensitive);
(2)函数QString::endsWith()类似于QString::startsWith(),此函数判断一个字符串是否以某个字符串结尾。
(3)函数QString::contains()判断一个指定的字符串是否出现过,例如:
QString str=" Welcome to you! ";
str.contains("Welcome",Qt::CaseSensitive);
(4)比较两个字符串也是经常使用的功能,QString提供了多种比较手段。
① operator<(const QString&):比较一个字符串是否小于另一个字符串。如果是,则返回true。
② operator<=(const QString&):比较一个字符串是否小于等于另一个字符串。如果是,则返回true。
③ operator==(const QString&):比较两个字符串是否相等。如果相等,则返回true。
④ operator>=(const QString&):比较一个字符串是否大于等于另一个字符串。如果是,则返回true。
⑤ localeAwareCompare(const QString&,const QString&):静态函数,比较前后两个字符串。如果前面字符串小于后面字符串,则返回负整数值;如果等于则返回0;如果大于则返回正整数值。该函数的比较是基于本地(locale)字符集的,而且是平台相关的。
⑥ compare(const QString&,const QString&,Qt::CaseSensitivity):该函数可以指定是否进行大小写的比较,而大小写的比较是完全基于字符的Unicode编码值的,而且是非常快的,返回值类似于localeAwareCompare()函数。
2.1.3 字符串的转换
(1)QString::toInt()函数将字符串转换为整型数值,类似的函数还有toDouble()、toFloat()、toLong()、toLongLong()等。下面举个例子说明其用法:
QString str="125";
bool ok;
int hex=str.toInt(&ok,16);
int dec=str.toInt(&ok,10);
其中,int hex=str.toInt(&ok,16):调用QString::toInt()函数将字符串转换为整型数值。函数QString::toInt()有两个参数。
(2)QString提供的字符编码集的转换函数将会返回一个const char类型版本的QByteArray,即构造函数QByteArray(const char)构造的QByteArray对象。QByteArray类具有一个字节数组,它既可以存储原始字节(raw bytes),也可以存储传统的以“\0”结尾的8位的字符串。在Qt中,使用QByteArray比使用const char*更方便,且QByteArray也支持隐式共享。转换函数有以下几种。
① toAscii():返回一个ASCII编码的8位字符串。
② toLatin1():返回一个Latin-1(ISO8859-1)编码的8位字符串。
③ toUtf8():返回一个UTF-8编码的8位字符串(UTF-8是ASCII码的超集,它支持整个Unicode字符集)。
④ toLocal8Bit():返回一个系统本地(locale)编码的8位字符串。
下面举例说明其用法:
QString str=" Welcome to you! ";
QByteArray ba=str.toAscii();
qDebug()<<ba;
ba.append("Hello,World! ");
qDebug()<<ba.data();
其中,
(a) QByteArray ba=str.toAscii():通过QString::toAscii()函数,将Unicode编码的字符串转换为ASCII码的字符串,并存储在QByteArray对象ba中。
(b) qDebug()< © ba.append(“Hello,World!”):使用QByteArray::append()函数追加一个字符串。
一个NULL字符串就是使用QString的默认构造函数或者使用“(const char*)0”作为参数的构造函数创建的QString字符串对象;而一个空字符串是一个大小为0的字符串。一个NULL字符串一定是一个空字符串,而一个空字符串未必是一个NULL字符串。例如:
QString().isNull();
QString().isEmpty();
QString("").isNull();
QString("").isEmpty();
2.2 容器类
这样的数据类型包含了通常使用的大多数数据类型,包括基本数据类型(如int和double等)和Qt的一些数据类型(如QString、QDate和QTime等)。不过,Qt的QObject及其他的子类(如QWidget和Qdialog等)是不能够存储在容器中的,例如:
QList<QToolBar> list;
上述代码是无法通过编译的,因为这些类(QObject及其他的子类)没有复制构造函数和赋值操作运算符。
一个可代替的方案是存储QObject及其子类的指针,例如:
QList<QToolBar*> list;
Qt的容器类是可以嵌套的,例如:
QHash<QString, QList<double> >
Qt的容器类为遍历其中的内容提供了以下两种方法。
(1)Java风格的迭代器(Java-style iterators)。
(2)STL风格的迭代器(STL-style iterators),能够同Qt和STL的通用算法一起使用,并且在效率上也略胜一筹。
2.2.1 QList类、QLinkedList类和QVector类
表2.1列出了QList、QLinkedList和QVector容器的时间复杂度比较。
其中,“Amort.O(1)”表示,如果仅完成一次操作,可能会有O(n)行为;但是如果完成多次操作(如n次),平均结果将会是O(1)。
1.QList类
QList是迄今为止最常用的容器类,它存储给定数据类型T的一列数值。继承自QList类的子类有QItemSelection、QQueue、QSignalSpy及QStringList和QTestEventList。
QList不仅提供了可以在列表中进行追加的QList::append()和Qlist::prepend()函数,还提供了在列表中间完成插入操作的函数QList::insert()。相对于任何其他的Qt容器类,为了使可执行代码尽可能少,QList被高度优化。
QList维护了一个指针数组,该数组存储的指针指向QList存储的列表项的内容。因此,QList提供了基于下标的快速访问。
对于不同的数据类型,QList采取不同的存储策略,存储策略有以下几种。
(1)如果T是一个指针类型或指针大小的基本类型(即该基本类型占有的字节数和指针类型占有的字节数相同),QList会将数值直接存储在它的数组中。
(2)如果QList存储对象的指针,则该指针指向实际存储的对象。
下面举一个例子:
#include
int main(int argc,char *argv[])
{
QList<QString> list;
{
QString str("This is a test string");
list<<str;
}
qDebug()<<list[0]<< "How are you! ";
return 0;
}
2.QLinkedList类
QLinkedList是一个链式列表,它以非连续的内存块保存数据。
QLinkedList不能使用下标,只能使用迭代器访问它的数据项。与QList相比,当对一个很大的列表进行插入操作时,QLinkedList具有更高的效率。
3.QVector类
QVector在相邻的内存中存储给定数据类型T的一组数值。在一个QVector的前部或者中间位置进行插入操作的速度是很慢的,这是因为这样的操作将导致内存中的大量数据被移动,这是由QVector存储数据的方式决定的。
QVector既可以使用下标访问数据项,也可以使用迭代器访问数据项。继承自QVector类的子类有QPolygon、QPolygonF和QStack。
4.Java风格迭代器遍历容器
对于每一个容器类,Qt都提供了两种类型的Java风格迭代器数据类型,即只读访问和读写访问,其分类见表2.2。
(1)QList只读遍历方法。
【例】(简单)(CH201)通过控制台程序实现QList只读遍历方法。
其具体代码如下:
#include
#include
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list;
list<<1<<2<<3<<4<<5;
QListIterator<int> i(list);
for(;i.hasNext();)
qDebug()<<i.next();
return a.exec();
}
其中,
(a) 头文件中已经包含了QList的头文件。
(b)Qt的一些类,如QString、QList等,不需要QCoreApplication的支持也能够工作,但是,在使用Qt编写应用程序时,如果是控制台应用程序,则建议初始化一个QCoreApplication对象,Qt 5.8创建控制台项目时生成的main.cpp源文件中默认就创建了一个QcoreApplication对象;如果是GUI图形用户界面程序,则会初始化一个QApplication对象。
© QListIterator i(list):以该list为参数初始化一个QListIterator对象i。此时,迭代点处在第一个列表项“1”的前面(注意,并不是指向该列表项)。
(d) for(;i.hasNext():调用QListIterator::hasNext()函数检查当前迭代点之后是否有列表项。
最后程序的运行结果为:
1 2 3 4 5
上例是QListIterator对列表进行向后遍历的函数,而对列表进行向前遍历的函数有如下几种。
QListIterator::toBack():将迭代点移动到最后一个列表项的后面。
QListIterator::hasPrevious():检查当前迭代点之前是否具有列表项。
QListIterator::previous():返回前一个列表项的内容并将迭代点移动到前一个列表项之前。
除此之外,QListIterator提供的其他函数还有如下几种。
toFront():移动迭代点到列表的前端(第一个列表项的前面)。
peekNext():返回下一个列表项,但不移动迭代点。
peekPrevious():返回前一个列表项,但不移动迭代点。
findNext():从当前迭代点开始向后查找指定的列表项,如果找到,则返回true,此时迭代点位于匹配列表项的后面;如果没有找到,则返回false,此时迭代点位于列表的后端(最后一个列表项的后面)。
findPrevious():与findNext()类似,不同的是它的方向是向前的,查找操作完成后的迭代点在匹配项的前面或整个列表的前端。
(2)QListIterator是只读迭代器,它不能完成列表项的插入和删除操作。
读写迭代器QMutableListIterator除了提供基本的遍历操作(与QListIterator的操作相同)外,还提供了insert()插入操作函数、remove()删除操作函数和修改数据函数等。
【例】(简单)(CH202)通过控制台程序实现QList读写遍历方法。
具体代码如下:
#include
#include
int main(int argc,char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list;
QMutableListIterator<int> i(list);
for(int j=0;j<10;++j)
i.insert(j);
for(i.toFront();i.hasNext();)
qDebug()<<i.next();
for(i.toBack();i.hasPrevious();)
{
if(i.previous()%2==0)
i.remove();
else
i.setValue(i.peekNext()*10);
}
for(i.toFront();i.hasNext();)
qDebug()<<i.next();
return a.exec();
}
其中,
(a) i.insert(j):通过QMutableListIterator::insert()插入操作,为该列表插入10个整数值。
(b) for(i.toFront();i.hasNext()、qDebug()< © for(i.toBack();i.hasPrevious(){…}:移动迭代器的迭代点到列表的后端,对列表进行遍历。如果前一个列表项的值为偶数,则将该列表项删除;否则,将该列表项的值修改为原来的10倍。
(d) i.setValue(i.peekNext()*10):函数QMutableListIterator::setValue()修改遍历函数next()、previous()、findNext()和findPrevious()跳过的列表项的值,但不会移动迭代点的位置。对于findNext()和findPrevious()有些特殊:当findNext()(或findPrevious())查找到列表项的时候,setValue()将会修改匹配的列表项;如果没有找到,则对setValue()的调用将不会进行任何修改。
最后编译,运行此程序,程序运行结果如下:
0 1 2 3 4 5 6 7 8 9
10 30 50 70 90
5.STL风格迭代器遍历容器
对于每一个容器类,Qt都提供了两种类型的STL风格迭代器数据类型:一种提供只读访问;另一种提供读写访问。由于只读类型的迭代器的运行速度要比读写迭代器的运行速度快,所以应尽可能地使用只读类型的迭代器。STL风格迭代器的两种分类见表2.3。
【例】(简单)(CH203)使用STL风格迭代器。
实现代码如下:
#include
#include
int main(int argc,char *argv[])
{
QCoreApplication a(argc, argv);
QList<int> list;
for(int j=0;j<10;j++)
list.insert(list.end(),j);
QList<int>::iterator i;
for(i=list.begin();i!=list.end();++i)
{
qDebug()<<(*i);
*i=(*i)*10;
}
QList<int>::const_iterator ci;
for(ci=list.constBegin();ci!=list.constEnd();++ci)
qDebug()<<*ci;
return a.exec();
}
其中,
(a) list.insert(list.end(),j):使用QList::insert()函数插入10个整数值。此函数有两个参数:第一个参数是QList::iterator类型,表示在该列表项之前插入一个新的列表项(使用QList::end()函数返回的迭代器,表示在列表的最后插入一个列表项);第二个参数指定了需要插入的值。
(b) for(i=list.begin();i!=list.end();++i){…}:在控制台输出列表的同时将列表的所有值增大10倍。这里用到两个函数:QList::begin()函数返回指向第一个列表项的迭代器;QList::end()函数返回一个容器最后列表项之后的虚拟列表项,为标记无效位置的迭代器,用于判断是否到达容器的底部。
最后编译、运行此应用程序,输出结果如下:
0 1 2 3 4 5 6 7 8 9
0 10 20 30 40 50 60 70 80 90
2.2.2 QMap类和QHash类
QMap类和QHash类具有非常类似的功能,它们的差别仅在于:
QHash具有比QMap更快的查找速度。
QHash以任意的顺序存储数据项,而QMap总是按照键Key的顺序存储数据。
QHash的键类型Key必须提供operator==()和一个全局的qHash(Key)函数,而QMap的键类型Key必须提供operator<()函数。
二者的时间复杂度比较见表2.4。
其中,“Amort.O(1)”表示,如果仅完成一次操作,则可能会有O(n)行为;如果完成多次操作(如n次),则平均结果将是O(1)。
1.QMap类
QMap提供了一个从类型为Key的键到类型为T的值的映射。
通常,QMap存储的数据形式是一个键对应一个值,并且按照键Key的顺序存储数据。为了能够支持一键多值的情况,QMap提供了QMap::insertMulti()和QMap::values()函数。存储一键多值的数据时,也可以使用QMultiMap容器,它继承自Qmap
2.QHash类
QHash具有与QMap几乎完全相同的API。QHash维护着一张哈希表(Hash Table),哈希表的大小与QHash的数据项的数目相适应。
QHash以任意的顺序组织它的数据。当存储数据的顺序无关紧要时,建议使用QHash作为存放数据的容器。QHash也可以存储一键多值形式的数据,它的子类QMultiHash实现了一键多值的语义。
3.Java风格迭代器遍历容器
对于每一个容器类,Qt都提供了两种类型的Java风格迭代器数据类型:一种提供只读访问;另一种提供读写访问。其分类见表2.5。
【例】(简单)(CH204)在QMap中的插入、遍历和修改。
实现代码如下:
#include
#include
int main(int argc,char *argv[])
{
QCoreApplication a(argc, argv);
QMap<QString,QString> map;
map.insert("beijing","111");
map.insert("shanghai","021");
map.insert("nanjing","025");
QMapIterator<QString,QString> i(map);
for(;i.hasNext();)
qDebug()<<" "<<i.key()<<" "<<i.next().value();
QMutableMapIterator<QString,QString> mi(map);
if(mi.findNext("111"))
mi.setValue("010");
QMapIterator<QString,QString> modi(map);
qDebug()<<" ";
for(;modi.hasNext();)
qDebug()<<" "<<modi.key()<<" "<<modi.next().value();
return a.exec();
}
其中,
(a) for(;i.hasNext()、qDebug()<<" “<::key();而在输出值的时候调用了QMapIterator ::next()。
(b) if(mi.findNext(“111”))、mi.setValue(“010”):首先查找某个<键,值>对,然后修改值。Java风格的迭代器没有提供查找键的函数。因此,在本例中通过查找值的函数QMutableMapIterator::findNext()来实现查找和修改。
最后编译、运行此程序,程序运行结果如下:
"beijing" "111"
"nanjing" "025"
"shanghai" "021"
"beijing" "010"
"nanjing" "025"
"shanghai" "021"
4.STL风格迭代器遍历容器
对于每一个容器类,Qt都提供了两种类型的STL风格迭代器数据类型:一种提供只读访问;另一种提供读写访问。其分类见表2.6。
【例】(简单)(CH205)功能与使用Java风格迭代器的例子基本相同。不同的是,这里通过查找键来实现值的修改。
实现代码如下:
#include
#include
int main(int argc,char *argv[])
{
QCoreApplication a(argc, argv);
QMap<QString,QString> map;
map.insert("beijing","111");
map.insert("shanghai","021");
map.insert("nanjing","025");
QMap<QString,QString>::const_iterator i;
for(i=map.constBegin();i!=map.constEnd();++i)
qDebug()<<" "<<i.key()<<" "<<i.value();
QMap<QString,QString>::iterator mi;
mi=map.find("beijing");
if(mi!=map.end())
mi.value()="010";
QMap<QString,QString>::const_iterator modi;
qDebug()<<" ";
for(modi=map.constBegin();modi!=map.constEnd();++modi)
qDebug()<<" "<<modi.key()<<" "<<modi.value();
return a.exec();
}
2.3 QVariant类
【例】(简单)(CH206)QVariant类的用法。
新建Qt Widgets Application(详见1.3.1节),项目名称为“myVariant”,基类选择“QWidget”,类名保持“Widget”不变,取消选择“创建界面”复选框。建好项目后,在widget.cpp文件中编写代码,具体内容如下。
其中,
(a) QVariant v(709):声明一个QVariant变量v,并初始化为一个整数。此时,QVariant变量v包含了一个整数变量。
(b) qDebug()< © QVariant w("How are you! "):声明一个QVariant变量w,并初始化为一个字符串。
(d) qDebug()< (e) QMapmap:声明一个QMap变量map,使用字符串作为键,QVariant变量作为值。
(f) qDebug()<