相比于C++提供的两种字符串:C风格的char 字符串和字符串类string,qt提供的字符串类QString的功能更为强大。
QString类保存16位Unicode值,提供了丰富的操作、查询和转换等函数。该类还进行使用了隐式共享、高效的内存分配策略等多方面的优化。
QString str1 = "Welcome";
str1 = str1 + "to you !";
QString str2 = "hello, ";
str2 += "World! ";
QString str1 = "Welcome ";
QString str2 = "to ";
str1.append(str2); //str1 = "Welcome to"
str1.append("you! "); //str1 = "Welcome to you! "
QString str;
str.sprintf("%s", "Welcome "); // str = "Wlecome "
str.sprintf("%s", " to you! "); //str = " to you! "
str.sprintf("%s %s", "Welcome ", "to you! "); // str = "Welcome to you! "
QString str;
Str = QString("%1 was born in %2.").arg("John").arg(1998);//Str = "John was born in 1998"
其中,%1被替换成了“John”,%2被替换成了 “1998”。
QString str = " Welcome \t to \n you! ";
str = str.trimmed(); // str = "Welcome \t to \n you!"
str = str.simplified(); //str = "Welcome to you!"
QString str = "Welcome to you!";
str.startWith("Welcome", Qt::CaseSensitive); //返回true
str.startWith("to you", Qt::CaseSensitive); //返回false
QString类提供了丰富的转换函数,可以将字符串转换为数值类型或者其他的字符编码集。
QString str = "125";
bool ok;
int hex = str.toInt(&ok, 16);//ok = true, hex = 293
int dev = str.toInt(&ok, 10);//ok = true, dev = 125
QString str = "Welcome to you!";
QByteArray ba = str.toAscii();
qDebug() << ba;
ba.append("Hello, World!");
qDebug() << ba.data();
qt提供了一组统一的基于模版的容器类。对比C++的标准模版库中的容器类,Qt的这些容器更轻量、更安全并且更容易使用。此外,Qt的容器类在速度、内存消耗和内联(inline)代码等方面进行了优化。
存储在Qt容器中的数据必须是可赋值的数据类型,也就是说,这种数据类型必须提供一个默认的构造函数、一个复制构造函数和一个赋值操作运算符。
这样的数据类型包含了通常使用的大多数数据类型,包括基本数据类型(如int、double等)和Qt的一些数据类型(如QString、QData、QTime等)。不过,Qt的QObject及其子类是不能干存储在容器中的(可以存储类指针)。
QList<QToolBar> list; //error
QList<QToolBar*> list; //success
Qt的容器类是可以嵌套的。例如:
QHash<QString, QList<double> > hash;
Qt的容器类为遍历其中内容提供了以下两种方法:
经常使用的Qt容器类有QList、QLinkedList和QVector等。在开发一个较高性能需求的应用程序时,我们需要了解这些容器类的运行效率。下面列出了这几个类的时间复杂度比较:
容器类 | 查找 | 插入 | 头部添加 | 尾部添加 |
---|---|---|---|---|
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) |
QList是最常用到的容器类,它存储给定的数据类型T的一列数值。继承自QList类的子类有QItemSelection、QQueue、QSignalSpy及QStringList和QTestEventList。
QList不仅提供了可以在列表中进行追加的 QList::append() 和 QList::prepend() 函数,还提供了可以在列表中间完成插入操作的函数 QList::inseert() ,相对于任何其他的Qt容器类,为了可执行代码尽可能少,QList被高度优化。
QList维护了一个指针数组,该数组存储的指针指向QList 存储的列表项的内容。因此,QList 提供了基于下标的快速访问。
对于不同的数据类型,QList 采取不同的存储策略,存储策略有以下几种:
#include
int main()
{
//声明一个QList栈对象
QList<QString> list;
{
QString str("This is a test string");
list << str;//通过“<<”运算符将QString字符串存储到该列表中
}//使用花括号进行作用域表明,此时QList保存了对象的复制。
qDebug() << list[0] << "How are you! ";
return 0;
}
QLinkedList是一个链式列表,它以非连续的内存块存储数据。
QLinkedList 不能使用下标,只能使用迭代器访问它的数据。与QList相比,当对一个很大的列表项进行插入操作时,QLinkedList具有更高的效率。
QVector在相邻的内存中存储给定数据类型T的数值。在一个QVector的前部或者中间位置进行插入操作的速度是很慢的,这是因为这样的操作将导致内存中的大量数据被移动,这是有QVector的存储数据方式决定的。
QVector既可以使用下标访问数据,也可以使用迭代器访问数据。集成自QVector类的子类有QPolygon、QPolygonF和QStack。
Java风格的迭代器同STL风格的迭代器相比,使用起来更简单方便,不过这也是以轻微的性能损耗为代价的。对于每一个容器类,Qt都提供了两种类型的Java风格迭代器数据类型,即只读访问和读写访问,其分类如下:
容器类 | 只读迭代器类 | 读写迭代器类 |
---|---|---|
QList, QQueue | QListIterator | QMutableListIterator |
QLinkedList | QLinkedListIterator | QMutableLinkedListIterator |
QVector, QStack | QVectorIterator | QMutableVectorIterator |
Java风格迭代器的迭代点(Java-style iterators point)位于列表项的中间,而不是直接指向某个列表项。因此,它的迭代点或者在第一个列表项的前面,或者在两个列表项之间,或者在最后一个列表项之后。
下面以QList为例,介绍Java风格的两种迭代器的用法。QLinkedList和QVector具有和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();
}
上面使用的 i.hasNext()是对列表进行向后遍历的函数,而对列表进行向前遍历的函数有以下几种:
QListIterator<T>::toBack() :将迭代点移动到最后一个列表项的后面。
QListIterator<T>::hasPrevious() :检查当前迭代点之前是否具有列表项。
QListIterator<T>::previous() :返回前一个列表项的内容并将迭代点移动到前一个列表项之前。
此外:
toFront():移动迭代点到列表的前端(第一个列表项前面)
peekNext():返回下一个列表项,但不移动迭代点。
peekPrevious():返回前一个列表项,但不移动迭代点。
findNext():从当前迭代点开始向后查找指定的列表项,如果找到返回true,否则返回false。
findPrevious():与findNext()类似,不同的是它的方向是向前的,查找操作完成后的迭代点在匹配项的前面或整个列表的前端。
实现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.hasHext(); )
qDebug() << i.next();
for(i.toBack(); i.hasPrevious(); )
{
if(i.previous() % 2 == 0)
i.remove();
else
i.setValue(i.peekNext() * 10);
}
for(i.toPront(); i.hasNext(); )
qDebug() << i.next();
return a.exec();
}
对于每一个容器类,Qt都提供了两种类型的STL风格迭代器数据类型:
容器类 | 只读迭代器 | 读写迭代器 |
---|---|---|
QList,QQueue | QList::const_iterator | QList::iterator |
QLinkedList | QLinkedList::const_iterator | QLinkedList::iterator |
QVector, QStack | QVector::const_iterator | QVector::iterator |
STL风格迭代器的API是建立在指针操作的基础上的。例如“++”操作运算符移动迭代器到下一个选项(item),而“ * ”操作符返回迭代器指向的项。
示例:
#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();
}
QMap类和QHash类具有非常类似的功能,它们的差别仅在于:
容器类 | 键查找 | – | 插入 | – |
---|---|---|---|---|
平均值 | 最坏情况 | 平均情况 | 最坏情况 | |
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的顺序存储数据。为了能够支持一键多值的情况,QMap提供了QMap
QHash
QHash以任意的顺序组织它的数据。当存储数据的顺序无关紧要时,建议使用QHash作为存放数据的容器,QHash也可以存储一键多值形式的数据,它的子类QMultiHash
对于每一个容器类,Qt都提供了两种类型的Java风格迭代器数据类型:
容器类 | 只读迭代器类 | 读写迭代器类 |
---|---|---|
QMap |
QMapIterator |
QMutableMapIterator |
QHash |
QHashIterator |
QMutableHashIterator |
示例代码:
#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();
}
对于每一个容器类,Qt都提供了两种类型的STL风格迭代器数据类型:
容器类 | 只读迭代器类 | 读写迭代器类 |
---|---|---|
QMap |
QMap |
QMap |
QHash |
QHash |
QHash |
示例:
#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 != mi.end())
mi.value() = "010";
qDebug() << "修改后:";
QMap<QString,QString>::const_iterator modi;
for(modi = map.constBegin(); modi != map.constEnd(); ++modi)
qDebug() << " " <<modi.key() <<" " <<modi.value();
return a.exec();
}
QVariant类类似于C++的联合(union)数据类型,它不仅能够保存很多Qt类型的值,包括QColor、QBrush、QFont、QPen、QRect、QString和QSize等,也能够存放Qt的容器类型的值。Qt的很多功能都是建立在QVarient基础上的,比如Qt的对象属性及数据库功能等。
示例:
新建Qt Widget项目,取消选择“创建界面”复选框,建好项目后在widget.cpp文件中编写以下代码:
#include "widget.h"
#include
#include
#include
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QVariant v(709);
qDebug() <<v.toInt();
QVariant w("How are you! ");
qDebug() << w.toString();
QMap<QString, QVariant> map;
map["int"] = 709; //输入整形
map["double"] = 709.709; //输入浮点型
map["string"] = "How are you!"; //输入字符串
map["color"] = QColor(255, 0, 0); //输入QColor类型的值
//调用相应的转换函数输出
qDebug() << map["int"] <<map["int"].toInt();
qDebug() << map["double"] << map["double"].toDouble();
qDebug() << map["string"] << map["string"].toString();
qDebug() << map["color"] << map["color"].value<QColor>();
QStringList sl; //创建一个字符串列表
sl << "A" << "B" << "C" << "D";
QVariant slv(sl); //将该列表保存到一个QVariant变量中
if(slv.type() == QVariant::StringList)
{
QStringList list = slv.toStringList();
for(int i = 0; i < list.size(); ++i)
{
qDebug() << list.at(i); //输出列表内容
}
}
}
Widget::~Widget()
{
}
变量 | 对应的类型 | 变量 | 对应的类型 |
---|---|---|---|
QVariant::Invalid | 无效类型 | QVariant::Time | QTime |
QVariant::Region | QRegion | QVariant::Line | QLine |
QVariant::Bitmap | QBitmap | QVariant::Palette | QPalette |
QVariant::Bool | bool | QVariant::List | QList |
QVariant::QBrush | QBrush | QVariant::SizePolicy | QSizePolicy |
QVariant::Szie | QSize | QVariant::String | QString |
QVariant::Char | QChar | QVariant::Map | QMap |
QVariant::Color | QColor | QVariant::StringList | QStringList |
QVariant::Cursor | Qcursor | QVariant::Point | QPoint |
QVariant::Date | QDate | QVariant::Pen | QPen |
QVariant::DateTime | QDateTime | QVariant::Pixmap | QPixmap |
QVariant::Double | double | QVariant::Rect | QRect |
QVariant::Font | QFont | QVariant::Image | QImage |
QVariant::Icon | QIcon | QVariant::UserType | 用户自定义类型 |
首先介绍Qt的 和 模块中几种常用算法,然后再介绍基本的正则表达式。
#include "widget.h"
#include
#include
int main(int argc, char *argv[])
{
QApplication a0(argc, argv);
double a = -19.3, b = 9.7;
double c = qAbs(a);//返回a的绝对值
double max = qMax(b, c);//返回两个数值中最大值
int bn = qRound(b);//返回一个与浮点数最接近的整数值。
int cn = qRound(c);
qDebug() << "a = " << a;
qDebug() << "b = " << b;
qDebug() << "c = qAbs(a) = " << c;
qDebug() << "qMax(b,c)" << max;
qDebug() << "bn = qRound(b) = " << bn;
qDebug() << "cn = qRound(c) = " << cn;
qSwap(bn, cn);//交换两个数的值
qDebug() <<"qSwap(bn, cn) : " << "bn = " << bn << ", cn = " << cn;
return a0.exec();
}
使用正则表达式可以方便地完成处理字符串的一些操作,如验证、查找、替换和分割等。Qt的QRegExp类是正则表达式的表示类,它基于Perl的正则表达式语言,完全支持Unicode。
正则表达式由表达式(expressions)、量词(quantifiers)和断言(assertions)组成。
在计算机语言中,标识符通常要求以字母或下划线开头,后面可以是字母、数字和下划线。满足条件的标识符表示为:
" [A-Za-z_]+[A-Za-z_0-9]* "
其中,表达式中的“+”表示“[A-Za-z]”至少出现一次,可以出现多次;“ * ”表示“[A-Za-z_0-9]”可以出现0次或者多次。
量词 | 含义 | 量词 | 含义 |
---|---|---|---|
E? | 匹配0次或1次 | E[n,] | 至少匹配n次 |
E+ | 匹配1次或者多次 | E[,m] | 最多匹配m次 |
E* | 匹配0次或多次 | E[n,m] | 至少匹配n次,最多匹配m次 |
E[n] | 匹配n次 | – | – |
符号 | 含义 | 符号 | 含义 |
---|---|---|---|
^ | 表示在字符串开头进行匹配 | \B | 非单词边界 |
$ | 表示在字符串结尾进行匹配 | (?=E) | 表示表达式后缀紧随E才匹配 |
\b | 单词边界 | (?!E) | 表示表达式后不跟随E才匹配 |
本节简单介绍几个常用的控件,以便对Qt的控件有一个初步认识。
按钮组如图所示:
具体如下 :
新建项目“PushButtonTest”,基类选择QWideget选项,取消“创建界面”复选框。
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
};
#endif // WIDGET_H
#include "widget.h"
#include
#include
#include
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
setMinimumSize(200, 120);
setMaximumSize(200, 120);
QPushButton *quit = new QPushButton("Quit", this);
quit->setGeometry(62, 40, 75, 30);
quit->setFont(QFont("Times", 18, QFont::Bold));
connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));
}
Widget::~Widget()
{
}
#include "widget.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.setGeometry(100, 100, 200, 120);
w.show();
return a.exec();
}
Date/Time Edit对应于QDateTime类,在Qt5中可以使用它获得系统时间。通过QDateTime::currentDateTime()来获取本地系统的时间和日期信息。可以通过date()和time()来返回datetime中的日期和时间部分。典型代码如下:
QLabel *datalbel = new QLabel();
QDateTime *datatime = new QDateTime(QDateTime::currentDateTime());
datalbel->setText(datatime->date().toString());
datalbel->show();
定时器(QTimer)的使用非常简单,只需要以下几个步骤就可以完成定时器的应用。
QTimer *timer_clock = new QTimer(parent);
connect(timer_clock, SIGNAL(timeout()), this, SLOT(slottimedone());
即定时时间一刀就会发生timeout()信号,从而触发slottimedone()槽去完成某件事情。
time_clock->start(2000);
Graphics View 对应于QGraphicsView类,提供了Qt5的图像视图框架。
Text Browser 对应于QTextBrowser类,QTextBrowser类继承自QTextEdit,而且仅是只读的,对里面的内容并不能继续更改,但是相对于QTextEdit来讲,它还具有链接文本的作用。
modified : const bool //通过布尔值来说明内容是否被修改
openExternalLinks : bool
openLinks : bool
readOnly : const bool
searchPaths : QStringList
source : QUrl
undoRedoEnabled : const bool
通过以上属性设置,可以设定QTextBrowser是否允许外部链接,是否为只读属性,外部链接的路径及链接内容,是否可以进行撤销等操作。
QTextBrowser还提供了几种比较有用的槽函数。
virtual void backward();//上一页
virtual void forward();//下一页
virtual void home(); //主页
可以通过链接这几个槽函数来达到“翻页”的效果。
QQuickWidget是Qt5.3发布的一个组件,传统的QWidget程序可以用它来嵌入QML代码,为Qt开发者将桌面应用迁移到Qt Quick提供了方便,但目前QML中尚不能嵌入其他非QML窗口,因为QML的渲染机制和QWidget是不一样的。
Widget是使用Qt编写的图像用户界面(GUI)应用程序的基本生成块。每个GUI组件,如按钮、标签或文本编辑器,都是一个Widget,并可以放置在现有的用户界面中或作为单独的窗口显示。每种类型的组件都是由QWidget的特殊子类提供的,而QWidget自身又是QObject的子类。
QWidget是所以QtGUI界面类的基类,它接收鼠标、键盘及其他窗口事件,并在显示器上绘制自己。
通过传入QWidget构造函数的参数(或者调用QWidget::setWindowFlags() 和 QWidget::setParent() 函数)可以指定一个窗口部件的窗口标识(window flag)和父窗口部件。
窗口部件的窗口标识(window flag)定义了窗口部件的窗口类型和窗口提示(hint)。窗口类型指定了窗口部件的窗口系统属性(window-system properties),一个窗口部件只能有一个窗口类型,窗口提示定义了顶层窗口的外观,一个窗口可以由多个提示。
没有父窗口部件的Widget对象是一个窗口,窗口通常具有一个窗口边框(frame)和一个标题栏。而子窗口部件通常处在父窗口部件的内部,没有窗口边框和标题栏。
QWidget窗口部件的构造函数为:
QWidget(QWidget *parent = 0, Qt::WindowFlags f = 0);
/*
parent:指定窗口部件的父窗口部件。如果为0则新建窗口部件蒋帅一个窗口。否则新建窗口将会出现在父窗口部件的界面内部。
f:指定了新窗口的窗口标识。默认是0,及Qt::Widget.
*/
示例:
QWidget *window = new QWidget();
window->resize(320, 240);
window->show();
QPushButton *button = new QPushButton(QObject::tr("Press me"), window);
button->move(100, 100);
button->show();
QWidget *window = new QWidget();
//新建label进行布局
QLabel *label = new QLabel(QObject::tr("Name:"));
QLineEdit *lineEdit = new QLineEdit();
QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(label);
layout->addWidget(lineEdit);
window->setLayout(layout);
window->resize(320, 240);
window->show();
QPushButton *button = new QPushButton(QObject::tr("Press me"), window);
button->move(20, 20);
button->show();
QWidget *window = new QWidget();
QLabel *queryLabel = new QLabel();
QLineEdit *queryEdit = new QLineEdit();
QTableView *resultView = new QTableView();
QHBoxLayout *queryLayout = new QHBoxLayout();
queryLayout->addWidget(queryLabel);
queryLayout->addWidget(queryEdit);
QVBoxLayout *mainLayout = new QVBoxLayout();
mainLayout->addLayout(queryLayout);
mainLayout->addWidget(resultView);
window->setLayout(mainLayout);
window->show();
具体如下表:
区别点 | QTableView | QTableWidget |
---|---|---|
继承关系 | – | QTableWidget集成子QTableView |
使用数据模型setModel | 可以使用setModel设置数据模型 | setModel是私有函数,不能使用该函数设置数据模型 |
显示复选框setCheckState | 没有函数实现复选框 | QTableWidgetItem类中的setCheckState(Qt::Checked):可以设置复选框。 |
与QSqlTableModel 绑定 | QTableView能与QSqlTableModel绑定 | QTableWidget不能与QSqlTableModel绑定 |
Qt5中引入了模型/视图框架用于完成数据与表现的分离,这在Qt5中成为InterView框架,类似于常用的MVC设计模式。
MVC设计模式是起源于Smalltalk语言的一种与用户界面相关的设计模式。MVC包括三个元素:模型(model)表示数据;视图(view)表示用户界面;控制(controller)定义了用户在界面上的操作。同使用MVC模式,有效地分离了数据和用户界面,使得设计更为灵活,更能适应变化。
示例:
QSqlTableModel *model = new QSqlTableModel();
model->setTable("employee");
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
model->removeColumn(0);
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Name"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Salary"));
QTableView *view = new QTableView();
view->setModel(model);
view->show();
* 视图与模型绑定,模型必须使用new创建,否则视图不能随模型的改变而改变。
// QStandardItemModel model(4, 2); 错误,没有使用new创建。
QStandardItemModel *model = new QStandardItemModel(4, 2); //正确,应该使用new创建model。
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Label"));
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Quantity"));
ui->tableView->setModel(model);
for(int row = 0; row <4 ; ++row)
{
for(int column = 0; column < 2; ++column)
{
QModelIndex index = model->index(row, column, QModelIndex());
model->setData(index, QVariant((row + 1) *(column + 1)));
}
}
树形控件QTree Widget,其中控件的树节点成为QTreeWidgetItem,这种控件有时很有用处。例如,在飞信软件群发短信时选择联系人的界面中就用到这种有复选框的树形控件。
接下来实现一个简单的树形控件实例。
1、新建项目,保持“创建界面”复选框选择状态。
2、双击“widget.ui”文件,打开Qt的设计器,拖拽一个QTreeWidget控件到widget窗口中。
3、添加以下代码
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
init();
connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(treeItemChanged(QTreeWidgetItem *, int)));
}
Widget::~Widget()
{
delete ui;
}
void Widget::init()
{
ui->treeWidget->clear();
//第一个分组
QTreeWidgetItem *group1 = new QTreeWidgetItem(ui->treeWidget);
group1->setText(0, "group1");
group1->setFlags(Qt::ItemIsUserCheckable |Qt::ItemIsEnabled |Qt::ItemIsSelectable);
group1->setCheckState(0, Qt::Unchecked);
QTreeWidgetItem *subItem11 = new QTreeWidgetItem(group1);
subItem11->setFlags(Qt::ItemIsUserCheckable |Qt::ItemIsEnabled |Qt::ItemIsSelectable);
subItem11->setText(0, "subItem11");
subItem11->setCheckState(0, Qt::Unchecked);
QTreeWidgetItem *subItem12 = new QTreeWidgetItem(group1);
subItem12->setFlags(Qt::ItemIsUserCheckable |Qt::ItemIsEnabled |Qt::ItemIsSelectable);
subItem12->setText(0, "subItem12");
subItem12->setCheckState(0, Qt::Unchecked);
QTreeWidgetItem *subItem13 = new QTreeWidgetItem(group1);
subItem13->setFlags(Qt::ItemIsUserCheckable| Qt::ItemIsEnabled | Qt::ItemIsSelectable);
subItem13->setText(0, "subItem13");
subItem13->setCheckState(0, Qt::Unchecked);
QTreeWidgetItem *subItem14 = new QTreeWidgetItem(group1);
subItem14->setFlags(Qt::ItemIsUserCheckable| Qt::ItemIsEnabled | Qt::ItemIsSelectable);
subItem14->setText(0, "subItem14");
subItem14->setCheckState(0, Qt::Unchecked);
//第二个分组
QTreeWidgetItem *group2 = new QTreeWidgetItem(ui->treeWidget);
group2->setText(0, "group2");
group2->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
group2->setCheckState(0, Qt::Unchecked);
QTreeWidgetItem *subItem21 = new QTreeWidgetItem(group2);
subItem21->setFlags(Qt::ItemIsUserCheckable| Qt::ItemIsEnabled | Qt::ItemIsSelectable);
subItem21->setText(0, "subItem21");
subItem21->setCheckState(0, Qt::Unchecked);
QTreeWidgetItem *subItem22 = new QTreeWidgetItem(group2);
subItem22->setFlags(Qt::ItemIsUserCheckable| Qt::ItemIsEnabled | Qt::ItemIsSelectable);
subItem22->setText(0, "subItem22");
subItem22->setCheckState(0, Qt::Unchecked);
QTreeWidgetItem *subItem23 = new QTreeWidgetItem(group2);
subItem23->setFlags(Qt::ItemIsUserCheckable| Qt::ItemIsEnabled | Qt::ItemIsSelectable);
subItem23->setText(0, "subItem23");
subItem23->setCheckState(0, Qt::Unchecked);
QTreeWidgetItem *subItem24 = new QTreeWidgetItem(group2);
subItem24->setFlags(Qt::ItemIsUserCheckable| Qt::ItemIsEnabled | Qt::ItemIsSelectable);
subItem24->setText(0, "subItem24");
subItem24->setCheckState(0, Qt::Unchecked);
}
void Widget::treeItemChanged(QTreeWidgetItem *item, int column)
{
QString itemText = item->text(0);
//选中时
if(Qt::Checked == item->checkState(0))
{
QTreeWidgetItem *parent = item->parent();
int count = item->childCount();
if(count > 0)
{
for(int i = 0; i <count; i++)
{
//子字节也选中
item->child(i)->setCheckState(0, Qt::Checked);
}
}
else {
//是子节点
updateParentItem(item);
}
}
else if (Qt::Unchecked == item->checkState(0)) {
int count = item->childCount();
if(count > 0)
{
for(int i = 0; i <count; i++)
{
//子字节也选中
item->child(i)->setCheckState(0, Qt::Unchecked);
}
}
else {
//是子节点
updateParentItem(item);
}
}
}
void Widget::updateParentItem(QTreeWidgetItem *item)
{
QTreeWidgetItem *parent = item->parent();
if(parent == NULL)
{
return ;
}
//选中子节点个数
int selectedCount = 0;
int childCount = parent->childCount();
for(int i = 0; i <childCount; i++)
{
QTreeWidgetItem *childItem = parent->child(i);
if(childItem->checkState(0) == Qt::Checked)
{
selectedCount++;
}
}
if(selectedCount <= 0)
{
//未选中状态
parent->setCheckState(0, Qt::Unchecked);
}
else if (selectedCount > 0 &&selectedCount <childCount) {
//部分选中
parent->setCheckState(0, Qt::PartiallyChecked);
}
else if (selectedCount == childCount) {
//选中状态
parent->setCheckState(0, Qt::Checked);
}
}
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
void init();
void updateParentItem(QTreeWidgetItem *item);
public slots:
void treeItemChanged(QTreeWidgetItem *item, int column);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H