第七章 Qt之对象模型与容器类
7.1 对象模型(object model)
图形界面编程的特点
- 运行的高效性
- 高度的灵活性
Qt在标准C++对象模型的基础上添加的特性,形成了Qt自己的对象模型
- 无缝对象通信机制——信号和槽(signals and slots)
- 可查询可设计的对象属性系统(object properties)
- 强大的事件和事件过滤器(event and event filters)
- 基于上下文的国际化字符串翻译机制(string translation for internationalization)
- 完善的定时器驱动,可以在一个事件驱动的GUI中处理多个任务
- 分层结构的、可查询的对象树(object trees),组织对象拥有权(object ownership)
- 守卫指针即QPointer,它在引用对象被销毁时自动将其设置为0
- 动态的对象转换机制(dynamic cast)
- 支持创建自定义类型(custom type)
7.1.1 信号和槽(signals & slots)
a)信号和槽
信号和槽用于实现对象间的通信
- 一个信号可以对应多个槽函数:槽函数的执行顺序与关联顺序有关
- 多个信号也可以对应一个槽函数
- 一个信号可以关联到另一个信号上
- 只有QObject类和其子类派生的类才能使用信号和槽机制
- 使用信号和槽需在类的最开始处添加Q_OBJECT宏
signal的特性
- 信号默认为public函数
- 信号只用声明,不需要也不能对其进行定义实现
- 信号函数没有返回值,为void类型
- 信号可以从任何地方发射,但建议在声明该信号的类及其子类中发射
- 发射信号使用emit关键字
slot的特性
- 槽就是普通的C++函数
- 声明槽要使用slots关键字,一个槽可以是public、private、protectd类型的
- 槽可以被声明为虚函数,和普通函数相同
- 最大特点就是可以与信号关联
- 槽函数中参数的类型要和信号参数的类型相对应,且不能比信号的参数多
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
MyDialog* dlg = new MyDialog(this);
connect(dlg, SIGNAL(dlgReturn(int)), this, SLOT(showvalue(int))); //Qt4形式
connect(dlg, %MyDialog::dlgReturn, this, &Widget::showvalue); //Qt5形式
dlg->show();
}
Widget::~Widget()
{
delete ui;
}
void Widget::showvalue(int value)
{
ui->label->setText(tr("获取的值是:%1").arg(value));
}
void MyDialog::on_pushButton_clicked()
{
int value = ui->spinBox->value(); //获取输入值
emit dlgReturn(value); //发射信号
close(); //关闭对话框
}
b)信号和槽的关联
/*信号和槽的关联使用的是connnect函数*/
[static] QMetaObject::Connection QObject::connect(const QObject* sender,
const char* signal,
const QObject* receiver,
const char* method,
Qt::ConnectionType type = Qt::AutoConnection)
/*
arg1:发射信号的对象
arg2:要发射的信号 SIGNAL(自定义信号)
arg3:接收信号的对象,当接收对象为this时,该参数可以省略
arg4:要执行的槽 SLOT(自定义的槽函数)
arg5:信号和槽的关联方式
*/
/*Qt5中重载的connect函数*/
[static] QMetaObject::Connection QObject::connect(const QObject* sender,
PointerToMemberFunction signal,
const QObject* receiver,
PointerToMemberFunciton method,
Qt::ConnectionType type = Qt::AutoConnection)
/*
与Qt4提供的connect函数相比,
该函数指定信号和槽两个参数不用再使用SIGNAL()和SLOT()宏进行操作
槽函数不必使用slots关键字声明,而是任何能和信号关联的成员函数
要使成员函数与信号关联,则该函数的参数数目不能超过信号的参数数目,
同时不要求槽函数拥有的参数类型与信号中对应的参数雷凌完成一致,只需要可以进行隐式转换即可
可以在程序编译时进行检查:信号和槽的拼写错误、槽函数参数数目多于信号的参数数目等
*/
/*Qt5中重载的connect支持C++11中的Lambda表达式,写法如下可以直接编写信号发射后要执行的代码,不用再定义槽函数*/
connect(dlg, &MyDialog::dlgReturn, [=](int value)){
ui->label->setText(tr("获取的值是:%1").arg(value));
}
| 信号和槽的关联方式Qt::ConnectionType | 描述 |
|---|---|
| Qt::AutoConnection | 自动关联。receiver存在发射信号的线程,使用DirectConnection,否则使用QueuedConnection;在信号被发射时决定使用哪种类型 |
| Qt::DirectConnection | 直接关联。发射完信号后立即调用槽,只有槽执行完成返回后,发射信号后面的代码才可以执行 |
| Qt::QueuedConnection | 队列关联。当控制返回receiver所在线程的事件循环后再执行槽,无论槽执行与否,发射信号后的代码会立即执行 |
| Qt::BlockingQueuedConnection | 阻塞队列关联。与上一条类似,不过信号线程会一直阻塞,直到槽返回,当receiver存在信号线程时不能使用该类型,不然程序会死锁 |
| Qt::UniqueConnection | 唯一关联。这是一个标志与其他关联类型结合使用,使用按位或操作,这是两个对象间相同的信号和槽只能有唯一的关联,使用该信号防止重复关联 |
c) on_objectName_signal()自动关联
/*
使用该种自动关联形式时注意:
要使用自动关联的部件的定义都要放在setupUi()函数调用之前,因为setupUi()函数中调用了connectSlotsByName()函数
使用自动关联的部件需使用setObjectName指定它们的objectName()
*/
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
QPushButton *button = new QPushButton(this);
button->setObjectName("myButton");
ui->setupUi(this); //须在定义部件以后调用该函数
}
d)断开关联
[static] bool QObject::disconnect(const QObject* sender,
const char* signal,
const QObject* receiver,
const char* method);
/*该函数的几种用法
:断开一个对象所有信号的所有关联;disconnect(myobject, 0, 0, 0);等价于 myobject->disconnect();
:断开一个指定信号的所有关联;disconnect(myobject,SIGNAL(musignal()), 0, 0);等价于myobject->disconnect(SIGNAL(mysignal()));
:断开一个指定receiver的所有关联;disconnect(myobject, 0, myreceiver, 0); 等价于myobject->disconnect(myreceiver);
:断开一个指定信号和槽的关联;disconnect(myobject,SIGNAL(mysignal()), myreceiver, SLOT(myslots())); 等价于 myobject->disconnect(SIGNAL(mysignal()), myreceiver, SLOT(myslots())); 也等价于disconnect(myconnection);其中myconnection是connect管来奶时的返回值
*/
/*Qt5中对disconnect函数的重载*/
[static] bool QObject::disconnect(const QObject* sender,
PointerToMemberFunction signal,
const QObject* receiver,
PointerToMemberFunction method);
/*该函数无法断开信号于一般函数或者lambda表达式之间的关联;需使用connect()返回值进行断开*/
/*用法于上述相同,只有对信号和槽调用的参数使用指针而不是宏*/
e)信号和槽的高级应用
- Qt提供QObject::sender()函数来返回发送该信号的对象的指针
- 信号映射器QSignalMapper类实现对多个相同部件的相同信号进行映射,为其添加字符串或者数值参数后再发射
7.1.2 属性系统(The Property System)
Qt提供强大的基于元对象系统的属性系统,可以在运行Qt的平台上支持标准C++编译器。
- 要在一个类中声明属性,该类必须继承自QObject类
- 在声明前使用Q_PROPERTY()宏定义各属性的函数
/*==========一个属性类似于一个数据成员,不过它添加了一些可以通过元对象系统访问的附加功能==============*/
Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
/*
type:表示属性的类型;可以是QVariant支持的类型或用户自定义的类型
name:属性的名称
READ getFunction:getFunction表示读取该属性的函数,这个函数是必须的。[]中的表示可选项
*/
- 一个读(READ)操作函数。
- 如果MEMBER变量没有指定,则该函数必须有,他用来读取属性的值。
- 该函数一般是const类型的,它的返回值类型必须是该属性的类型,或者是该属性类型的指针或者引用
- 该函数可以被继承,也可以时虚函数
- 一个可选的写(WRITE)操作函数。
- 它用来设置属性的值
- 这个函数必须只有一个参数,而且它的返回值必须为空void
- 该函数可以被继承,也可以时虚函数
- 如果没有指定READ操作函数,那么必须指定一个MEMBER变量关联,这样会使给定的成员变量变为可读/写的而不用创建READ和WRITE操作函数
- 一个可选的重置(RESET)函数。
- 用来将属性恢复到一个默认值
- 该函数不能有参数,而且返回值必须为空void
- 该函数可以被继承,也可以时虚函数
- 一个可选的通知(NOTIFY)信号。
- 如果使用该选项,那么需要指定类中一个已经存在的信号,每当该属性的值改变时都会发射该信号
- 如果使用MEMBER变量时指定NOTIFY信号,那么信号最多只能有一个参数,并且参数的类型必须于属性的类型相同
- 一个可选的版本(REVISION)号。
- 如果包含了该版本号,那么它会定义属性及其通知信号只用于特定版本的API(通常暴露给QML)
- 如果不包含,则默认为0
- 可选的DESIGNABLE
- 表明这个属性在GUI设计器的属性编辑器中是否可见,大多数为可见true。
- 可选的STORED
- 表明是否在对象的状态被存储时也必须存储这个属性的值,大部分为true。
- 可选的USER
- 表明这个属性是否被设计为该类的面向用户或者用户可编辑的属性
- 一般,每一个类中只有一个USER属性,它的默认值为false
- 可选的CONSTANT
- 表明这个属性的值是一个常量
- 对于给定的一个对象实例,每一次使用常量属性的READ方法都必须返回相同的值;但对于类的不同的实例,这个常量可以不同
- 一个常量属性不可以有WRITE方法和NOTIFY信号
- 可选的FINAL
- 表明这个属性不能被派生类重写
/*=================================MyClass.h====================================*/
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(QString userName
READ getUserName
WRITE setUserName
NOTIFY userNameChanged); //注册属性userName
public:
explicit MyClass(QObject *parent = nullptr);
QString getUserName() const //实现READ读操作
{
return m_userName;
}
void setUserName(QString userName) //实现写函数
{
m_userName = userName;
emit userNameChanged(userName); //当属性值改变时发射该信号
}
signals:
void userNameChanged(QString); //声明NOTIFY通知消息
private:
QString m_userName; //私有变量,存放userName属性的值
};
#endif // MYCLASS_H
/*==========================================Widget.cpp=========================================*/
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
MyClass *my = new MyClass(this);
connect(my, &MyClass::userNameChanged, this, &Widget::userChanged);
my->setUserName("yafei");
qDebug() << "userName1: "<<my->getUserName();
//使用QObject类的setPRoperty()函数设置属性的值
my->setProperty("userName", "linux"); //将my对象的userName属性值设置为linux
//输出属性的值,这里使用了QObject类的property()函数,它返回值类型为QVariant
qDebug()<<"userName2:"<<my->property("userName").toString();
my->setProperty("myValue", 10); //动态设置属性,该属性只对my对象有效,对MyClass无效
qDebug()<<"myValue: "<<my->property("myValue").toString();
}
Widget::~Widget()
{
delete ui;
}
void Widget::userChanged(QString)
{
qDebug()<<"user changed: NOthing";
}
7.1.3 对象树和拥有权
Qt中使用对象树(object tree)来组织和管理所有的QObject类及其子类的对象。
- 当创建一个QObject时,如果使用了其他的对象作为其父对象,那么这个QObject就会被添加到父对象的children()列表中
- 当父对象被销毁时,这个QObject也会被销毁
对象销毁的过程
- 当顶层窗口关闭时,应用程序要销毁该窗口部件(如果不是顶层窗口,窗口关闭时只是被隐藏,不会被销毁)
- 当窗口部件销毁时,会自动销毁其子部件(这就是Qt中只有new操作,没有delete操作)
Widget w; //栈上创建
QPushButton* button = new QPushButton(&w); //堆上创建
/*Qt程序的规范操作*/
//主窗口对象的创建需要在栈上创建
//其余对象可以在堆上创建,但必须指定主窗口为父部件;只有这样做销毁时不需要使用delete操作符销毁对应对象
7.1.4 元对象系统(The Meta-Object System)
Qt的元对象系统(Meta-Object System)提供了对象间通信的信号和槽机制,运行时类型信息和动态属性系统。元对象系统的三个条件:
- 该类是继承自QObject类
- 必须在类的声明区声明Q_OBJECT宏
- 元对象编译器Meta-Object Compiler(moc)为QObject的子类实现元对象特性提供的必要代码
元对象系统提供的特性
- 信号和槽
- QObject::MetaObject()函数可以返回一个类的元对象
- QMetaObject::className()可以在运行时以字符串形式返回类名,而不需要C++编译器原生的运行时类型信息(RTTI)的支持
- QObject::inherits()函数返回一个对象是否是QObject继承树上一个类的实例信息
- QObject::tr()和QObject::trUtf8()进行字符串翻译来实现国际化
- QObject::sertProperty()和QObject::property()通过名字来动态设置或者获取对象属性
- QMetaObject::newInstance()构造类的一个新实例
- qobject_cast()函数对QObject类进行动态类型转换;类似于标准c++中的dynamic_cast()函数
7.2 容器类(container classes)
Qt提供了一组通用的基于模板的容器类(container classes)用来存储指定类型的项目。
7.2.1 Qt的容器类介绍
- 顺序容器:数据一个接一个线性存储
- QList(当进行插入和删除操作时,原本定义的迭代器会失效)
- QLinkedList(当进行插入和删除操作时,迭代器依然有效)
- QVector
- QStack
- QQueue
- 关联容器:数据按照<键,值>成对进行存储
- QMap(依据键顺序存储键值对,不关系存储数据可以使用QHash)
- QMultiMap
- QHash
- QMultiHash
- QSet
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
/*=====================================QList========================================*/
QList<QString > list;
list<<"aa"<<"bb"<<"cc";
if(list[1] == "bb") list[1] = "ab" ;
list.replace(2, "bc");
qDebug() << "The list is:";
for(int i=0; i<list.size(); i++)
{
qDebug()<<list.at(i);
}
list.append("dd");
list.prepend("mm");
QString str = list.takeAt(2); //从list中后去索引为2的字符串并删除它
qDebug()<<"at(2) item is: "<<str;
qDebug()<<"the list is: ";
for(int i=0; i<list.size(); i++)
qDebug()<<list.at(i);
list.insert(2, "mm"); //在索引位置2处插入mm,原先的数据后移
list.swap(1,3); //交换索引为1和3的值
qDebug()<<"the list is: ";
for(int i=0; i<list.size(); i++)
qDebug()<<list.at(i);
qDebug()<<"contains 'mm'?"<<list.contains("mm"); //list中是否包含”mm“
qDebug()<<"the 'mm' count? "<<list.count("mm"); //list中包含mm的个数
qDebug()<<"the first mm index? "<<list.indexOf("mm"); //查找list中第一个mm的索引值,默认为从索引0处开始查找
qDebug()<<"the first mm index? "<<list.indexOf("mm",1); //从索引1处开始查找, 查找list中第一个mm的索引值
/*=====================================QMap========================================*/
QMap<QString, int> map;
map["one"] = 1;
map["three"] = 3;
map.insert("seven", 7);
int value1 = map["six"]; //如果”six“不存在,则会在map中自动插入
qDebug()<<"value1:"<<value1;
qDebug()<<"Contain 'six'? "<<map.contains("six");
int value2 = map.value("five"); //如果”five"不存在,value2 = 0,则不会自动插入
qDebug()<<"Value2: "<<value2;
qDebug()<<"contains 'five?"<<map.contains("five");
int value3 = map.value("nine", 9); //如果map中没有“nine”,则value3被设置为9
qDebug()<<"value3: "<<value3;
//map后续插入的已存在的键值会覆盖之前的
map.insert("ten", 10);
map.insert("ten", 100);
qDebug()<<"ten: "<<map.value("ten");
//通过insertMulti()函数实现一键多值,之后使用values()函数获取该值的列表
map.insertMulti("two",2);
map.insertMulti("two",4);
QList<int> values = map.values("two");
qDebug()<<"two: " << values;
/*=====================================QMultiMap========================================*/
//通过使用QMultiMap类实现一键多值
QMultiMap<QString, int> map1, map2, map3;
map1.insert("values",1);
map1.insert("values",2);
map2.insert("values",3);
//可以相加,map.values(values") = 2,1,3
map3 = map2+map1;
QList<int> myValues = map3.values("values");
qDebug()<<"the values of map3: "<<myValues;
return a.exec();
}
7.2.2 遍历容器
遍历一个容器可以使用迭代器(iterators),迭代器提供了一个统一的方法来访问容器中的项目。Qt提供了两种风格的迭代器。
-
Java风格迭代器(使用方便,性能不如STL风格)
-
每一个容器类都有两个Java风格迭代器数据类型:一个提供只读访问,一个提供读/写访问
-

-

-
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); //只读操作 QList<QString> list; list<<"A"<<"B"<<"C"<<"D"; QListIterator<QString> i(list); //创建列表的只读迭代器,以list为参数,迭代器指向list的第一个元素的前面 qDebug()<<"The forwardis: "; while(i.hasNext()) //正向遍历 qDebug()<<i.next(); //i.next()返回迭代器移动之前的元素 qDebug()<<"The Backwardis: "; while(i.hasPrevious()) qDebug()<<i.previous(); //读写操作 QMutableListIterator<QString> j(list); j.toBack(); while(j.hasPrevious()){ QString str = j.previous(); if(str == "B") j.remove(); //remove()删除上一次跳过的项目 } j.insert("Q"); //在迭代器指向的位置插入一个项目;插入后迭代器到插入的项目后一个位置 j.toBack(); if(j.hasPrevious()) j.previous() = "N"; //对previous赋值后,迭代器往前走一个 j.previous(); j.setValue("M"); //迭代器对跳过的项目进行值设定 j.toFront(); qDebug()<<"The forwardis: "; while(j.hasNext()) qDebug()<<j.next(); return a.exec(); }
-
-
STL风格迭代器
-
每一个容器类都有两个STL风格迭代器类型,一个只读访问,一个读写访问
-
STL迭代器于Java迭代器不同直接指向项目
-
当迭代器使用前缀如(++i,--i);先修改迭代器,然后返回迭代器的一个引用(如果在表达式中不会对返回值进行处理,使用前缀速度更快)
-
当迭代器使用后缀如(i++,i--);对迭代器先进行复制,后修改迭代器,并返回复制的值
-

-
foreach关键字
-
foreach是Qt向C++语言中添加的一个用来进行容器顺序遍历的关键字,使用预处理进行实施
-
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QList<QString> list; list.insert(0,"A"); list.insert(1,"B"); list.insert(2, "C"); qDebug()<<"The forward is: "; foreach (QString str, list){ qDebug()<<str; } QMap<QString, int> map; map.insert("first", 1); map.insert("second",2); map.insert("third",3); qDebug()<<endl<<"The map is: "; foreach (QString str, map.keys()){ qDebug()<<str<<": "<<map.value(str); } QMultiMap<QString, int> map2; map2.insert("first", 1); map2.insert("first", 2); map2.insert("first", 3); map2.insert("second", 2); qDebug()<<endl<<"The map2 is: "; QList<QString> keys = map2.uniqueKeys(); foreach (QString str, keys){ foreach (int i, map2.values(str)) qDebug()<<str<<": "<<i; } return a.exec(); }
-
-
7.2.3 通用算法
在<QtAlgorithms>头文件中,Qt提供了一些全局的模板函数,这些函数是可以使用在容器上的十分常用的算法。Qt只是提供了STL中比较重要的一部分算法,如果在目标平台上可以使用STL,那么可以使用STL的算法来代替Qt的这些算法。
- <QtGlobal>头文件中提供了一些函数来实现一些经常使用的功能
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QStringList list;
list<<"one"<<"two"<<"three";
qDebug()<<QObject::tr("std::copy算法:");
QVector<QString> vect(3);
//将list中所有项目复制到vector中
std::copy(list.begin(),list.end(),vect.begin());
qDebug()<<vect;
qDebug()<<endl<<QObject::tr("std::equal算法");
bool ret1 = std::equal(list.begin(),list.end(),vect.begin());
qDebug()<<"equal: "<<ret1;
qDebug()<<endl<<QObject::tr("std::find算法");
QList<QString>::iterator i = std::find(list.begin(),list.end(), "two");
qDebug()<<*i;
qDebug()<<endl<<QObject::tr("std::fill算法");
std::fill(list.begin(),list.end(),"eleven");
qDebug()<<list;
QList<int> list1;
list1<<3<<3<<6<<6<<6<<8;
qDebug()<<endl<<QObject::tr("std::count算法");
int countOf6 = std::count(list1.begin(),list1.end(), 6);
qDebug()<<"countOf6: "<<countOf6;
qDebug()<<endl<<QObject::tr("std::lower_bound算法");
QList<int>::iterator j = std::lower_bound(list1.begin(),list1.end(),5);
list1.insert(j,5);
qDebug()<<list1;
QList<int> list2;
list2<<33<<22<<68<<6<<12;
qDebug()<<endl<<QObject::tr("std::sort算法");
std::sort(list2.begin(),list2.end()) ;
qDebug()<<list2;
qDebug()<<endl<<QObject::tr("std::stable_sort算法:");
std::stable_sort(list2.begin(),list2.end());
qDebug()<<list2;
qDebug()<<endl<<QObject::tr("std::greater算法");
qSort(list2.begin(), list2.end(), std::greater<int>());
qDebug()<<list2;
qDebug()<<endl<<QObject::tr("std::swap算法");
double pi = 3.14;
double e = 2.71;
std::swap(pi,e);
qDebug()<<"pi: "<<pi<<" e: "<<e;
return a.exec();
}
7.2.4 QString
QString类提供一个Unicode字符串(Unicode是一种支持大部分文字系统的国际字符编码标准)。QString存储一串QChar,QChar提供了一个16位的Unicode4.0字符。
a)隐式共享(implict sharing)
隐式共享又称写时复制(copy-on-write)。在后台Qt使用隐式共享来减少内存使用和避免不必要的数据复制。有助于减少存储16位字符的固有开销。
-
使用隐式共享类作为参数传递是即安全又有效的,因为只有一个指向该数据的指针被传递了,只有当函数向它写入时才会复制该数据。
-
深复制与浅复制;
-
深复制指复制一个对象,该过程比较消耗内存和CPU资源
-
浅复制指复制一个对象的引用(仅仅是一个指向共享数据块的指针),和增加元数据的引用计数值
-
QPixmap p1,p2; //创建对象默认引用计数为1 p1.load("image.bmp"); p2 = p1; //此处进行浅复制,p2在执行该语句时,原先的对象引用计数变为0,执行后p1的引用计数为2 Qpainter paint; paint.begin(&p2); //此处需要对p2进行修改,执行深复制,此处p1的引用计数为1,p2的引用计数也为1 paint.drawText(0.50, "hi"); paint.end();
-
b)编辑操作
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<QObject::tr("以下是编辑字符串操作")<<endl;
QString str = "hello";
qDebug()<<QObject::tr("字符串大小:")<<str.size();
str[0] = QChar('H');
qDebug()<<QObject::tr("第一个字符: ")<<str[0];
str.append("Qt");
qDebug()<<"append:"<<str;
str.replace(1,4,"i"); //将str中索引从1开始的4各字符用“i”代替
qDebug()<<"replace:"<<str;
str.insert(2,"my");
qDebug()<<QObject::tr("str为:")<<str;
str = str+"!!!";
qDebug()<<QObject::tr("str为:")<<str;
str = "hi\r\n Qt!\n";
qDebug()<<QObject::tr("str为:")<<str;
QString str1 = str.trimmed(); //将str两端的空白符去除,其中空白符包括 '\t', '\n', '\v', '\f', '\r', and ' '
qDebug()<<QObject::tr("str1:")<<str1;
QString str2 = str.simplified(); //将str中的空白符用单一的空格代替,如果有连续的空白符则只显示一个空格
qDebug()<<QObject::tr("str2:")<<str2;
str = "Hi, my Qt!";
QStringList list = str.split(",", QString::SkipEmptyParts); //以“,”为分隔符将str拆分
qDebug()<<QObject::tr("str拆分后为:")<<list;
str = list.join("");
qDebug()<<QObject::tr("list组合后为:")<<str;
qDebug()<<QString().isNull();
qDebug()<<QString().isEmpty();
qDebug()<<QString("").isNull();
qDebug()<<QString("").isEmpty();
return a.exec();
}
c)查询操作
QString str = "yafeilinux";
qDebug()<<str.right(5); //字符串最右端5个字符的子字符串
qDebug()<<str.left(5); //字符串最左端5个字符的子字符串
qDebug()<<str.mid(2,3); //从第二个字符开始的长度为3的子字符串(包含第二个字符)
qDebug()<<str.indexOf("fei"); //字符串中“fei”子字符串的起始索引值
qDebug()<<str.at(0); //字符串中索引值为0的字符 比用“[]”速度块不会引起深复制
qDebug()<<str.count('i'); //字符串中字符‘i’的个数(也可以查找子字符串的个数)
qDebug()<<str.startWith("ya"); //字符串是否以“ya”开始
qDebug()<<str.endWith("linux"); //字符串是否以“linux”结尾
qDebug()<<str.contains("lin"); //字符串中是否包含“lin”子字符串
d)转换操作
QString str = "100";
str.toInt();
int num = 45;
QString::number(num);
str = "FF";
bool ok;
str.toInt(&ok, 16);
QString::number(num, 16);
str = "123.456";
str.toFloat();
str = "abc";
str.toUpper();
str = "ABC";
str.toLower();
int age = 25;
QString name = "yafei";
str = QString("name is %1, age is %2").arg(name).arg(age);
str = "%1 %2";
qDebug()<<str.arg("%1f", "hello"); //输出为%1f hello;(此处%1f 替换%1 hello替换了%2的位置)
qDebug()<<str.arg("%1f").arg("hello"); //输出为hellof %2;(首先%1f替换了%1的位置, 之后hello替换%1的位置)
//arg(QString &a, int fieldWidth=0, fillchar=QLatin1char('')) const; //当a小于fileWidth时,用fillchar填充
str = QString("ni %1").arg("hi",5,'*'); //%1的位置由字段为5,‘*’替换空余位置,hi替换%1;结果为:ni ***hi
qreal value = 123.456;
//arg(double a, int fieldWidth; char format='g', int precision);以精度precision和宽度fieldWidth,及格式g表示a
qDebug()<<QString("number:%1").arg(value, 0, 'f', 2); //结果为123.45
str = "123.45";
qDebug()<<qPrintable(str); //输出时不显示字符串两端的引号
7.2.5 QByteArray和QVariant
-
QByteArray类提供一个字节数组,用来存储原始字节(包括'\0')和传统的以’\0‘结尾的8位字符串。
-
QVariant类像是常见的Qt数据类型的一个共用体(union),一个QVariant对象在一个时间只保存一个单一类型的单一的值。
-
使用toT()函数,其中T表示一种数据类型,将QVariant对象转换为T类型,并且获取它的值。(该函数采用复制转换不会改变源对象数据类型)
-
QVariant v1(15); qDebug()<<v1.toInt(); //将v1转换为整数,结果为15;复制转化,不影响v1的源数据类型 QVariant v2(12.6); qDebug()<<v2.toFloat(); //将v2转换为浮点数 QVariant v3("nihao"); qDebug()<<v3.toString(); //将v3转换为字符串 QColor color = QColor(Qt::red); QVariant v4 = color; qDebug()<<v4.type(); qDebug()<<v4.value<QColor>(); //QVariant不提供对Qt GUI模块定义的数据类型进行转换,可以使用value()函数获得该数据类型值 QString str = "hello"; QVariant v5 = str; qDebug()<<v5.canConvert(QVariant::Int); //一个类型是否能转换为另一个数据类型,可以用改函数测试 qDebug()<<v5.toString(); qDebug()<<v5.convert(QVariant::Int); //转换成功返回true,无法进行转换则返回false,同时QVariant对象被清空 qDebug()<<v5.toString(); -

-
7.3 正则表达式(regular expression)
正则表达式是一个文本中匹配子字符串的一种模式(pattern),可以简写为regexp。Qt中QRegExp类实现使用正则表达式进行模式匹配。QRegExp是以Perl的正则表达式语言为蓝本,支持Unicode。QRegExp中的语言规则可以使用setPatternSyntax()函数来更改。一个regexp主要应用在下述方面:
- 验证;测试一个字符串是否符合一些规范
- 搜素;子字符串的模式匹配
- 查找和替换;
- 字符串分割;
7.3.1 正则表达式介绍
Regexps由表达式(expressions)、量词(quantifiers)和断言(assertions)组成。
- 表达式;
- 最简单的表达式就是一个字符,如x或5。一组字符可以使用方括号括起来。如[ABC]表示匹配一个A或B或C,也可以表示为[A-C]。
- 量词;指定必须要匹配的表达式出现的次数,使用花括号括起来。{min, max};
- x{1, 1}表示必须匹配且只能匹配一个字符x,x{1,5}表示匹配一列字符x,其中至少包含1个字符x,最多包含5个字符x
- 断言;指定匹配的界限,使用’^‘和’$‘分别指定开始和结尾
- ^[0-9]{1,2}$,表示从开头开始匹配至结尾,0-99的整数
特殊符号可以用来表示一些常见的字符组和量词
//例如:^[0-9]{1,2}$,可以简化为^\d{1,2}$, 可以简化为^\d\d{0,1}$,可以简化为^\d\d?$;
//其中[0-9]可以使用\d符号表示,{1,2}可以用\d{0,1}表示,而{0,1}表示最多出现1次为可选项可以用?表示。
//例如:匹配单词mail和letter但不匹配包含上述单词的单词
//首先,匹配mail可以表示为:m{1,1}a{1,1}i{1,1}l{1,1},因为{1,1}可以省略,即表示为mail
//同理,匹配letter可以表示为letter
//因为需匹配两者中一个,则可以使用或符号表示,即mail|letter
//使用括号将表达式组合在一起,(mail|letter)
//因为只匹配mail或letter整个单词,因此需强制匹配的开始和结束在单词的边界上,表示为\b(mail|letter)\b
//"\b"断言在regexp中匹配一个位置,而不是一个字符,一个单词的边界是任何的非单词字符,如一个空格、新行、或者一个字符串的开始或结束
//使用一个单词替换一个字符,如用Mail替换字符M,但是当M后面为ail时不替换,则表示为M(?!ail)
//统计Eric和Eirik在字符串中出现的次数,表示为:\b(Eric|Eirik)\b或者\bEi?ri[ck]\b
//C++中'\'为转移字符,如果需使用’\b'则应表示为'\\b’,如果使用'\'本身则表示为'\\\\';
QRegExp rx("^\\d\\d?$");
qDebug()<<rx.indexIn("al"); //结果为-1,没有匹配项
qDebug()<<rx.indexIn("5"); //结果为0;匹配到,且返回匹配项的第一个索引值
qDebug()<<rx.indexIn("5b"); //结果为-1;因为第二个字符不是数字或空
qDebug()<<rx.indexIn("12"); //结果为0,匹配到
qDebug()<<rx.indexIn("123"); //结果-1,整数超过2位数
qDebug()<<"====================================================";
rx.setPattern("\\b(mail|letter)\\b");
qDebug()<<rx.indexIn("emailletter"); //结果为-1,因为mail和letter不是单个的单词
qDebug()<<rx.indexIn("my mail"); //结果为3,匹配到
qDebug()<<rx.indexIn("my email letter"); //结果为9,匹配到
qDebug()<<"====================================================";
rx.setPattern("M(?!ail)");
QString str1 = "this is M";
str1.replace(rx, "Mail"); //依据正则表达式规则,用Mail替换M
QString str2 = "my M, your Ms, his Mail";
str2.replace(rx,"Mail");
qDebug()<<"str2: "<<str2;
qDebug()<<"====================================================";
QString str3 = "one Eric another Eirik, and an Ericsson"
" How many Eiriks, Eric";
QRegExp rx2("\\bEi?ri[ck]\\b");
int pos = 0;
int count = 0;
while(pos >= 0)
{
pos = rx2.indexIn(str3, pos);
if(pos >= 0)
{
++pos;
++count;
}
}
qDebug()<<"Count: "<<count;
7.3.2 正则表达式组成元素




7.3.3 正则表达式的多重应用
- 通过使用setPatternSyntax()函数可以对正则表达式进行更多的语法匹配


QRegExp rx("*.txt"); //未使用通配符匹配
rx.setPatternSyntax(QRegExp::Wildcard); //设置使用通配符匹配
rx.indexIn("README.txt"); //结果为true
rx.indexIn("welcome.txt.bak"); //结果为false
7.3.4 文本捕获
int RegExp::indexIn(const QString &str, int offset=0, QRegExp::caretMode caretMode=CaretAtZero) const
/*
str: 表示对应待匹配的字符串
offset:表示str匹配的起始点索引,默认为0;如果=-1,则表示起始点为最后一个字符,如果为-2,则表示倒数第二个字符,etc
该函数返回-1则表示没有匹配项,否则返回的值为第一个匹配项的索引值
*/
QString QRegExp::cap(int nth = 0) const
/*
Return the text captured by the nth subexpression
The order of elements matched by cap() is as follows. The first element, cap(0), is the entire matching string. Each subsequent element corresponds to the next capturing open left parentheses. Thus cap(1) is the text of the first capturing parentheses, cap(2) is the text of the second, and so on.
*/
QRegExp rxlen("(\\d+)(?:\\s*)(cm|inch)");
int pos = rx.indexIn("Length: 189cm");
if(pos > -1)
{
QString value = rxlen.cap(1); //189
QString unit = rxlen.cap(2); //cm
}
7.3.5 新的QRegularExpression类
Qt5引入新的QRegularExpression类,实现了与Perl兼容的正则表达式,并在QRegExp基础上进行了改进,建议编写Qt5程序时使用QRegularExpression代替QRegExp。
QRegularExpression中,一个正则表达式由两部分构成:一个模式字符串和一组模式选项。其中模式选项用来更改模式字符串的含义。


#include <QRegularExpression>
#include <QDebug>
QRegularExpression re("^(\\d\\d)/(\\d\\d)/(\\d\\d\\d\\d)$"); //每一个小括号表示一个捕获组,通过captured()获取
QRegularExpressionMatch match = re.match("08/12/1985"); //match进行匹配
if(match.hasMatch()){ //hasMatch()判断匹配是否成功
QString matched = match.captured(0); //完全匹配项
QString day = match.captured(1); //第一个匹配项()
QString month = match.captured(2); //第二个匹配项()
QString year = match.captured(3); //第三个匹配项()
qDebug()<<"matched: "<<matched<<endl
<<"day: "<<day<<endl
<<"month: "<<month<<endl
<<"year: "<<year<<endl;
}
QString pattern("^(Jan|Feb|Mar|Apr|May)\\d\\d?, \\d\\d\\d\\d$");
QRegularExpression rel(pattern);
QString input("Jan 21,");
//语句中的零表示偏移量,指代第offset个匹配到的字符串;第三个参数表示匹配的类型
QRegularExpressionMatch match1 = rel.match(input,0, QRegularExpression::PartialPreferCompleteMatch);
bool hasMatch = match1.hasMatch(); //是否存在完全匹配
bool hasPartialMatch = match1.hasPartialMatch(); //是否存在部分匹配
qDebug()<<"hasMatch: "<<hasMatch<<" hasParialMatch: "<<hasPartialMatch;
浙公网安备 33010602011771号