配色: 字号:
第2章 Qt 5模板库、工具类及控件
2022-04-20 | 阅:  转:  |  分享 
  
第2章Qt5模板库、工具类及控件——字符串类01操作字符串操作字符串字符串有如下几个操作符。(1)QString提供了一
个二元的“+”操作符用于组合两个字符串,并提供了一个“+=”操作符用于将一个字符串追加到另一个字符串的末尾,例如:QString
str1="Welcome";str1=str1+"toyou!"; //str1="Welcometoyou!
"QStringstr2="Hello,";str2+="World!"; //str2="Hello,World!"其中
,QStringstr1="Welcome"传递给QString一个constchar类型的ASCII字符串“Wel
come”,它被解释为一个典型的以“\0”结尾的C类型字符串。这将会导致调用QString构造函数,来初始化一个QString字符
串。其构造函数原型为:QT_ASCII_CAST_WARN_CONSTRUCTORQString::QString(const
charstr)被传递的constchar类型的指针又将被QString::fromAscii()函数转换为Unicode
编码。默认情况下,QString::fromAscii()函数会将超过128的字符作为Latin-1进行处理(可以通过调用QTex
tCodec::setCodecForCString()函数改变QString::fromAscii()函数的处理方式)。操作
字符串(2)QString::append()函数具有与“+=”操作符同样的功能,实现在一个字符串的末尾追加另一个字符串,例如
:QStringstr1="Welcome";QStringstr2="to";str1.append(str2)
;//str1="Welcometo"str1.append("you!");//str1="Welcometoyou!
"(3)组合字符串的另一个函数是QString::sprintf(),此函数支持的格式定义符和C++库中的函数sprintf()
定义的一样。例如:QStringstr;str.sprintf("%s","Welcome"); //str="Welcom
e"str.sprintf("%s","toyou!"); //str="toyou!"str.sprintf("%s
%s","Welcome","toyou!"); //str="Welcometoyou!"操作字符
串(4)Qt还提供了另一种方便的字符串组合方式,使用QString::arg()函数,此函数的重载可以处理很多的数据类型。此外,一
些重载具有额外的参数对字段的宽度、数字基数或者浮点数精度进行控制。通常,相对于QString::sprintf()函数,QStri
ng::arg()函数是一个比较好的解决方案,因为其类型安全,完全支持Unicode,并且允许改变"%n"参数的顺序。例如:QSt
ringstr;str=QString("%1wasbornin%2.").arg("John").arg(1998);
//str="Johnwasbornin1998."其中,"%1"被替换为"John","%2"被替换为"1998"。(5
)QString也提供了一些其他组合字符串的方法,包括如下几种。①insert()函数:在原字符串特定的位置插入另一个字符串。②
prepend()函数:在原字符串的开头插入另一个字符串。③replace()函数:用指定的字符串代替原字符串中的某些字符。操
作字符串(6)很多时候,去掉一个字符串两端的空白(空白字符包括回车字符“\n”、换行字符“\r”、制表符“\t”和空格字符
“”等)非常有用,如获取用户输入的账号时。①QString::trimmed()函数:移除字符串两端的空白字符。②QStri
ng::simplified()函数:移除字符串两端的空白字符,使用单个空格字符“”代替字符串中出现的空白字符。例如:QStri
ngstr="Welcome\tto\nyou!";str=str.trimmed();
//str="Welcome\tto\nyou!"02查询字符串数据查询字符串数据(1)
QString::startsWith()函数判断一个字符串是否以某个字符串开头。此函数具有两个参数。第一个参数指定了一个字符串,
第二个参数指定是否大小写敏感(默认情况下,是大小写敏感的),例如:QStringstr="Welcometoyou!";s
tr.startsWith("Welcome",Qt::CaseSensitive); //返回truestr.startsW
ith("you",Qt::CaseSensitive); //返回false(2)QString::endsWith()函数类似
于QString::startsWith()函数,此函数判断一个字符串是否以某个字符串结尾。(3)QString::contain
s()函数判断一个指定的字符串是否出现过,例如:QStringstr="Welcometoyou!";str.conta
ins("Welcome",Qt::CaseSensitive); //返回true查询字符串数据(4)比较两个字符串也是经常使用
的功能,QString提供了多种比较手段。①operator<(constQString&):比较一个字符串是否小于另一个字符
串。如果是,则返回true。②operator<=(constQString&):比较一个字符串是否小于等于另一个字符串。如果
是,则返回true。③operator==(constQString&):比较两个字符串是否相等。如果相等,则返回true。④
operator>=(constQString&):比较一个字符串是否大于等于另一个字符串。如果是,则返回true。⑤loc
aleAwareCompare(constQString&,constQString&):静态函数,比较前后两个字符串。如果前
面字符串小于后面字符串,则返回负整数值;如果等于则返回0;如果大于则返回正整数值。该函数的比较是基于本地(locale)字符集的,
而且是与平台相关的。通常,该函数用于向用户显示一个有序的字符串列表。⑥compare(constQString&,const
QString&,Qt::CaseSensitivity):该函数可以指定是否进行大小写的比较,而大小写的比较是完全基于字符的Un
icode编码值的,而且是非常快的,返回值类似于localeAwareCompare()函数。03字符串的转换字符串的转换QStr
ing类提供了丰富的转换函数,可以将一个字符串转换为数值类型或者其他的字符编码集。(1)QString::toInt()函数将字符
串转换为整型数值,类似的函数还有toDouble()、toFloat()、toLong()、toLongLong()等。下面举个例
子说明其用法:QStringstr="125"; //初始化一个"125"的字符串boolok;inthex=str
.toInt(&ok,16); //ok=true,hex=293intdec=str.toInt(&ok,10); //ok=
true,dec=125其中,inthex=str.toInt(&ok,16):调用QString::toInt()函数将字符串
转换为整型数值。QString::toInt()函数有两个参数。第一个参数是一个bool类型的指针,用于返回转换的状态。当转换成功
时设置为true,否则设置为false。第二个参数指定了转换的基数。字符串的转换(2)QString提供的字符编码集的转换函数将会
返回一个constchar类型版本的QByteArray,即构造函数QByteArray(constchar)构造的QBy
teArray对象。QByteArray类具有一个字节数组,它既可以存储原始字节(rawbytes),也可以存储传统的以“\0”
结尾的8位的字符串。在Qt中,使用QByteArray比使用constchar更方便,且QByteArray也支持隐式共享。转
换函数有以下几种。①toAscii():返回一个ASCII编码的8位字符串。②toLatin1():返回一个Latin-1(I
SO8859-1)编码的8位字符串。③toUtf8():返回一个UTF-8编码的8位字符串(UTF-8是ASCII码的超集,它支
持整个Unicode字符集)。④toLocal8Bit():返回一个系统本地(locale)编码的8位字符串。字符串的转换下面举
例说明其用法:QStringstr="Welcometoyou!"; //初始化一个字符串对象QByteArrayb
a=str.toAscii(); //(a)qDebug()<); //(c)qDebug()<.toAscii():通过QString::toAscii()函数,将Unicode编码的字符串转换为ASCII码的字符串,并存储
在QByteArray对象ba中。(b)qDebug()<数支持输出Qt对象)。(c)ba.append("Hello,World!"):使用QByteArray::append()函数
追加一个字符串。字符串的转换一个NULL字符串就是使用QString的默认构造函数或者使用“(constchar)0”作为参数
的构造函数创建的QString字符串对象;而一个空字符串是一个大小为0的字符串。一个NULL字符串一定是一个空字符串,而一个空字符
串未必是一个NULL字符串。例如:QString().isNull(); //结果为trueQString().isEmpty()
; //结果为trueQString("").isNull(); //结果为falseQString("").isEmpty();
//结果为true第2章Qt5模板库、工具类及控件——容器类容器类这样的数据类型包含了通常使用的大多数数据类型,包括
基本数据类型(如int和double等)和Qt的一些数据类型(如QString、QDate和QTime等)。不过,Qt的QObje
ct及其他的子类(如QWidget和Qdialog等)是不能够存储在容器中的,例如:QListlist;上
述代码是无法通过编译的,因为这些类(QObject及其他的子类)没有复制构造函数和赋值操作运算符。一个可代替的方案是存储QObje
ct及其子类的指针,例如:QListlist;Qt的容器类是可以嵌套的,例如:QHashg,QList>其中,QHash的键类型是QString,它的值类型是QList。Qt的容器类
为遍历其中的内容提供了以下两种方法。(1)Java风格的迭代器(Java-styleiterators)。(2)STL风格的迭代
器(STL-styleiterators),能够同Qt和STL的通用算法一起使用,并且在效率上也略胜一筹。1.QList类012
.QLinkedList类3.QVector类QList类、QLinkedList类和QVector类4.Java风格迭代器遍历容
器5.STL风格迭代器遍历容器QList类、QLinkedList类和QVector类在开发一个较高性能需求的应用程序时,程序员会
比较关注这些容器类的运行效率。表2.1列出了QList、QLinkedList和QVector容器的时间复杂度比较。容器类查
找插入头部添加尾部添加QListO(1)O(n)Amort.O(1)Amort.O(1)QLinkedListO(n
)O(1)O(1)O(1)QVectorO(1)O(n)O(n)Amort.O(1)1.QList类QList是迄今为止最常
用的容器类,它存储给定数据类型T的一列数值。继承自QList类的子类有QItemSelection、QQueue、QSignalS
py、QStringList和QTestEventList。QList不仅提供了可以在列表中进行追加的QList::append(
)和Qlist::prepend()函数,还提供了在列表中间完成插入操作的QList::insert()函数。相对于任何其他的Qt
容器类,为了使可执行代码尽可能少,QList被高度优化。QList维护了一个指针数组,该数组存储的指针指向QList
储的列表项的内容。因此,QList提供了基于下标的快速访问。对于不同的数据类型,QList采取不同的存储策略,存储策略
有以下几种。(1)如果T是一个指针类型或指针大小的基本类型(即该基本类型占有的字节数和指针类型占有的字节数相同),QList
会将数值直接存储在它的数组中。(2)如果QList存储对象的指针,则该指针指向实际存储的对象。1.QList类下面举一个例子
:#includeintmain(intargc,charargv[]){ QList
list; //(a) { QStringstr("Thisisateststring"); list<<
str;//(b) }//(c) qDebug()<中,(a)QListlist:声明了一个QList栈对象。(b)list<操作运算符“<<”将一个QString字符串存储在该列表中。(c)程序中使用花括弧“{”和“}”括起来的作用域表明,此时QLis
t保存了对象的一个复制。2.QLinkedList类QLinkedList是一个链式列表,它以非连续的内存块保存数据。
QLinkedList不能使用下标,只能使用迭代器访问它的数据项。与QList相比,当对一个很大的列表进行插入操作时,QLi
nkedList具有更高的效率。3.QVector类QVector在相邻的内存中存储给定数据类型T的一组数值。在一个QVec
tor的前部或者中间位置进行插入操作的速度是很慢的,这是因为这样的操作将导致内存中的大量数据被移动,这是由QVector存储数据的
方式决定的。QVector既可以使用下标访问数据项,也可以使用迭代器访问数据项。继承自QVector类的子类有QPolygo
n、QPolygonF和QStack。4.Java风格迭代器遍历容器Java风格迭代器同STL风格迭代器相比,使用起来更简单方便,
不过这也是以轻微的性能损耗为代价的。对于每一个容器类,Qt提供了两种类型的Java风格迭代器数据类型,即只读迭代器类和读写迭代器类
,见表2.2。容器类只读迭代器类读写迭代器类QList,QQueueQListIteratorQMutabl
eListIteratorQLinkedListQLinkedListIteratorQMutableLinke
dListIteratorQVector,QStackQVectorIteratorQMutableVec
torIteratorJava风格迭代器的迭代点(Java-styleiteratorspoint)位于列表项的中间,而
不是直接指向某个列表项。4.Java风格迭代器遍历容器下面以QList为例,介绍两种Java风格迭代器的用法。QLinkedLis
t和QVector具有与QList相同的遍历接口,在此不再详细讲解。(1)QList只读遍历方法。【例】(简单)(CH201)通过
控制台程序实现QList只读遍历方法。其具体代码如下:#include #include<
QDebug> //(a)intmain(intargc,charargv[]){QCoreApplicationa
(argc,argv); //(b)QListlist;//创建一个QList栈对象listlis
t<<1<<2<<3<<4<<5; //用操作运算符“<<”输入五个整数QListIteratori(list
); //(c)for(;i.hasNext();) //(d)qDebug()<a.exec();}4.Java风格迭代器遍历容器其中,(a)?头文件中已经包含了QList的头文件。(b)Qt的
一些类,如QString、QList等,不需要QCoreApplication的支持也能够工作,但是,在使用Qt编写应用程序时,如
果是控制台应用程序,则建议初始化一个QCoreApplication对象,Qt5.11创建控制台项目时生成的main.cpp源文
件中默认创建了一个QCoreApplication对象;如果是GUI图形用户界面程序,则会初始化一个QApplication对象。
(c)?QListIteratori(list):以该list为参数初始化一个QListIterator对象i。此时,
迭代点处在第一个列表项“1”的前面(注意,并不是指向该列表项)。(d)?for(;i.hasNext();):调用QListIte
rator::hasNext()函数检查当前迭代点之后是否有列表项。如果有,则调用QListIterator::nex
t()函数进行遍历。next()函数将会跳过下一个列表项(即迭代点将位于第一个列表项和第二个列表项之间),并返回它跳过的列表项的内
容。最后程序的运行结果为:123454.Java风格迭代器遍历容器上例是QListIterator对列表进行向后遍历
的函数,而对列表进行向前遍历的函数有如下几种。QListIterator::toBack():将迭代点移动到最后一个列表项的
后面。QListIterator::hasPrevious():检查当前迭代点之前是否具有列表项。QListIterator
::previous():返回前一个列表项的内容并将迭代点移动到前一个列表项之前。除此之外,QListIterator
提供的其他函数还有如下几种。toFront():移动迭代点到列表的前端(第一个列表项的前面)。peekNext():返回下一个列表
项,但不移动迭代点。peekPrevious():返回前一个列表项,但不移动迭代点。findNext():从当前迭代点开始向后查找
指定的列表项,如果找到,则返回true,此时迭代点位于匹配列表项的后面;如果没有找到,则返回false,此时迭代点位于列表的后端(
最后一个列表项的后面)。findPrevious():与findNext()类似,不同的是,它的方向是向前的,查找操作完成后的迭代
点在匹配项的前面或整个列表的前端。4.Java风格迭代器遍历容器(2)QListIterator是只读迭代器,它不能完成列表
项的插入和删除操作。读写迭代器QMutableListIterator除提供基本的遍历操作(与QListIterator的操
作相同)外,还提供了insert()插入操作函数、remove()删除操作函数和修改数据函数等。【例】(简单)(CH202)通过控
制台程序实现QList读写遍历方法。具体代码如下:#include#includebug>intmain(intargc,charargv[]){ QCoreApplicationa(argc,arg
v); QListlist;//创建一个空的列表list QMutableListIteratori(li
st); //创建上述列表的读写迭代器 for(intj=0;j<10;++j) i.insert(j);//(a) for
(i.toFront();i.hasNext();) //(b) qDebug()<ck();i.hasPrevious();) //(c) { if(i.previous()%2==0)?i.remove()
; elsei.setValue(i.peekNext()10); //(d) } for(i.toFront();i.h
asNext();) //重新遍历并输出列表qDebug()<ava风格迭代器遍历容器其中,(a)i.insert(j):通过QMutableListIterator::insert(
)插入操作,为该列表插入10个整数值。(b)for(i.toFront();i.hasNext();)、qDebug()<next():将迭代器的迭代点移动到列表的前端,完成对列表的遍历和输出。(c)for(i.toBack();i.hasPrevi
ous();){…}:移动迭代器的迭代点到列表的后端,对列表进行遍历。如果前一个列表项的值为偶数,则将该列表项删除;否则,将该列表
项的值修改为原来的10倍。(d)i.setValue(i.peekNext()10):函数QMutableListIterat
or::setValue()修改遍历函数next()、previous()、findNext()和findPrevious(
)跳过的列表项的值,但不会移动迭代点的位置。对于findNext()和findPrevious()有些特殊:当findNext()
(或findPrevious())查找到列表项的时候,setValue()将会修改匹配的列表项;如果没有找到,则对setValue
()的调用将不会进行任何修改。最后编译,运行此程序,程序运行结果如下:012345678910305070
905.STL风格迭代器遍历容器对于每个容器类,Qt都提供了两种类型的STL风格迭代器数据类型:一种提供只读访问;另一种提供读写
访问。由于只读类型的迭代器的运行速度要比读写迭代器的运行速度快,所以应尽可能地使用只读类型的迭代器。STL风格迭代器的两种分类见表
2.3。STL风格迭代器的API是建立在指针操作基础上的。例如,“++”操作运算符移动迭代器到下一个项(item),而“”操作运
算符返回迭代器指向的项。容器类只读迭代器类读写迭代器类QList,QQueueQList::const_it
eratorQList::iteratorQLinkedListQLinkedList::const_itera
torQLinkedList::iteratorQVector,QStackQVector::const_
iteratorQVector::iterator5.STL风格迭代器遍历容器【例】(简单)(CH203)使用STL风格迭代
器。具体代码如下:#include#includeintmain(int
argc,charargv[]){ QCoreApplicationa(argc,argv); QListl
ist;//初始化一个空的QList列表 for(intj=0;j<10;j++) list.insert(list.
end(),j); //(a) QList::iteratori; //初始化一个QList::itera
tor读写迭代器 for(i=list.begin();i!=list.end();++i) //(b) {qDebug()<
<(i);i=(i)10; } //初始化一个QList::const_iterator读写迭代器 QLis
t::const_iteratorci; //在控制台输出列表的所有值 for(ci=list.constBegin(
);ci!=list.constEnd();++ci)qDebug()<风格迭代器遍历容器其中,(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()函数返回一个容器最后列表
项之后的虚拟列表项,为标记无效位置的迭代器,用于判断是否到达容器的底部。最后编译、运行此应用程序,输出结果如下:012
345678901020304050607080901
.QMap类022.QHash类3.Java风格迭代器遍历容器QMap类和QHash类4.STL风格迭代器遍历容器QMap类和QH
ash类QMap类和QHash类具有非常类似的功能,它们的差别仅在于:?QHash具有比QMap更快的查找速度。?QHash以
任意的顺序存储数据项,而QMap总是按照键Key的顺序存储数据。?QHash的键类型Key必须提供operator==()和一个
全局的qHash(Key)函数,而QMap的键类型Key必须提供operator<()函数。QMap和QHash的时间复杂度比较见
表2.4。容器类键查找插入平均最坏平均最坏QMapO(logn)O(logn)O(logn)O(logn
)QHashAmort.O(1)O(n)Amort.O(1)O(n)1.QMap类QMap提供了一个从类型为Key的
键到类型为T的值的映射。通常,QMap存储的数据形式是一个键对应一个值,并且按照键Key的顺序存储数据。为了能够支持一键多值的情况
,QMap提供了QMap::insertMulti()和QMap::values()函数。存储一键多值
的数据时,也可以使用QMultiMap容器,它继承自QMap。2.QHash类QHash具有与QMap
几乎完全相同的API。QHash维护着一张哈希表(HashTable),哈希表的大小与QHash的数据项的数目相适应。QHash
以任意的顺序组织它的数据。当存储数据的顺序无关紧要时,建议使用QHash作为存放数据的容器。QHash也可以存储一键多值形式的数据
,它的子类QMultiHash实现了一键多值的语义。3.Java风格迭代器遍历容器对于每一个容器类,Qt都提供了两种
类型的Java风格迭代器数据类型:一种提供只读访问;另一种提供读写访问。Java风格迭代器的两种分类见表2.5。容器类只读迭代
器类读写迭代器类QMap,QMultiMapQMapIteratorQMutableMa
pIteratorQHash,QMultiHashQHashIterator>QMutableHashIterator【例】(简单)(CH204)在QMap中的插入、例(CH204).txt遍
历和修改例(CH204).txt。具体代码例(CH204).txt。其中,(a)for(;i.hasNext();)、i.nex
t()、qDebug()<<""<的键和值时,调用的函数是不同的。在输出键的时候,调用QMapIterator::key();而在输出值的时候调用QMap
Iterator::value(),为兼容不同编译器内部的算法,保证输出正确,在调用函数前必须先将迭代点移动到下一个位
置。(b)if(mi.findNext("111"))、mi.setValue("010"):首先查找某个<键,值>对,然后修改
值。Java风格的迭代器没有提供查找键的函数。因此,在本例中通过查找值的函数QMutableMapIterator::f
indNext()来实现查找和修改。3.Java风格迭代器遍历容器最后编译、运行此程序,程序运行结果如下:"beijing"
"111""nanjing" "025""shanghai" "021""beijing" "010""nanjing"
"025""shanghai" "021"4.STL风格迭代器遍历容器对于每一个容器类,Qt都提供了两种类型的STL风格迭
代器数据类型:一种提供只读访问;另一种提供读写访问。STL风格迭代器的两种分类见表2.6。容器类只读迭代器类读写迭代器类QMa
p,QMultiMapQMap::const_iteratorQMap::
iteratorQHash,QMultiHashQHash::const_iterato
rQHash::iterator【例】(简单)(CH205)功能与使用Java风格迭代器的例子基本相同。不同的是,这
里通过查找键来例(CH205).txt实现值的修改。具体例(CH205).txt代码例(CH205).txt。其中,(a)mi.
value()="010":将新的值直接赋给QMap::iterator::value()返回的
结果,因为该函数返回的是<键,值>对其中值的引用。第2章Qt5模板库、工具类及控件——QVariant类QVariant类【例
】(简单)(CH206)QVariant类的用法。新建QtWidgetsApplication(详见1.3.1节),项目名称为
“myVariant”,基类选择“QWidget”,类名保持“Widget”不变,取消选择“创建界面”复选框。建好项目后,在wid
get.cpp文件例(CH206).txt中编写代码,具体例(CH206).txt内容例(CH206).txt。其中,(a)QV
ariantv(709):声明一个QVariant变量v,并初始化为一个整数。此时,QVariant变量v包含了一个整数变量。(
b)qDebug()<并输出。(c)QVariantw("Howareyou!"):声明一个QVariant变量w,并初始化为一个字符串。(d
)qDebug()<转换为字符串并输出。(e)QMapmap:声明一个QMap变量map,使用字符串作为键,QV
ariant变量作为值。(f)qDebug()<r>():在QVariant变量中保存了一个QColor对象,并使用模板QVariant::value()还原为QColor,然后
输出。QVariant类(g)if(slv.type()==QVariant::StringList):QVariant::ty
pe()函数返回存储在QVariant变量中的值的数据类型。QVariant::StringList是Qt定义的一个QVarian
t::type枚举类型的变量。Qt的常用QVariant::type枚举类型变量见表2.7。变量对应的类型变量对应的类型QVa
riant::Invalid无效类型QVariant::TimeQTimeQVariant::RegionQRegionQVari
ant::LineQLineQVariant::BitmapQBitmapQVariant::PaletteQPaletteQVa
riant::BoolboolQVariant::ListQListQVariant::BrushQBrushQVariant::
SizePolicyQSizePolicyQVariant::SizeQSizeQVariant::StringQStringQV
ariant::CharQCharQVariant::MapQMapQVariant::ColorQColorQVariant::
StringListQStringListQVariant::CursorQCursorQVariant::PointQPoint
QVariant::DateQDateQVariant::PenQPenQVariant::DateTimeQDateTimeQV
ariant::PixmapQPixmapQVariant::DoubledoubleQVariant::RectQRectQVa
riant::FontQFontQVariant::ImageQImageQVariant::IconQIconQVariant:
:UserType用户自定义类型QVariant类最后,运行上述程序的结果如下:709"Howareyou!"QVarian
t(int,709)709QVariant(double,709.709)709.709QVariant(QString,"
Howareyou!")"Howareyou!"QVariant(QColor,QColor(ARGB1,1,0
,0))QColor(ARGB1,1,0,0)"A""B""C""D“第2章Qt5模板库、工具类及控件——算法及正则表达式
01Qt5常用算法Qt5常用算法【例】(简单)(CH207)几个常用算法。#include>#includeintmain(intargc,charargv[]){ QCoreApplicati
ona0(argc,argv); doublea=-19.3,b=9.7; doublec=qAbs(a); //(a)
doublemax=qMax(b,c); //(b) intbn=qRound(b); //(c) intcn=qRoun
d(c); qDebug()<<"a="<"<bn; qDebug()<<"cn=qRound(c)="<用qDebug()函数输出所有的计算结果 qDebug()<<"qSwap(bn,cn):"<<"bn="<<回double型数值a的绝对值,并赋值给c(c=19.3)。(b)doublemax=qMax(b,c):函数qMax()返回
两个数值中的最大值(max=c=19.3)。(c)intbn=qRound(b):函数qRound()返回与一个浮点数最接近的
整数值,即四舍五入返回一个整数值(bn=10,cn=19)。(d)qSwap(bn,cn):函数qSwap()交换两数的值。最后
,编译、运行上述程序,输出结果如下:a=-19.3b=9.7c=qAbs(a)=19.3qMax(b,c)=19.3bn=
qRound(b)=10cn=qRound(c)=19qSwap(bn,cn):bn=19cn=1002基本的正则表达
式基本的正则表达式正则表达式由表达式(expressions)、量词(quantifiers)和断言(assertions)组成。
(1)最简单的表达式是一个字符。字符集可以使用表达式如“[AEIOU]”,表示匹配所有的大写元音字母;使用“[^AEIOU]”,表
示匹配所有非元音字母,即辅音字母;连续的字符集可以使用表达式如“[a-z]”,表示匹配所有的小写英文字母。(2)量词说明表达式出现
的次数,如“x[1,2]”表示“x”可以至少有一个,至多有两个。在计算机语言中,标识符通常要求以字母或下画线(也称下画线)开头,后
面可以是字母、数字和下画线。满足条件的标识符表示为:"[A-Za-z_]+[A-Za-z_0-9]"其中,表达式中的“+”表
示“[A-Za-z_]”至少出现一次,可以出现多次;“”表示“[A-Za-z_0-9]”可以出现零次或多次。基本的正则表达式正则
表达式的量词见表2.8。量词含义量词含义E?匹配0次或1次E[n,]至少匹配n次E+匹配1次或多次E[,m]最多匹配m次E
匹配0次或多次E[n,m]至少匹配n次,最多匹配m次E[n]匹配n次??基本的正则表达式(3)“^”“$”“\b”都是正则表达式
的断言,正则表达式的断言见表2.9。符号含义符号含义^表示在字符串开头进行匹配\B非单词边界$表示在字符串结尾进行匹配(?
=E)表示表达式后紧随E才匹配\b单词边界(?!E)表示表达式后不跟随E才匹配例如,若要只有在using后面是namespace时
才匹配using,则可以使用“using(?=E\s+namespace)”(此处“?=E”后的“\s”表示匹配一个空白字符,下
同)。如果使用“using(?!E\s+namespace)”,则表示只有在using后面不是namespace时才匹配using
。如果使用“using\s+namespace”,则匹配为usingnamespace。第2章Qt5模板库、工具类及控件——
控件01按钮组(Buttons)按钮组(Buttons)按钮组(Buttons)如图2.1所示。按钮组(Buttons)中各个按
钮的名称依次解释如下。?PushButton:按钮。?ToolButton:工具按钮。?RadioButton:单选按
钮。??CheckBox:复选框。?CommandLinkButton:命令链接按钮。?DialogButtonB
ox:对话框按钮盒。按钮组(Buttons)【例】(简单)(CH208)以QPushButton为例介绍按钮的用法。(1)新建Qt
WidgetsApplication(详见1.3.1节),项目名为“PushButtonTest”,基类选择“QWidget”
选项,类名命名为“MyWidget”,取消“创建界面”复选框的选中状态。(2)在头文件“mywidget.h”中的具体代码如下:#
ifndefMYWIDGET_H#defineMYWIDGET_H?#include?classMyWi
dget:publicQWidget{Q_OBJECT?public:MyWidget(QWidgetparent
=0);~MyWidget();};?#endif//MYWIDGET_H按钮组(Buttons)(3)在源文件“mywi
dget.cpp”中的具体代码如下:#include"mywidget.h"#include#
include#includeMyWidget::MyWidget(QWidg
etparent):QWidget(parent){setMinimumSize(200,120);setMax
imumSize(200,120);QPushButtonquit=newQPushButton("Quit"
,this);quit->setGeometry(62,40,75,30);quit->setFont(QFon
t("Times",18,QFont::Bold));connect(quit,SIGNAL(clicked())
,qApp,SLOT(quit()));}MyWidget::~MyWidget(){}按钮组(Buttons)(4)在源文
件“main.cpp”中的具体代码如下:#include"mywidget.h"#includei
ntmain(intargc,charargv[]){QApplicationa(argc,argv);MyWi
dgetw;w.setGeometry(100,100,200,120);w.show();returna.e
xec();}(5)QPushButton实例的运行结果如图2.2所示。021.QDateTime类2.QTimer类输入部件组(
InputWidgets)输入部件组(InputWidgets)输入部件组(InputWidgets)如图2.3所示,组中各
个部件的名称依次解释如下。?ComboBox:组合框。?FontComboBox:字体组合框。?LineEdit:行
编辑框。?TextEdit:文本编辑框。?PlainTextEdit:纯文本编辑框。?SpinBox:数字显示框(自
旋盒)。?DoubleSpinBox:双自旋盒。?TimeEdit:时间编辑。?DateEdit:日期编辑。?Da
te/TimeEdit:日期/时间编辑。?Dial:拨号。?HorizontalScrollBar:横向滚动条。?Ve
rticalScrollBar:垂直滚动条。?HorizontalSlider:横向滑块。?VerticalSlide
r:垂直滑块。?KeySequenceEdit:按键序列编辑框。1.QDateTime类Date/TimeEdit对应于Q
DateTime类,在Qt5中可以使用它来获得系统时间。通过QDateTime::currentDateTime()来获取本地系
统的时间和日期信息。可以通过date()和time()来返回datetime中的日期和时间部分,典型代码如下:QLabeld
atalabel=newQLabel();QDateTimedatatime=newQDateTime(QDateTim
e::currentDateTime());datalabel->setText(datatime->date().toStrin
g());datalabel->show();2.QTimer类定时器(QTimer)的使用非常简单,只需要以下几个步骤就可以完
成定时器的应用。(1)新建一个定时器。QTimertime_clock=newQTimer(parent);(2)连接这个
定时器的信号和槽,利用定时器的timeout()。connect(time_clock,SIGNAL(timeout()),th
is,SLOT(slottimedone()));即定时时间一到就会发送timeout()信号,从而触发slottimedone(
)槽去完成某件事情。(3)开启定时器,并设定定时周期。定时器定时有两种方式:start(inttime)和setSingleSh
ot(true)。其中,start(inttime)表示每隔“time”秒就会重启定时器,可以重复触发定时,利用stop()将定
时器关掉;而setSingleShot(true)则是仅启动定时器一次。工程中常用的是前者,例如:time_clock->sta
rt(2000);031.GraphicsView2.TextBrowser3.QQuickWidget显示控件组(Displ
ayWidgets)显示控件组(DisplayWidgets)显示控件组(DisplayWidgets)如图2.4所示。显示
控件组(DisplayWidgets)中各个控件的名称依次解释如下。?Label:标签。?TextBrowser:文本浏览
器。?GraphicsView:图形视图。?CalendarWidget:日历。?LCDNumber:液晶数字。?P
rogressBar:进度条。?HorizontalLine:水平线。?VerticalLine:垂直线。?OpenG
LWidget:开放式图形库工具。?QQuickWidget:嵌入QML工具。显示控件组(DisplayWidgets)1.
GraphicsViewGraphicsView对应于QGraphicsView类,提供了Qt5的图形视图框架,其具体用法将
在本书第7章详细介绍。2.TextBrowserTextBrowser对应于QTextBrowser类。QTextBrowse
r类继承自QTextEdit,而且仅是只读的,对里面的内容不能进行更改,但是相对于QTextEdit来讲,它还具有链接文本的作用。
QTextBrowser的属性有以下几点:modified:constbool//通过布尔值来说明其内容是否被修改open
ExternalLinks:boolopenLinks:boolreadOnly:constboolsearchPa
ths:QStringListsource:QUrlundoRedoEnabled:constbool通过以上的属性
设置,可以设定QTextBrowser是否允许外部链接,是否为只读属性,外部链接的路径及链接的内容,是否可以进行撤销等操作。QTe
xtBrowser还提供了几种比较有用的槽(SLOTS),即virtualvoidbackward()virtualvoid
forward()virtualvoidhome()显示控件组(DisplayWidgets)3.QQuickWidget
这是Qt5.3发布的一个组件,传统QWidget程序可以用它来嵌入QML代码,为Qt开发者将桌面应用迁移到QtQuick提供了
方便,但目前在QML中尚不能嵌入其他非QML窗口,因为QML的渲染机制和QWidget的是不一样的。04空间间隔组(Spacers
)空间间隔组(Spacers)空间间隔组(Spacers)如图2.5所示。空间间隔组(Spacers)中各个控件的名称依次解释如下
。?HorizontalSpacer:水平间隔。?VerticalSpacer:垂直间隔。具体应用见2.5.9节中的小综合
例子。05布局管理组(Layouts)布局管理组(Layouts)布局管理组(Layouts)如图2.6所示。布局管理组(Layo
uts)中各个控件的名称依次解释如下。VerticalLayout:垂直布局。HorizontalLayout:横向(水平)布
局。GridLayout:网格布局。FormLayout:表单布局。061.创建窗口2.使用布局容器组(Containers)
容器组(Containers)容器组(Containers)如图2.7所示。容器组(Containers)中各个控件的名称依次解释
如下。?GroupBox:组框。?ScrollArea:滚动区域。?ToolBox:工具箱。?TabWidget:
标签小部件。?StackedWidget:堆叠部件。?Frame:帧。?Widget:小部件。?MDIArea:MDI
区域。?DockWidget:停靠窗体部件。?QAxWidget:封装Flash的ActiveX控件。容器组(Contain
ers)下面介绍Widget对应QWidget类的用法。Widget是使用Qt编写的图形用户界面(GUI)应用程序的基本生成块。
每个GUI组件,如按钮、标签或文本编辑器,都是一个Widget,并可以放置在现有的用户界面中或作为单独的窗口显示。每种类型的组件
都是由QWidget的特殊子类提供的,而QWidget又是QObject的子类。QWidget是所有QtGUI界面类的基类,它
接收鼠标、键盘及其他窗口事件,并在显示器上绘制自己。通过传入QWidget构造函数的参数(或者调用QWidget::setWind
owFlags()和QWidget::setParent()函数)可以指定一个窗口部件的窗口标识(windowflags)和父
窗口部件。窗口部件的窗口标识(windowflags)定义了窗口部件的窗口类型和窗口提示(hint)。窗口类型指定了窗口部件的窗
口系统属性(window-systemproperties),一个窗口部件只有一个窗口类型。窗口提示定义了顶层窗口的外观,一个窗
口可以有多个提示(提示能够进行按位或操作)。没有父窗口部件的Widget对象是一个窗口,窗口通常具有一个窗口边框(frame)和一
个标题栏。QMainWindow和所有的QDialog对话框子类都是经常使用的窗口类型,而子窗口部件通常处在父窗口部件的内部,没有
窗口边框和标题栏。QWidget窗口部件的构造函数为:QWidget(QWidgetparent=0,Qt::WindowF
lagsf=0)容器组(Containers)QWidget定义的窗口类型为Qt::WindowFlags枚举类型,它们的可用性
依赖于窗口管理器是否支持它们。QWidget不是一个抽象类,它可用作其他Widget的容器,并很容易作为子类来创建定制Widget
。它经常用于创建放置其他Widget的窗口。对于QObject,可使用父对象创建Widget以表明其所属关系,这样可以确保删除不再
使用的对象。使用Widget,这些父子关系就有了更多的意义,每个子类都显示在其父级所拥有的屏幕区域内。也就是说,当删除窗口时,其包
含的所有Widget也都被自动删除。1.创建窗口如果Widget未使用父级进行创建,则在显示时视为窗口或顶层Widget。由于顶层
Widget没有父级对象类来确保在其不再被使用时就被删除,所以需要开发人员在应用程序中对其进行跟踪。例如,使用QWidget创建和
显示具有默认大小的窗口:QWidgetwindow=newQWidget();window->resize(320,2
40);window->show();QPushButtonbutton=newQPushButton(tr("Pres
sme"),window); //(a)button->move(100,100);button->show();其中,(
a)QPushButtonbutton=newQPushButton(tr("Pressme"),window);
:通过将window作为父级传递给其构造器来向窗口添加子Widget:button。在这种情况下,向窗口添加按钮并将其放置在特定位
置。该按钮现在为窗口的子项,并在删除窗口时被同时删除。请注意,隐藏或关闭窗口不会自动删除该按钮。2.使用布局通常,子Widget是
通过使用布局对象在窗口中进行排列的,而不是通过指定位置和大小进行排列的。在此,构造一个并排排列的标签和行编辑框Widget:QLa
bellabel=newQLabel(tr("Name:"));QLineEditlineEdit=newQL
ineEdit();QHBoxLayoutlayout=newQHBoxLayout();layout->addWidg
et(label);layout->addWidget(lineEdit);window->setLayout(layout);构
造的布局对象管理通过addWidget()函数提供Widget的位置和大小。布局本身是通过调用setLayout()函数提供给窗口
的。布局仅可通过其对所管理的Widget(或其他布局)的显示效果来展示。2.使用布局由于Widget可包含其他Widget,所以布
局可用来提供按不同层次分组的Widget。这里,要在显示查询结果的表视图上方、窗口顶部的行编辑框旁显示一个标签:QLabelq
ueryLabel=newQLabel(tr("Query:"));QLineEditqueryEdit=newQ
LineEdit();QTableViewresultView=newQTableView();QHBoxLayout
queryLayout=newQHBoxLayout();queryLayout->addWidget(queryLabe
l);queryLayout->addWidget(queryEdit);QVBoxLayoutmainLayout=ne
wQVBoxLayout();mainLayout->addLayout(queryLayout);mainLayout->ad
dWidget(resultView);window->setLayout(mainLayout);07项目视图组(ItemVi
ews)项目视图组(ItemViews)项目视图组(ItemViews)如图2.8所示。项目视图组(ItemViews)中各
个控件的名称依次解释如下。?ListView:清单视图。?TreeView:树形视图。?TableView:表视图。?
ColumnView:列视图。项目视图组(ItemViews)下面介绍此处的TableView与2.5.8节中的Table
Widget的区别,其具体区别见表2.10。区别点QTableViewQTableWidget继承关系?QTableWidge
t继承自QTableView使用数据模型setModel可以使用setModel设置数据模型setModel是私有函数,不能使用该
函数设置数据模型显示复选框setCheckState没有函数实现复选框QTableWidgetItem类中的setCheckSta
te(Qt::Checked);可以设置复选框与QSqlTableModel绑定QTableView能与QSqlTableMode
l绑定QTableWidget不能与QSqlTableModel绑定Qt5中引入了模型/视图框架用于完成数据与表现的分离,这在
Qt5中称为InterView框架,类似于常用的MVC设计模式。MVC设计模式是起源于Smalltalk语言的一种与用户界面相关
的设计模式。MVC包括三个元素:模型(Model)表示数据;视图(View)是用户界面;控制(Controller)定义了用户在界
面上的操作。?模型:所有的模型都基于QAbstractItemModel类,该类是抽象基类。?视图:所有的视图都从抽象基类QA
bstractItemView继承。项目视图组(ItemViews)QTableWidget继承自QTableView。QSql
TableModel能够与QTableView绑定,但不能与QTableWidget绑定。例如:QSqlTableModelm
odel=newQSqlTableModel;model->setTable("employee");model->setE
ditStrategy(QSqlTableModel::OnManualSubmit);model->select();model
->removeColumn(0);//不显示IDmodel->setHeaderData(0,Qt::Horizontal
,tr("Name"));model->setHeaderData(1,Qt::Horizontal,tr("Salary"
));QTableViewview=newQTableView;view->setModel(model);view->
show();项目视图组(ItemViews)视图与模型绑定时,模型必须使用new创建,否则视图不能随着模型的改变而改变。下面是
错误的写法:QStandardItemModelmodel(4,2);model.setHeaderData(0,Qt::Ho
rizontal,tr("Label"));model.setHeaderData(1,Qt::Horizontal,tr(
"Quantity"));ui.tableView->setModel(&model);for(introw=0;row
<4;++row){ for(intcolumn=0;column<2;++column) { QMod
elIndexindex=model.index(row,column,QModelIndex()); model.se
tData(index,QVariant((row+1)(column+1))); }}项目视图组(ItemViews)
下面是正确的写法:QStandardItemModelmodel;model=newQStandardItemModel
(4,2);ui.tableView->setModel(model);model->setHeaderData(0,Qt::H
orizontal,tr("Label"));model->setHeaderData(1,Qt::Horizontal,t
r("Quantity"));for(introw=0;row<4;++row){ for(intcolum
n=0;column<2;++column) { QModelIndexindex=model->index(
row,column,QModelIndex()); model->setData(index,QVariant((row+
1)(column+1))); }}08项目控件组(ItemWidgets)项目控件组(ItemWidgets)项目控件
组(ItemWidgets)如图2.9所示。项目控件组(ItemWidgets)中各个控件的名称依次解释如下。?ListW
idget:清单控件。?TreeWidget:树形控件。?TableWidget:表控件。项目控件组(ItemWidge
ts)【例】(难度中等)(CH209)创建具有复选框的树形控件。在Qt中,树形控件称为QTreeWidget,而控件里的树形节点称
为QTreeWidgetItem。这种控件有时很有用处。例如,利用飞信软件群发短信时,选择联系人的界面中就使用了有复选框的树形控件
,如图2.10所示。项目控件组(ItemWidgets)要实现这种界面其实很简单。首先在Qt的设计器中拖曳出一个QTreeWid
get,然后在主窗口中编写一个函数init初始化界面,连接树形控件的节点改变信号itemChanged(QTreeWidgetIt
emitem,intcolumn),实现这个信号即可。具体步骤如下。(1)新建QtWidgetsApplication
(详见1.3.1节),项目名称为“TreeWidget”,基类选择“QWidget”,类名保持“Widget”不变,保持“创建界面
”复选框的选中状态。(2)双击“widget.ui”文件,打开Qt的设计器,拖曳出一个QTreeWidget控件。(3)在头文件“
widget.h”中添加代码:#include在类Widget声明中添加代码:public:
voidinit();voidupdateParentItem(QTreeWidgetItemitem);public
slots:voidtreeItemChanged(QTreeWidgetItemitem,intcolumn);项
目控件组(ItemWidgets)(4)在源文件“widget.cpp”的类Widget构造函数中添加代码:init();con
nect(ui->treeWidget,SIGNAL(itemChanged(QTreeWidgetItem,int)),t
his,SLOT(treeItemChanged(QTreeWidgetItem,int)));在此文件中实现例(CH209
)-1.txt各个函数的具体例(CH209)-1.txt代码例(CH209)-1.txt。函数treeItemChanged()例
(CH209)-2.txt的具体实现例(CH209)-2.txt代码例(CH209)-2.txt。函数updateParentIt
em()例(CH209)-3.txt的具体实现例(CH209)-3.txt代码例(CH209)-3.txt。(5)运行结果如图2.
10所示。09小综合例子小综合例子【例】(难度一般)(CH210)将上面的几个控件综合起来使用。具体步骤如下。(1)新建
QtWidgetsApplication(详见1.3.1节),项目名称为“Test”,基类选择“QDialog”,类名保持“D
ialog”不变,保持“创建界面”复选框的选中状态。(2)双击dialog.ui文件,打开Qt的设计器,中间的空白视窗为一个Par
entWidget,接着需要建立一些ChildWidget。在左边的工具箱中找到所需要的Widget:拖曳出一个Label、一
个LineEdit(用于输入文字)、一个HorizontalSpacer及两个PushButton。现在不需要花太多时间在这
些Widget的位置编排上,以后可利用Qt的LayoutManage进行位置的编排。(3)设置Widget的属性:?选择Lab
el,确定objectName属性为“label”,并且设定text属性为“&CellLocation”。?选择LineEd
it,确定objectName属性为“lineEdit”。?选择第一个按钮,将其objectName属性设定为“okButton
”,enabled属性设为“false”,text属性设为“OK”,并将default属性设为“true”。?选择第二个按钮,
将其objectName属性设为“cancelButton”,并将text属性设为“Cancel”。?将表单背景的windowT
itle属性设为“GoToCell”。小综合例子初始的设计效果如图2.11所示。小综合例子(4)运行工程,此时看到界面中的la
bel会显示一个“&”。为了解决这个问题,选择“编辑”→“EditBuddies”(编辑伙伴)命令,在此模式下,可以设定伙伴。
选中label并拖曳至lineEdit,然后放开,此时会有一个红色箭头由label指向lineEdit,如图2.12所示。小综合例
子此时,再次运行该程序,label的“&”不再出现,如图2.13所示,此时label与lineEdit这两个Widget互为伙伴了
。选择“编辑”→“EditWidgets”(编辑控件)命令,即可离开此模式,回到原本的编辑模式。小综合例子(5)对Widge
t进行位置编排的布局(layout)。?利用?Ctrl?键一次选取多个Widget,首先选取label与lineEdit;接着单
击上方工具栏中的“”水平布局按钮。类似地,首先选取Spacer与两个PushButton,接着单击上方工具栏中的“”按钮
即可,水平布局后的效果如图2.14所示。?选取整个form(不选任何项目),单击上方工具栏中的“”垂直布局按钮。?单击上方工
具栏中的“”调整大小按钮,整个表单就自动调整为合适的大小。此时,出现红色的线将各Widget框起来,被框起来的Widget表示已
经被选定为某种布局了,如图2.15所示。小综合例子(6)单击“”编辑Tab键顺序按钮,每个Widget上都会出现一个方框显示数字
,这就是表示按下Tab键的顺序,调整到需要的顺序,如图2.16所示。单击“”编辑元件按钮,即可离开此模式,回到原来的编辑模式。此
时,运行该程序后的效果如图2.17所示。(7)在头文件“dialog.h”中的Dialog类声明中添加语句:privatesl
ots:voidon_lineEdit_textChanged();小综合例子(8)在源文件“dialog.cpp”中的构造函
数中添加代码如下:ui->setupUi(this); //(a)QRegExpregExp("[A-Za-z]
[1-9][0-9]{0,2}"); //正则表达式限制输入字元的范围ui->lineEdit->setValidator(new
QRegExpValidator(regExp,this)); //(b)connect(ui->okButton,SIGNAL
(clicked()),this,SLOT(accept())); //(c)connect(ui->cancelButton,S
IGNAL(clicked()),this,SLOT(reject()));其中,(a)ui->setupUi(this);:在
构造函数中使用该语句进行初始化。在产生界面之后,setupUi()将根据namingconvention对slot进行连接,即连
接on_objectName_signalName()与objectName中signalName()的signal。在此,set
upUi()会自动建立下列的signal-slot连接:connect(ui->lineEdit,SIGNAL(textChang
ed(QString)),this,SLOT(on_lineEdit_textChanged()));(b)ui->lineEd
it->setValidator(newQRegExpValidator(regExp,this)):使用QRegExpVali
dator并且搭配正则表示法"[A-Za-z][1-9][0-9]{0,2}"。这样,只允许第一个字元输入大小写英文字母,后面接1
位非0的数字,再接0~2位可为0的数字。小综合例子(c)connect(…):连接了“OK”按钮至QDialog的accept(
)槽函数,以及“Cancel”按钮至QDialog的reject()槽函数。这两个槽函数都会关闭Dialog视窗,但是accept
()会设定Dialog的结果至QDialog::Accepted(结果设为1),而reject()则会设定为QDialog::Re
jected(结果设为0),因此可以根据这个结果来判断按下的是“OK”按钮还是“Cancel”按钮。实现槽函数on_lineEdi
t_textChanged():voidDialog::on_lineEdit_textChanged(){ui->okBut
ton->setEnabled(ui->lineEdit->hasAcceptableInput());}小综合例子(9)运行此工
程。当在lineEdit中输入A12后,“OK”按钮将自动变为可用状态,当单击“Cancel”按钮时则会关闭视窗,其最终运行效果如
图2.18所示。第2章Qt5模板库、工具类及控件——字符串类QString:概念解析L1隐式共享隐式共享QString类
采用隐式共享技术,将深拷贝和浅拷贝有机地结合起来。下面通过一个例子来具体介绍隐式共享是如何工作的。QStringstr1="da
ta"; //初始化一个内容为“data”的字符串QStringstr2=str1; //(a)str2[3]=''e''; /
/(b)str2[0]=''f''; //(c)str1=str2;//(d)其中,(a)QStringstr2=str1:将该
字符串对象str1赋值给另一个字符串str2(由QString的复制构造函数完成str2的初始化),此时,str2="data"。
在对str2赋值的时候,将发生一次浅拷贝,导致两个QString对象都指向同一个数据结构。该数据结构除保存字符串“data”外,还
保存了一个引用计数器,以记录字符串数据的引用次数。在这里,因为str1和str2指向同一个数据结构,所以计数器的值为2。(b)s
tr2[3]=''e'':对QString对象str2的修改将会导致一次深拷贝,使得str2对象指向一个新的、不同于str1所指的数
据结构(该数据结构的引用计数为1,因为只有str2指向这个数据结构),同时修改原来的str1指向的数据结构,设置它的引用计数为1(
此时,只有QString对象str1指向该数据结构)。继而在这个str2指向的、新的数据结构上完成数据的修改。引用计数为1意味着这
个数据没有被共享。此时,str2="date",str1="data"。隐式共享(c)str2[0]=''f'':进一步对QStr
ing对象str2进行修改,但这个操作不会引起任何形式的复制,因为str2指向的数据结构没有被共享。此时,str2="fate",
str1="data"。(d)str1=str2:将str2赋值给str1。此时,str1将它指向的数据结构的引用计数器的值修改
为0,也就是说,没有QString对象再使用这个数据结构了。因此,str1指向的数据结构将会从内存中释放掉。该操作的结果是,QSt
ring对象str1和str2都指向字符串为“fate”的数据结构,该数据结构的引用计数为2。Qt中支持隐式共享的类,还包括:?
所有的容器类。?QByteArray、QBrush、QPen、QPalette、QBitmap、QImage、QPixmap、Q
Cursor、QDir、QFont和QVariant等。L2内存分配策略内存分配策略QString在一个连续的内存块中保存字符串数
据。当字符串的长度不断增长时,QString需要重新分配内存空间,以便有足够的空间保存增加的字符串。QString使用的内存分配策
略如下。?每次分配4个字符空间,直到大小为20。?在20~4084之间,QString分配的内存块大小以2倍的速度增长。?
从4084开始,每次以2048个字符大小(4096字节,即4KB)的步长增长。下面举例具体说明QString在后台是如何运行
的:QStringtest(){ QStringstr; for(inti=0;i<9000;++i) str.append
("a"); returnstr;}首先定义了一个QString栈对象str,然后为它追加9?000个字符。根据QString的
内存分配策略,这个循环操作将导致14次内存重分配:4、8、16、20、52、116、244、500、1012、2?036、4?0
84、6?132、8?180、10?228。最后一次内存重分配操作后,QString对象str具有一个10?228个Unicode
字符大小的内存块(20?456字节),其中有9?000个字符空间被使用(18?000字节)。第2章Qt5模板库、工具类及控件—
—Qt5控件:概念解析00Qt::WindowFlags枚举类型Qt::WindowFlags枚举类型Qt::WindowFla
gs枚举类型有以下几种形式。?Qt::Widget:QWidget构造函数的默认值,如果新的窗口部件没有父窗口部件,则它是一个独立的窗口,否则就是一个子窗口部件。?Qt::Window:无论是否有父窗口部件,新窗口部件都是一个窗口,通常有一个窗口边框和一个标题栏。?Qt::Dialog:新窗口部件是一个对话框,它是QDialog构造函数的默认值。?Qt::Sheet:新窗口部件是一个Macintosh表单(sheet)。?Qt::Drawer:新窗口部件是一个Macintosh抽屉(drawer)。?Qt::Popup:新窗口部件是一个弹出式顶层窗口。?Qt::Tool:新窗口部件是一个工具(tool)窗口,它通常是一个用于显示工具按钮的小窗口。如果一个工具窗口有父窗口部件,则它将显示在父窗口部件的上面,否则,将相当于使用了Qt::WindowStaysOnTopHint提示。?Qt::ToolTip:新窗口部件是一个提示窗口,没有标题栏和窗口边框。?Qt::SplashScreen:新窗口部件是一个欢迎窗口(splashscreen),它是QSplashScreen构造函数的默认值。Qt::WindowFlags枚举类型?Qt::Desktop:新窗口部件是桌面,它是QDesktopWidget构造函数的默认值。?Qt::SubWindow:新窗口部件是一个子窗口,而无论该窗口部件是否有父窗口部件。此外,Qt还定义了一些控制窗口外观的窗口提示(这些窗口提示仅对顶层窗口有效)。?Qt::MSWindowsFiredSizeDialogHint:为Windows系统上的窗口装饰一个窄的对话框边框,通常这个提示用于固定大小的对话框。?Qt::MSWindowsOwnDC:为Windows系统上的窗口添加自身的显示上下文(displaycontext)菜单。?Qt::X11BypassWindowManagerHint:完全忽视窗口管理器,它的作用是产生一个根本不被管理的无窗口边框的窗口(此时,用户无法使用键盘进行输入,除非手动调用QWidget::activateWindow()函数)。?Qt::FramelessWindowHint:产生一个无窗口边框的窗口,此时用户无法移动该窗口和改变它的大小。?Qt::CustomizeWindowHint:关闭默认的窗口标题提示。?Qt::WindowTitleHint:为窗口装饰一个标题栏。?Qt::WindowSystemMenuHint:为窗口添加一个窗口系统菜单,并尽可能地添加一个关闭按钮。Qt::WindowFlags枚举类型?Qt::WindowMinimizeButtonHint:为窗口添加一个“最小化”按钮。?Qt::WindowMaximizeButtonHint:为窗口添加一个“最大化”按钮。?Qt::WindowMinMaxButtonsHint:为窗口添加一个“最小化”按钮和一个“最大化”按钮。?Qt::WindowContextHelpButtonHint:为窗口添加一个“上下文帮助”按钮。?Qt::WindowStaysOnTopHint:告知窗口系统,该窗口应该停留在所有其他窗口的上面。?Qt::WindowType_Mask:一个用于提取窗口标识中的窗口类型部分的掩码。枚举类型Qt::WindowFlags低位的1个字节用于定义窗口部件的窗口类型,0x00000000~0x00000012共定义了11个窗口类型。Qt::WindowFlags的高位字节定义了窗口提示,窗口提示能够进行位或操作,例如:Qt::WindowContextHelpButtonHint|Qt::WindowMaximizeButtonHint当Qt::WindowFlags的窗口提示部分全部为0时,窗口提示不起作用。当有一个窗口提示被应用时,若要其他的窗口提示起作用,则必须使用位或操作(如果窗口系统支持这些窗口提示的话)。例如:Qt::WindowFlagsflags=Qt::Window;widget->setWindowFlags(flags);widget窗口部件是一个窗口,它有一般窗口的外观(有窗口边框、标题栏、“最小化”按钮、“最大化”按钮和“关闭”按钮等),此时窗口提示不起作用。例如:flags|=Qt::WindowTitleHint;widget->setWindowFlags(flags);Qt::WindowFlags枚举类型上述代码的执行将会使窗口提示发挥作用。在Windows系统中,widget窗口部件是一个窗口,它仅有标题栏,没有“最小化”按钮、“最大化”按钮和“关闭”按钮等。而X11窗口管理器忽略了窗口提示Qt::WindowTitleHint,例如,在红旗Linux工作站和SUSE系统上,上述代码并不起作用。在Windows系统中,如果需要添加一个“最小化”按钮,则必须重新设置窗口部件的窗口标识(在红旗Linux工作站和SUSE系统上,下面的窗口提示也被忽略了),具体如下:flags|=Qt::WindowMinimizeButtonHint;widget->setWindowFlags(flags);如果要取消设置的窗口0提示,则具体代码如下:flags&=Qt::WindowType_Mask;widget->setWindowFlags(flags);
献花(0)
+1
(本文系gdp8egnayq4...首藏)