Qt 5 模板库、工具类及控件
Qt 5 模板库、工具类及控件
2.1 字符串类
C++提供了两种字符串,一种是C语言风格的以“\0”字符结尾的字符数组,另一种是字符串类String。
而Qt字符串类QString功能更强大。
QString类提供了丰富的操作、查询和转换等函数。
该类还进行了使用隐式共享(implicit sharing)、高效的内存分配策略
2.1.1 操作字符串
二元的“+”操作符用于组合两个字符串,并提供了一个“+=”操作符用于将一个字符串追加到另一个字符串的末尾,例如:
1
2
3
4QStirng str1 = "Welcome";
str1 = str1 + "to you!"; // str1 = "Welcome to you!"
QString str2 = "Hello, ";
str+="World! "; //str2 = "Hello,World!"QString::append()函数具有与“+=”操作符同样的功能,实现在字符串末尾追加另一个字符串。
1
2
3
4QString str1 = "Welcome ";
QString str2 = "to";
str1.append(str2); //str1 = “Welcome to”
str2.append("you!"); //strl = "welcome to you"组合字符串的另一个函数是QString::sprintf(),此函数支持的格式定义符合C++库中的函数sprintf()定义的一样。
1
2
3
4QString str;
str.sprintf("%s","Welcome"); //str = "Welcome"
str.sprintf("%s","to you!"); //str = "to you!"
str.sprintf("%s %s","Welcome","to you!"); //str = "Welcome to you!"Qt提供了另一个方便的字符串组合方式,使用QString::arg()函数,此函数的重载可以处理很多的数据类型。此外,一些重载具有额外的参数对字段的宽度、数字基数或者浮点数精度进行控制。通常,相对于函数QString::sprintf(),函数**QString::arg()**是一个比较好的解决方案。
1
2
3QString str;
str = QString("%1 was born in %2.").arg("John").arg(1982);
//str = "John was born in 1982."QString 也提供了一些其他组合字符串的方法,包括如下:
- insert()函数:在原字符串特定的位置插入另一个字符串;
- prepend()函数:在原字符串的开头插入另一个字符串;
- replace()函数:用指定的字符串代替原版字符串的某些字符。
很多时候去掉一个字符串的两端的空白(空白字符包括回车字符“\n”、换行字符“\r”、制表符“\t”和空格字符“ ”等)非常有用,如获取用户输入的账号时。
QString::timmed()函数:移除字符串两端的空白字符;
QString::simplified()函数:移除字符串两端的空白字符,使用单个空格字符“ ”代替字符串中出现的空白字符。
1
2QString str = "Welcome \t to \n you!";
str = str.timmed(); //str = "Welcome \t to \n you!"如果使用str = str.simplified(),则str的结果是“Wecome to you!”。
2.1.2 查询字符串数据
QString::startsWith()判断一个字符串是否以某个字符串开头。此函数具有两个参数,第一个参数指定了一个字符串,第二个参数指定是否大小写敏感(默认情况下,是大小写敏感的)。
1
2
3QString str = "Welocme to you! "
str.startsWith("Welcome",Qt::CaseSensitive); //返回true
str.startsWith("you",Qt::CaseSensitive); //返回false函数QString::endsWith()类似于QString::startsWith(),此函数判断一个字符串是否以某个字符串结尾。
函数QString::contains()判断一个指定的字符串是否出现过。
1
2QString str = "Welcome to you! ";
str.contains("Welcome", Qt::CaseSensitive); //返回true
2.1.3 字符串的转换
QString 类提供了一个丰富的转换函数,可以将一个字符串转换为数值类型或者其他的字符编码集。
QString::toInt()函数将字符串转换为整数数值,类似的函数还有toDouble()、toFloat()、toLong、toLong()、toLongLong()等。
1
2
3
4QString str = "125"; // 初始化一个“125”字符串
bool ok;
int hex = str.toInt(&ok,16); // ok = true ,hex = 293
int dec = str.toInt(&ok,10); // ok = true ,dec = 125QByteArray():字符编码集的转换函数
1
2
3
4
5QString str = "Welcome to you!"; // 初始化一个字符串的对象
QByteArray ba = str.toAscii(); //通过QString::toAscii()函数,将Unicode编码的字符串转换为ASCII码的字符串,并存储在QByteArray对象ba中
qDebug() << ba; //使用qDebug()函数输出转换后的字符串(qDebug()支持输出Qt对象)
ba.append("Hello,World!"); //使用QByteArray::append()函数追加一个字符串
qDebug()<<ba.data(); //输出最后的结果注意注意:NULL字符串和空(empty)字符串的区别
一个NULL字符串就是使用QString的默认构造函数或者使用“(const char* )0”作为参数的构造函数创建的QString字符串对象;而一个空字符串是一个大小为0的字符串。一个NULL字符串一定是一个空字符串,而一个空字符串未必是一个NULL字符串。
1
2
3
4QString().isNULL(); //结果为true
QString().isEmpty(); //结果为true
QString("").isNULL(); //结果为false
QString("").isEmpty(); //结果为true
2.2 容器类
Qt提供了一组通用的基于模板的容器类。对比C++的标准库中的容器类,Qt的这些容器类更轻量、更容易使用。此外,Qt的容器类在速度、内存消耗和内联(inline)代码方面进行了优化(较少的内联代码将会减少可执行程序的大小)。
存储在Qt容器中的数据必须是可赋值的数据类型,也就是说,这种数据类型提供一个默认的构造函数(不需要参数的构造函数)、一个复制构造函数和一个赋值操作运算符。
这样的数据类型包含了通常使用的大多数数据类型,包括基本数据类型(如int和double等)和Qt的一些数据类型(如QString、QDate和QTime等)。不过,Qt的QObject及其他的子类(如QWidget和Qdialog等)是不能够存储在容器类的。
1 | QList<QToolBar> list; //无法通过编译,因为这些类(QOBject及其他的子类)没有复制构造函数和赋值操作运算符 |
一个可替代的方案是在存储QObject及其子类的指针。例如:
1 | QList<QToolBar*> list; |
Qt的容器类是可以嵌套的。例如:
1 | QHash<QString,QList<double> > //QHash的键类型是QString,它的值类型是QList<double>. |
2.2.1 QList类、QLinkedList类和QVector类
经常使用到的Qt容器类有QList、QLinkedList和Vector等。
在开发一个较高性能需求的应用程序时,程序员会比较关注这些容器类的运行效率。
各容器的时间复杂度比较:
| 容器类 | 查找 | 插入 | 头部添加 | 尾部添加 |
|---|---|---|---|---|
| QList | O(1) | O(n) | Amort.O(1) | Amort.O(1) |
| QLinkedList | O(n) | O(1) | O(1) | O(1) |
| QVector | O(1) | O(n) | O(n) | Amort.O(1) |
其中,“Amort.O(1)”表示,如果仅完成一次操作,可能会有O(n)行为;但是如果完成多次操作(如n次),平均结果将会是O(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
存储对象的指针,则该指针指向实际存储的对象。 1
2
3
4
5
6
7
8
9
10
11
int main(int argc,char *argc[]){
QList<QString> list; //声明了一个QList<QString>栈对象。
{
QString str("This is a test string");
list<<str; //通过操作符运算符“<<”将一个QString字符串存储在该列表中。
}
qDebug()<<list[0]<<"How are you!";
return 0;
}
//程序中使用花括弧“{”和“}”括起来的作用域表明,此时QList<T>保存了对象的一个复制。
QLinkedList类
QLinkedList
是一个链式列表,它以非连续的内存块保存数据。 QLinkedList
不能使用下标,只能使用迭代器访问它的数据项。与QList相比,当对一个很大的列表进行插入操作时,QLinkedList具有更高的效率。 QVector类
QVector
在相邻的内存中存储给定类型T的一组数值。在一个QVector的前部或者中间位置进行插入操作的速度是很慢的,这是因为这样的操作将导致内存中的大量数据被移动,这是由QVector存储数据的方式决定的。 QVector
既可以使用下标访问数据项,也可以使用迭代器访问数据项。继承自QVector类的子类有QPolygon、QPolygonF和QStack。 QLIst遍历
1)QList只读遍历
1
2
3
4
5
6
7
8
9
int main(int argc,char *argv[]){
QList<int> list; //创建一个空的列表list
list<<1<<2<<3<<4<<5; //利用操作运算符 “<<” 输入五个整数值
QListIterator<int> i(list); //以该list为参数初始化一个QListIterator对象i
for(;i.hasNext();)
qDebug()<<i.next();
return 0;
}上述例子是QListIterator
对列表进行向后遍历的函数,而对列表进行向前遍历的函数有: - QListIterator
::toBack():将迭代点移动到最后一个列表项的后面。 - QListIterator
::hasPrevious(): 检查当前迭代点之前是否具有列表项。 - QListIterator
::previous():返回前一个列表项的内容并将迭代点移动到前一个列表项之前。
QListIterator
是只读迭代器,它不能完成列表项的插入和删除操作。 2)QList读写遍历方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main(int argc,char *argv[]){
QList<int> list; //创建一个空的列表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 0;
}- QListIterator
STL风格迭代器遍历容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main(int argc,char *argv[])
{
QList<int> list; //创建一个空的列表list
for(int j = 0;j < 10;j++){ //使用QList<T>::insert()函数插入十个整数值
list.insert(list.end(),j);//两个参数:1、插入新的列表项 2、指定插入的值
}
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 0;
}
//输出结果
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<()函数。
时间复杂度的比较
| 容器类 | 键查找 | 键查找 | 插入 | 插入 |
|---|---|---|---|---|
| 平均 | 最坏 | 平均 | 最坏 | |
| QMap | O(log n) | O(log n ) | O(log n) | O(log n) |
| QHash | Amort.O(1) | O(n) | Amort.O(1) | O(n) |
QMap类
QMap<key,T>提供了一个从类型为key的键到类型为T的值得映射。
通常,QMap存储的数据形式是一个键对应一个值,并且按照键key的次序存储数据。为了能够支持一键多值得情况。QMap提供了QMap<Key,T>::insertMulti()和QMap<Key,T>::value()函数。存储一键多值的数据时,也可以使用QMultiMap<Key,T>容器,它继承QMap。
QHash类
QHash<key,T>具有与QMap几乎完全相同的API。QHash维护着一张哈希表(Hash Table),哈希表的大小与QHash的数据项的数目相适应。
QHash以任意的顺序组织它的数据。当存储数据的顺序无关紧要时,建议使用QHash作为存放数据的容器。QHash也可以存储一键多值形式的数据,它的子类QMultiHash<Key,T>实现了一键多值的语义。
QMap中的插入、遍历和修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(int argc,char *argv[]){
QMap<QString,QString> map; //创建一个QMap栈对象
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 0;
}STL风格迭代器遍历容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main(){
QMap<QString,QString> map;
map.insert("beijing","111");
map.insert("shanghai","021");
map.insert("jinan","0531");
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>::iterator::value()返回的结果
QMap<QString,QString>::const_iterator modi;
qDebug()<<" ";
for(modi = map.constBegin();modi != map.constEnd();++modi)
qDebug()<<" "<<modi.key()<<" "<modi.value();
return 0;
}
2.3 QVariant类
QVariant类类似于C++的联合(union)数据类型。它不仅能够包括很多Qt类型的值,包括QColor、QBrush、QFont、QPen、QRect、QString和QSize等,也能够存放Qt的内容类型的值。Qt的很多功能都是建立在QVariant基础上的,如Qt的对象属性及数据库功能等。
例如:新建Qt Gui应用,基类选择“QWidget”,类名保持“Widget”不变,取消选择“创建界面”复选框。建好项目后,在widget。cpp文件中编写代码。
1 |
|
2.4 算法及正则表达式
2.4.1 Qt5x常用算法
1 |
|
2.4.2 基本的正则表达式
使用正则表达式可以方便地完成处理字符串的一些操作,如验证、查找、替换和分隔等。
2.5 控件
2.5.1 按钮组(Buttons)
- Push Button:按钮
- Tool Button:工具按钮
- Radio Button:单选按钮
- Check Box:复选框
- Command Link Button:命令链接按钮
- Button Box:按钮盒
2.5.2 输入控件组(Input Widgets)
- Combo Box:组合框
- Font Combo Box:字体组合框
- Line Edit:行编辑
- Text Edit:文本编辑
- Plain Text Edit:纯文本编辑
- Spain Box:数字显示框(自旋盒)
- Double Spin Box:双自旋盒
- Time Edit:时间编辑
- Date Edit:日期编辑
- Dial:拨号
- Horizon Scroll Bar:横向滚动条
- Vertical Scroll Bar:垂直滚动条
- Horizontal Slider:横向滑块
- Vertical Slider:垂直滑块
QDateTime类
1
2
3
4
5Qlabel * datalabel = new Qlabel();
//获取本地本地系统的时间和日期信号
QdateTime *datatime = new QdataTime(QdateTime::currentDateTime());
datalabel->setText(datatime->date().toString());
datalabel->show();Qtimer类
定时器(Qtimer)的使用非常简单
1)新建一个定时器
1
QTimer *time_clock = new QTimer(parent);
2)连接这个定时器的信号和槽,利用定时器的timeout()。
1
2connect(time_clock,SIGNAL(timeout()),this,SLOT(slottimedone()));
//即定时时间一到就会发送timeout()信号,从而触发slottimedone()槽去完成某件事情。3)开启定时器,并设定定时周期
1
time_clock->start(2000);
2.5.6 容器组(Containers)
- Group Box:组框
- Scroll Area:滚动区域
- Widget:小部件
- Tab Widget:标签小部件
- Stacked Widget:堆叠部件
- Frame:帧
Widget对应QWidget类的用法。Widget是使用Qt编写的图形用户界面(GUI)应用程序的基本生成块。每个GUI组件,如按钮、标签或文本编辑器,都是一个Widget,并可以放置在现有的用户界面中或作为单独的窗口显示。每种类型的组件都是由QWidget的特殊子类提供的,而QWidget自身又是QObject的子类。
QWidget是所有Qt GUI界面类的基类,它接收鼠标、键盘及其他窗口事件,并在显示器上绘制自己。
创建窗口
如果Widget未使用父级进行创建,则在显示时视为窗口或者顶层Widget。由于顶层Widget没有父级对象类来确保在其不再使用时就删除,所以需要开发人员在应用程序中对其进行跟踪。
1
2
3
4
5
6
7
8
9QWidget *widget = new QWidget();
window()->resieze(320,240);
window->show();
QPushButton *button = new QPushButton(tr("Press me"),window);
button->move(100,100);
button->show();
//通过将window作为父级传递给其构造器来向窗口添加子Widget:button。在这种情况下,向窗口添加按钮并将其放置在特定位置
//该按钮现在为窗口的子项,并在删除窗口时同时删除。请注意,隐藏或者关闭窗口不会自动删除该按钮使用布局
通常,子Widget是通过使用布局对象在窗口中进行排列,而不是通过指定位置和大小进行排列的,在此,构造一个并排排列的标签和行编辑框Widget:
1
2
3
4
5
6
7QLabel *label = new QLabel(tr("Name:"));
QLineEdit *lineEdit = new QLineEdit();
QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(label);
layout->addWidget(lineEdit);
layout->setLayout(layout);
2.5.7 项目视图组(Item Views)
QTableWidget继承自QTableView。QSqlTableModel能够与QTableView绑定,但不能与QTableWidget绑定。
1 | QSqlTableModel *model = new QSqlTableModel; |
L2.1 字符串类QString
L1 隐式共享
1 | QString str1 = "data"; |
L2 内存分配策略
QString在一个连续的内存块中保存字符串数据。当字符串的长度不断增长时。QString需要重新分配内存空间,以便有足够的空间保存增加的字符串。
1 | QString test(){ |
L2.5 Qt5控件:概念解析
Qt5::WIndowFlags 枚举类型
- Qt::Widget:QWidget构造函数的默认值,如果新的窗口部件没有父窗口部件,则它是一个独立的窗口,否则就是一个子窗口部件。
- Qt::Window:无论是否有父窗口部件,新窗口部件都是一个窗口,通常有一个窗口边框和一个标题栏。
- Qt::Dialog:新窗口部件是一个对话框,它是QDialog的构造函数的默认值。
Qt 5 布局管理
3.1 分割窗口的QSplitter类
1 |
|
textLeft->setAlignment(Qt::AlignCenter):设定TextEdit中文字的对齐方式。
- Qt::AlignLeft:左对齐
- Qt::AlignRight:右对齐
- Qt::AlignCenter:文字居中(Qt::AlignHCenter为水平居中,Qt::AlignVCenter为垂直居中)。
- Qt::AlignUp:文字与底部对齐
- Qt::AlignBottom:文字与底部对齐
