C++ QT开发 学习笔记(2)
Qt元对象系统(Meta-Object System)
元对象系统是一个基于标准**C++的扩展,为Qt提供了信号与槽机制、实时类型信息、动态属性系统。
元对象系统的三个基本条件:类必须继承自QObject、类声明Q_OBJECT宏(默认私有有)、元对象编译
器moc。
信号和槽机制是 QT 的核心机制,信号和槽是一种高级接口,应用于对象之间的通信,它是QT的核心特性,信号和槽是QT自行定义的一种通信机制,它独立于标准的 C/C++ 语言,要正确的处理信号和槽,必须借助一个称为 moc(Meta-Object Compiler),也就是“元对象编译器”。,它为高层次的事件处理自动生成所需要的必要代码。Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件。如果moc发现在一个类头文件中包含了宏 Q_OBJECT,则会生成以moc_className.cpp(自定义类名)的.cpp文件。这个源文件中包含了 Q_OBJECT 宏的实现代码。新的文件同样将进入编译系统,与原文件一起参与编译。构建生成的.o文件包含moc生成的cpp文件。
Q_OBJECT定义在qobjectdefs.h文件中:
分析Q_OBJECT后,发现都是在操作元对象,并没有所谓的信号和槽,属性等内容,很显然,QObject对象能够支持这些功能,必然是通过QMetaObject这个元对象来实现的。
QMetaObject定义:
所以,QObject之所以为元对象系统的基类,因其提供了元对象系统很多支持工作:
信号与槽、事件处理、属性设置、国际化支持(翻译)、对象树资源管理等等。
使用QObject作为基类而不使用Q_OBJECT宏和元对象代码是可以的,但是如果Q_OBJECT宏没有被使用,那么这个类声明的信号和槽,以及其他特征描述都不会被调用。
元对象系统除实现信号与槽外,还有很多其他特性,比如:
QObject::metaObject()
返回与该类绑定的meta-object对象。
QMetaObject::className()
可以在运行时以字符串的形式返回类的名字,不需要C++编译器原生的运行时类型信息(RTTI)的支持。
QObject::inherits()
函数返回继承信息:对象是否是QObject继承树上一个类的实例。
QObject::tr()和QObject::trUtf8()
提供国际化支持,将字符串翻译成指定的语言。
QObject::setProperty()和QObject::property()
通过名字动态设置和获取对象属性。
QMetaObject::newInstance()
构造该类的一个新实例。
qObject_cast()
动态类型转换。
一般建议在QObject的所有子类中使用Q_OBJECT宏,而不管它们是否使用了信号与槽。
信号与槽
GUI用户界面中,当用户操作一个窗口部件时,需要其他窗口部件响应,传统方式经常使用callback(回调机制)来实现。所谓回调即事先将函数指针作为参数传递另一个函数,然后在函数处理过程中适当地方调用回调函数回调机制有两个缺陷:类型不安全,不能保证调用函数过程中使用正确的参数;强耦合,处理函数必须知道调用哪个回调函数。Qt的信号与槽机制是类型安全的,松耦合,更灵活,更方便。
信号与槽(Signal & Slot)是 Qt 编程的基础,也是 Qt 的一大创新。因为有了信号与槽的编程机制,在 Qt 中处理界面各个组件的交互操作时变得更加直观和简单。
信号(Signal)就是在特定情况下被发射的事件,例如PushButton 最常见的信号就是鼠标单击时发射的 clicked() 信号。发射信号使用Qt的emit关键字。QT 的 signals 关键字指出进入了信号声明区,随后即可声明自己的信号。
定义信号,如:
signals:
void Comeon(QString& str); //Signal is no need to achieve
发射信号,如:
void MainWindow::on_CCSBtn_clicked()
{
QString str = "CCS Come on!";
emit Comeon(str);
}
槽(Slot)就是对信号响应的函数。槽就是一个函数,与一般的C++函数是一样的,可以声明在类的任何部分(public、private 或 protected),可以具有任何参数,也可以被直接调用。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。
声明三个槽方法,如:
private slots:
void on_CCSBtn_clicked();
void on_SQBtn_clicked();
void on_TGTBtn_clicked();
槽方法中处理需要处理的工作,如:
void MainWindow::ComeonGuys(QString &str)
{
qDebug()<<str;
}
注意:使用qDebug()输出信息时注意添加头文件:
include <QDebug>
GUI 程序设计的主要内容就是对界面上各组件的信号的响应,只需要知道什么情况下发射哪些信号,合理地去响应和处理这些信号就可以了。
信号与槽关联是用 QObject::connect() 函数实现:
QMetaObject::Connection QObject::connect(
const QObject *sender, //信号发送者
const char *signal, //发送的信号
const QObject *receiver, //信号接收者
const char *method, //表示与信号连接的方式的字符串,可以是槽或信号
Qt::ConnectionType type = Qt::AutoConnection //连接方式,默认自动连接
)
常用格式:connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
用来表示信号和槽的参数都是字符串,Qt提供了两个宏用于构造这样的字符串:对于信号使用SIGNAL,对于槽则使用SLOT,用它们将函数的原型包围起来即可。注意connect方法采用SIGNAL()及SLOT()时,这里的函数原型只能写出类型,不能有任何参数名,否则连接将会失败。
同时信号与槽连接方式:
1)Qt::AutoConnection
:(默认连接方式)自动方式,由系统自动选择连接方式。
2)Qt::DirectConnection
:直接方式,信号发射时,立即调用槽。
3)Qt::QueuedConnection
:队列方式,信号发射时产生一个事件进入队列,事件被处理时槽才能调用。
4)Qt::BlockQueuedConnection
:阻塞队列方式,信号发射时产生一个事件进入队列,然后当前线程进入阻塞状态,直到事件处理完毕,若接收方位于发送信号的线程中,则程序会死锁,故此连接方式仅用于多线程。
信号可以看做是特殊的函数,需要带括号,可带参数,信号无需实现也不能实现。槽函数需要带括号,有参数时还需要指明参数。当信号和槽函数带有参数时,在 connect()函数里,要写明参数的类型。信号的参数需与槽的参数列表一致,允许比槽参数多。如果不匹配或参数过少,会出现编译错误或运行错误。在使用信号与槽的类中,必须在类的定义中加入宏 Q_OBJECT。当一个信号被发射时,与其关联的槽函数通常被立即执行,就像正常调用一个函数一样。只有当信号关联的所有槽函数执行完毕后,才会执行发射信号处后面的代码。
信号与槽的关联方式有如下特点:
一个信号连接一个槽:
connect(sender, SIGNAL(single1()), receiver, SLOT(slotFun()));
一个信号连接一个信号:
connect(sender, SIGNAL(single1()), receiver, SIGNAL(single2()));
一个信号连接多个槽,关联信号的槽函数按照建立连接时的顺序依次执行:
connect(sender, SIGNAL(single1()), receiver1, SLOT(slotFun())); connect(sender, SIGNAL(single1()), receiver2, SLOT(slotFun())); connect(sender, SIGNAL(single1()), receiver3, SLOT(slotFun()));
多个信号连接一个槽:
connect(sender1, SIGNAL(single1()), receiver, SLOT(slotFun())); connect(sender2, SIGNAL(single2()), receiver, SLOT(slotFun())); connect(sender3, SIGNAL(single3()), receiver, SLOT(slotFun()));
信号与槽的自动关联
ui_xxxx.h文件中 connectSlotsByName()方法通过对象名支持信号与槽的自动关联。不采用connect()函数而是采用on_objectName_signal命名方式命名槽达到自动关联的效果。
如类头文件中声明槽方法:
private slots:
void on_sjBtn_clicked();
信号与槽的断开关联
QObject::disconnect(const QObject* sender,const char* signal,const QObject *receiver,const char* method);
断开与一个对象所有的信号的所有关联
disconnect(sender,0,0,0);
等价于:
sender->disconnect();
断开与一个指定信号的所有关联
disconnect(sender, SIGNAL(single1()), 0, 0);
等价于:
sender->disconnect(SIGNAL(single1()));
断开与一个指定接受者receiver的所有关联
disconnect(sender, 0, receiver, 0);
等价于:
sender->disconnect(SIGNAL(single1()));
断开指定信号与槽的关联:
disconnect(sender, SIGNAL(single1()), receiver, SLOT(slotFun()));
等价于:
disconnect(myConnection); //myConnection为connect()的返回值
如:
xxx.h文件中添加m_res变量用于保存connect()返回值。
private:
QMetaObject::Connection m_res
信号与槽连接
m_res = connect(this,SIGNAL(Comeon(QString&)),this,SLOT(ComeonGuys(QString&)));
断开该链接
disconnect(m_res)
信号与槽机制的优越性:
- 信号与槽机制是类型安全的,相关联的信号与槽参数必须匹配
- 信号与槽是松耦合的,信号发送者不知道也不需知道接受者的信息。
- 信号与槽可以使用任意类型的任意数量的参数。
动态属性系统
在标准C++中,为了保证封装性,我们经常声明一个私有变量,然后声明两个公有函数,即set函数和get函数。在Qt中我们可以使用宏Q_PROPERTY()宏来实现这些。一个属性可以使用常规函数QObject::property()和QObject::setProperty()进行读写,不用知道属性所在类的任何细节,除了属性的名字
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])
示例:
- 新建桌面应用程序TestProperty,父类QWidget,其他采用默认。
- 右键单击项目添加自定义类MyPropertyClass,父类QObject.
- mypropertyclass.h文件中Q_OBJECT下方声明属性宏:
class MyPropertyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(QString mask READ mask WRITE setMask NOTIFY maskChanged)
声明属性读取、设置函数,定义属性变更时发送的信号,定义成员变量m_mask保存属性值。
public: explicit MyPropertyClass(QObject *parent = nullptr); QString mask() const; QString setMask(QString strMaskNum); signals: void maskChanged(QString str); public slots: private: QString m_mask; };
widget.h添加槽函数声明
public slots: void maskChangedslot(QString str);
widget.cpp添加头文件
#include<QDebug()>
添加槽方法定义:void Widget::maskChangedslot(QString str) { qDebug()<<"New mask stock: "<<str; }
Widget类构造函数中添加如下代码
#include "widget.h" #include "ui_widget.h" #include "mypropertyclass.h" #include <QDebug> Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); MyPropertyClass* mypC = new MyPropertyClass; MyPropertyClass* mypC2 = new MyPropertyClass; connect(mypC,SIGNAL(maskChanged(QString)),this,SLOT(maskChangedslot(QString))); mypC->setMask("100,000 Masks"); qDebug()<<"Curernt mask have:"<<mypC->mask(); QObject* obj = mypC; qDebug()<<"obj executing property reading:"<<obj->property("mask").toString(); obj->setProperty("mask","200,000 Masks"); qDebug()<<"obj executing second property reading:"<<obj->property("mask").toString(); qDebug()<<"mypC2 reading property:"<<mypC2->mask(); } Widget::~Widget() { delete ui; } void Widget::maskChangedslot(QString str) { qDebug()<<"New mask stock: "<<str; }
QT 对象树
Qt提供了对象树机制,能够自动、有效的组织和管理继承自QObject的Qt对象。
每个继承自QObject类的对象通过它的对象链表(QObjectList)来管理子类对象,当用户创建一个子对象时,其对象链表相应更新子类对象信息,对象链表可通过children()获取。
当父对象析构的时候,其对象链表中的所有(子)对象也会被析构,父对象会自动将其从父对象列表中删除。Qt 保证没有对象会被 delete 两次。开发中手动回收资源时建议使用deleteLater代替delete,因deleteLater多次是安全的,而delete多次是不安全的。
#include "widget.h"
#include <QApplication>
#include "mylabel.h"
#include "mylayout.h"
#include "mypushbutton.h"
#include "myradio.h"
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
MyLabel* mylabel = new MyLabel(&w);
MyPushButton* myBtn = new MyPushButton(&w);
MyRadio* myRBtn = new MyRadio(&w);
MyLayout* mylayout = new MyLayout(&w);
MyLayout* sonlayout = new MyLayout(mylabel);
MyPushButton* sonBtn1 = new MyPushButton;
MyPushButton* sonBtn2 = new MyPushButton;
MyPushButton* sonBtn3 = new MyPushButton;
sonBtn1->setText("son Btn 1");
sonBtn1->setText("son Btn 2");
sonBtn1->setText("son Btn 3");
sonlayout->addWidget(sonBtn1);
sonlayout->addWidget(sonBtn2);
sonlayout->addWidget(sonBtn3);
mylabel->setLayout(sonlayout);
mylabel->setText("Son Label");
myBtn->setText("Son Button");
myRBtn->setText("Son single button");
mylayout->addWidget(mylabel,0,0);
mylayout->addWidget(myBtn,1,0);
mylayout->addWidget(myRBtn,2,0);
w.setLayout(mylayout);
w.show();
const QObjectList list = w.children();
qDebug()<<"w children are: ";
foreach(QObject* obj, list)
qDebug()<<obj;
const QObjectList myLabel = mylabel->children();
qDebug()<<"mylabel children are: ";
foreach(QObject* obj1, myLabel)
qDebug()<<obj1;
mylabel->deleteLater();
qDebug()<<"mylabel children are: ";
foreach(QObject* obj1, myLabel)
qDebug()<<obj1;
return a.exec();
}
上述代码实为了确定其实一旦父对象销毁时,子对象/孙子对象也会被销毁.
所以其实QT会自动的帮我们管理对象,并不会发生内存泄漏问题.
窗口部件
默认部件基类
QT提供的默认部件基类包括QMainWindow、QWidget、和QDialog这三种,这三个部件基类也是用的最多的。
QMainWindow
QMainWindow是带有菜单栏、工具栏、状态栏的主窗口类,它有自己单独的布局。布局有一个中心区域,通常是标准的Qt部件,也可以是定制部件,且必须有一个中心小部件。setCentralWidget()方法可设置中心部件。
QWidget
QWidget类是所有部件对象的基类,被称为基础窗口部件,继承关系详看QWidget类关系图。QWidget提供自我绘制和处理用户输入等基本功能,如接收鼠标、键盘和其它事件,并且在屏幕上绘制自己的表现。每一个窗口部件都是矩形,并且它们按Z轴顺序排列的。一个窗口部件可以被它的父窗口部件或者它前面的窗口部件盖住一部分。一个没有父窗口部件的窗口部件一直是顶级窗口部件。非顶级窗口部件是父窗口的子部件。QWidget构造函数有两个参数:QWidget *parent = 0,Qt::WindowFlags f = 0
。parent即父窗口,默认为0,即没有父窗口,是顶级窗口,如果指定parent值则当前窗体将会是一个子部件。Qt::WindowFlags是Qt::WindowType枚举值的组合,用来设置窗口的属性,f = 0表默认为Qt::Widget风格。setWindowState()可设置窗体的状态,参数由Qt::WindowStates枚举值指定,窗体状态如下表所示:
#include <QApplication>
#include <QWidget>
#include <QDebug>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QWidget widget;
widget.setWindowTitle("Widget windows");
//widget.setWindowState(Qt::WindowMaximized); //Qt::WindowState
//widget.resize(QSize(600,300));
widget.resize(500,500);
//widget.move(QPoint(0,0));
widget.move(50,90);
qDebug()<<"widget.x:" <<widget.x()<<"widget.y:"<<widget.y()<<"width:"<<widget.width()<<"height:"<<widget.height()<<"geometry:"<<widget.geometry();
widget.show();
QWidget widget1(0,Qt::FramelessWindowHint | Qt::Dialog);
widget1.setWindowTitle("No Frame Widget windows");
widget1.show();
return app.exec();
}
setWindowState()
可设置窗体的状态
窗体状态 | 作用 |
---|---|
Qt::WindowNoState | 窗体为正常状态 |
Qt::WindowMinimized | 窗体最小化 |
Qt::WindowMaximized | 窗体最大化 |
Qt::WindowFullScreen | 窗体全屏显示 |
Qt::WindowActive | 窗体为活动窗体 |
QWidget widget1(0,Qt::FramelessWindowHint | Qt::Dialog);
Qt:WindowFlags | 释义 |
---|---|
Qt::Widget | QWidget构造函数的默认值,如果新的窗口部件没有父窗口部件,则它是一个独立的窗口,否则就是一个子窗口部件。另请参见Qt :: Window和Qt :: SubWindow。 |
Qt::Window | 无论是否有父窗口部件,新窗口部件都是一个窗口,通常有一个窗口边框和一个标题栏 。请注意,如果部件没有父控件,则无法取消设置此标记(即如果存在父控件,则可以取消此标记,否则,必须存在此标记用以标识此控件是个独立窗口)。 |
Qt:: Dialog | 新窗口部件是一个对话框,它是QDialog构造函数的默认值。如果要将其用作模态对话框,则应从另一个窗口启动它,或者如果有父窗口,则与QWidget::windowModality属性一起使用。如果将其设置为模态,则对话框将阻止应用程序中的其他顶级窗口获取任何输入。我们将具有父级的顶级窗口称为辅助窗口。 |
Qt::Sheet | 新窗口部件是一个Macintosh表单(sheet) 。由于使用工作表意味着窗口模态,推荐的方法是使用QWidget :: setWindowModality()或QDialog :: open()。 |
Qt:: Drawer | 新窗口部件是一个Macintosh抽屉(drawer) |
Qt::Popup | 新窗口部件是一个弹出式顶层窗口 |
Qt::Tool | 新窗口部件是一个工具(tool)窗口,它通常是一个用于显示工具按钮的小窗口。如果一个工具窗口有父窗口部件,则它将显示在父窗口的部件上面,否则相当于使用了Qt::WindowStaysOnTopHint提示。如果窗口系统支持它,工具窗口可以用更轻的框架装饰。它也可以与Qt :: FramelessWindowHint结合使用。 |
Qt::ToolTip | 表示窗口小部件是工具提示。 这在内部用于实现工具提示,没有标题栏和窗口边框。 |
Qt:: Desktop | 新窗口部件是桌面,它是QDesktopWidget构造函数的默认值 |
Qt::SplashScreen | 新窗口部件是一个欢迎窗口,它是SplashScreen构造函数的默认值。 |
Qt::SubWindow | 新窗口部件是一个子窗口,而无论窗口部件是否有父窗口部件。此外,Qt还定义了一些控制窗口外观的窗口提示(这些窗口提示仅对顶层窗口有效) |
Qt::ForeignWindow | 指示此窗口对象是表示由另一个进程创建的本机平台窗口或手动使用本机代码的句柄。 |
Qt::CoverWindow | 表示该窗口代表一个封面窗口,例如,当应用程序在BlackBerry平台上最小化时显示。 |
Qt::MSWindowFiredSizeDialogHint | 为Windows系统上的窗口装饰一个窄的对话框边框,通常这个提示用于固定大小的对话框 |
Qt::MSWindowOwnDC | 为Windows系统上的窗口添加自身的显示上下文菜单 |
Qt::BypassWindowManagerHint | 此标志可用于向平台插件指示应禁用“所有”窗口管理器协议。 此标志的行为会有所不同,具体取决于运行应用程序的操作系统以及正在运行的窗口管理器。 该标志可用于获取没有配置集的本机窗口。 |
Qt::X11BypassWindowManagerHint | 完全忽视窗口管理器,它的作用是产生一个根本不被管理的无窗口边框的窗口(此时,用户无法使用键盘进行输入,除非手动调用QWidget::activateWindow()函数) |
Qt::FramelessWindowHint: | 产生一个无窗口边框的窗口,此时用户无法移动该窗口和改变它的大小 |
Qt::NoDropShadowWindowHint | 在支持平台上禁用窗口投影 |
Qt::CustomizeWindowHint | 关闭默认的窗口标题提示 |
Qt::WindowTitleHint | 为窗口装饰一个标题栏 |
Qt::WindowSystemMenuHint | 为窗口添加一个窗口系统系统菜单,并尽可能地添加一个关闭按钮 |
Qt::WindowMinimizeButtonHint | 为窗口添加一个“最小化”按钮 |
Qt::WindowMaximizeButtonHint | 为窗口添加一个“最大化”按钮 |
Qt::WindowCloseButtonHint | 为窗口添加一个“关闭”按钮 |
Qt::WindowMinMaxButtonHint | 为窗口添加一个“最小化”按钮 和一个“最大化”按钮 |
Qt::WindowContextHelpButtonHint | 为窗口添加一个“上下文帮助”按钮 |
Qt::WindowStaysOnTopHint | 告知窗口系统,该窗口应该停留在所有其他窗口的上面 |
Qt::WindowType_Mask | 一个用于提示窗口标识的窗口类型部分的掩码 |
QWidget设计模式属性设置:
enabled 启用或禁用widget,默认启用。
geometry widget的位置和尺寸
sizePolicy 设置widget在水平和垂直方向的伸缩策略以及伸缩因子(Stretch Factors),所谓伸缩策略实际就是widget对待部件大小提示的策略,需结合布局管理器一起使用。伸缩策略可通过调用setSizePolicy()方法设置,大小提示可通过sizeHint()函数返回值获取,也可重载sizeHint()方法进行重置。
拉伸因子描述了各个部件在进行拉伸时,部件间以指定的比例进行拉伸,如水平布局的三个按钮其拉伸因子分别设为1,2,3则表示该3个按钮将会以1:2:3的比例进行拉伸。
注意:1.当主窗口的大小不能按计算出来的比例容纳下所有子部件时,子部件不一定会按设计好的比例进行排列。
大小策略与拉伸因子之间的关系:
若部件的拉伸因子大于0,则按照拉伸因子的比例分配空间;若拉伸因子为0,则只有在其他部件不需要空间时才会获得空间;也就是说若一些部件拉伸因子大于0,而一些部件拉伸因子为0,则只有拉伸因子大于0的部件会被拉伸,而拉伸因子为0的部件不会被拉伸。若所有部件的拉伸因子都为0,则按照大小策略的规则对部件进行拉伸。
注意:2.若部件的大小策略为Fixed,则即使设置了拉伸因子,该部件也不会被拉伸。故拉伸因子会使大小策略不起作用或失效(除了Fixed策略外)
属性值 | 作用 |
---|---|
Fixed | 尺寸不能改变,尺寸为sizeHint大小 |
Minimum | 尺寸可以拉伸,尺寸可变范围:≥sizeHint |
Maximum | 尺寸可以缩小,尺寸可变范围:minimumSizeHint ~ sizeHint |
Preferred | 可以变大缩小,尺寸可变范围:≥minimumSizeHint |
Expanding | 可以变大缩小,尺寸可变范围:≥minimumSizeHint,且部件有优先扩展权 (注:优先扩展权表部件将尽可能多的占用空间,如Preferred与Expanding同时存在则优先分配空间给Expanding) |
MinimumExpanding | 尺寸可以拉伸,尺寸可变范围:≥minimumSizeHint,且部件有优先扩展权 |
Ignored | 任意变大缩小,尺寸可变范围:≥minimumSizeHint (若minimumSizeHint为0,则可缩小至0,此时部件不可见) |
minimumSize 设置部件的最小尺寸,伸缩时不可继续缩小。
maximumSize 设置部件的最大尺寸,伸缩时不可继续变大。
palette 画板,设置部件的基本样式。
mouseTracking 鼠标跟踪,鼠标离开部件范围后,部件仍可以接收到mousemove等事件。
tabletTracking 平板或手机的陀螺仪、加速度传感器跟踪
focusPolicy 焦点策略,按钮可以通过NoFocus使虚线消失,lineedit这类文本编辑框必须能获得焦点。
属性值 作用 NoFocus 无法通过点击和键盘获得焦点 TabFocus 鼠标无法获得焦点,Tab键获得焦点 ClickFocus 鼠标点击获得焦点,Tab键无法获得焦点 StrongFocus 鼠标和Tab键都可以获得焦点 WheelFocus 通过滚轮获得焦点 contextMenuPolicy 上下文菜单策略,指定菜单的显示方式。
属性值 | 作用 |
---|---|
NoContextMenu | 部件无菜单,菜单处理延迟到部件父亲 |
PreventContextMenu | 部件无菜单,菜单处理不延迟到父亲,而是传递到本身。 |
DefaultContextMenu | 调用部件的contextMenuEvent方法,默认处理方式为忽略上下文事件。 |
ActionsContextMenu | 部件菜单由 actions定义构成 |
CustomContextMenu | 部件菜单自定义,发送customContextMenuRequested信号 |
acceptDrops 设置部件是否接受拖拽事件,默认启用。
toolTip 设置部件的提示,鼠标悬浮时会显示。
toolTipDuration 设置widgettoolTip的显示持续时间,毫秒为单位,默认为-1,会一直显示。
statusTip 设置部件的状态提示,当窗口有statusBar时会显示在上面。
whatsThis 某些窗口会有whatsThis按钮,选中后点击其他按钮会显示帮助信息
accessibleName 辅助功能客户端应用程序所使用的控件名称。
accessibleDescription主要用来为视力较差或盲人用户,提供更大的上下文,也可以使用上下文搜索或其他应用程序。
layoutDirection widget布局的方向,LeftToRight,RightToLeft,LayoutDirectionAuto,字面意思
autoFillBackground 设置widget背景是否被画板颜色自动填充,默认不勾选
styleSheet 设置widget的qss样式表
locale 设置widget的区域和语言,在将时间等信息转成字符串后,年月日几个字会显示不同语言
inputMethodHint 设置widget输入时的屏幕键盘模式,有纯数字、纯字母等多种模式,用在手机端
QDialog
QDialog是各种对话框的基类,其继承自QWidget,对话框有两种表现形式:模态对话框、非模态对话框。模态对话框就是阻塞同一应用程序中其它可视窗口的输入的对话框。用户必须完成当前对话框中的交互操作并且关闭窗口后才能操作应用程序中的其它窗口。模式对话框有它们自己的本地事件循环。exec()方法可使窗口以模态方式运行。当用户关闭这个对话框,exec()将提供一个可用的返回值并且这时流程控制继续从调用exec()的地方进行。通常,我们连接默认按钮,例如“OK”到accept()槽并且把“Cancel”连接到reject()槽,来使对话框关闭并且返回适当的值。另外我们也可以连接done()槽,传递给它Accepted或Rejected。
模态窗口运行:
调用exec()方法,如
QDialog dlg; dlg.exec();
调用setModal()方法设置模态,如
QDialog dlg; dlg.setModal(true); dlg.show();
QWidget widget;
widget.setWindowModality(Qt::ApplicationModal); widget.show();
阻塞方式 阻塞效果 Qt::ApplicationModal 阻塞应用程序的所有窗口 Qt::WindowModal 阻塞阻塞父窗口、祖先窗口及它们的子窗口 Qt::NoModal 不阻塞,默认值
其它部件
QLabel
常用来显示文本、数字、图片、gif动图。
新建桌面应用程序,项目名testQLabel,基类QWidget,类名Widget,勾选创建界面文件。
类构造函数中添加如下代码(准备好一个图片及gif格式动图):
#include "widget.h"
#include "ui_widget.h"
#include <QMovie>
#include <QSize>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
ui->text_label->setText("label text show, text too much...");
ui->num_label->setNum(3.14);
QSize size = ui->pic_label->size();
QPixmap* pixmap = new QPixmap("cat.jpg");
QPixmap scaledPixmap = pixmap->scaled(size, Qt::KeepAspectRatioByExpanding);
ui->pic_label->setPixmap(scaledPixmap);
QMovie* movie = new QMovie("cat_move.gif");
ui->gif_label->setMovie(movie);
ui->gif_label->setScaledContents(true);
movie->start();
}
Widget::~Widget()
{
delete ui;
}
QSize size = ui->pic_label->size();
QPixmap* pixmap = new QPixmap("cat.jpg");
QPixmap scaledPixmap = pixmap->scaled(size, Qt::KeepAspectRatioByExpanding);
ui->pic_label->setPixmap(scaledPixmap);
上述代码是为了让图片能依据QLabel的大小显示图片是的图片不会被压缩.
QMovie* movie = new QMovie("cat_move.gif");
ui->gif_label->setMovie(movie);
ui->gif_label->setScaledContents(true);
movie->start();
上述代码是为了gif动图例子
可与其他部件设置伙伴关系,此时标签起到快捷键作用。如下图标签与按钮建立了伙伴关系,则按下快捷键Alt+P时,按钮便被按下了。
注意:快捷键按什么取决于(&_)什么字母.
例子:
PushButton shortcut(&P)
是 ALT+P快捷键
RadioButton(&a)
是ALT+A键
至于那个快捷键控制那个按钮 需要再界面里设置(如下图)
QLineEdit
接收用户输入。
显示模式
显示模式 | 效果 |
---|---|
QLineEdit::Normal | 正常显示 |
QLineEdit::NoEcho | 不显示 |
QLineEdit::Password | 密码显示 |
QLineEdit::PasswordEchoOnEdit | 编辑时候正常显示,其他密码显示 |
QLineEdit掩码字符
字符(必须输入) | 字符(可留空) | 含义 |
---|---|---|
A | a | 只能输入A-Z,a-z |
N | n | 只能输入A-Z,a-z,0~9 |
X | x | 可以输入任意字符 |
9 | 0 | 只能输入0-9 |
D | d | 只能输入1-9 |
# | 只能输入+,-,0-9 | |
H | h | 只能输入十六进制字符(0-9,a-f,A-F) |
B | b | 只能输入二进制字符(0,1) |
> | - | 后面的字母字符自动转换为大写 |
< | - | 后面的字母字符自动转换为小写 |
! | - | 停止字母字符的大小写转换 |
\ | - | 将该表中的特殊字符正常显示用作分隔符 |
;(符号) | - | ;用于后面输入的符号所有的空格都会转换成该符号 |
QPushButton 按钮类,常用显示文字、图标。
QRadioButton 单选按钮类,只能选一个,当将单选按钮加入一个分组框中时,加入的按钮便是一个分组,分组中只能选中一个,具有排他性。QCheckBox是多选按钮类,可多选。
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void showInput();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->lineEdit,&QLineEdit::returnPressed,this,&Widget::showInput);
}
Widget::~Widget()
{
delete ui;
}
void Widget::showInput()
{
qDebug()<<ui->lineEdit->text()<<ui->lineEdit->displayText();
}
布局管理器
利用布局管理器做出如下界面效果(标签与输入框设置伙伴关系):
新建桌面应用程序,项目名TestLayout,基类QWidget,类名Widget,不勾选创建界面。
修改main函数:
#include "widget.h"
#include <QApplication>
#include "QLabel"
#include "QLineEdit"
#include "QFormLayout"
#include "QRadioButton"
#include "QHBoxLayout"
#include "QPushButton"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
QLabel *namelabel = new QLabel("Name:(&N)");
QLabel *agelabel = new QLabel("Age:(&A)");
QLabel *emaillabel = new QLabel("Email:(&E)");
QLabel *addrlabel = new QLabel("Address:");
QLineEdit *nameline = new QLineEdit;
QLineEdit *ageline = new QLineEdit;
QLineEdit *emailline = new QLineEdit;
QLineEdit *addrline = new QLineEdit;
namelabel->setBuddy(nameline);
agelabel->setBuddy(ageline);
emaillabel->setBuddy(emailline);
addrlabel->setBuddy(addrline);
QFormLayout *headerLayout = new QFormLayout;
headerLayout->addRow(namelabel,nameline);
headerLayout->addRow(agelabel,ageline);
headerLayout->addRow(emaillabel,emailline);
headerLayout->addRow(addrlabel,addrline);
QLabel *sexlabel = new QLabel("Sex: ");
QRadioButton *maleRBtn = new QRadioButton("Male");
QRadioButton *femaleRBtn = new QRadioButton("Female");
QHBoxLayout *sexlayout = new QHBoxLayout;
sexlayout->addWidget(sexlabel);
sexlayout->addWidget(maleRBtn);
sexlayout->addWidget(femaleRBtn);
QSpacerItem *vSpacer = new QSpacerItem(30,30);
QSpacerItem *hSpacer = new QSpacerItem(150,20);
QPushButton *okBtn = new QPushButton("OK");
QVBoxLayout *mainlayout = new QVBoxLayout(&w);
mainlayout->addLayout(headerLayout);
mainlayout->addLayout(sexlayout);
mainlayout->addItem(vSpacer);
mainlayout->addWidget(okBtn);
mainlayout->setMargin(10);
mainlayout->addItem(hSpacer);
mainlayout->setSpacing(30);
w.setLayout(mainlayout);
w.show();
return a.exec();
}
- QFormLayout 的
addRow
函数通常第一个参数是标签控件而第二个是输入控件 - QHBoxLayout 通常是以
addWidget
函数来加入标签控件/输入控件- 但QHBoxLayout 也可以通过addItem, addLayout 来加入各种部件和布局
- QSpacerItem 是用来让布局更加灵活 而使用的,他可以让一些部件排序中有着一些可控的空间
- QSpacerItem 需要用 addItem来加入进布局里面
- 其实 如果代码中是写入
QVBoxLayout *mainlayout = new QVBoxLayout(&w);
而不是QVBoxLayout *mainlayout = new QVBoxLayout();
那么代码最后无需w.setLayout(mainlayout);
也能显示出界面出来. 原因是QVBoxLayout的 mainlayout 对象创建时就已经让w对象成为了父类. 所以w.show()的话也是会显示全部界面的.
容器
QString 类
隐式共享:隐式数据共享机制去最大化资源有效利用和最小化复制克隆操作。隐式共享类当作为函数参数传递的时候,不仅安全而且效率很高,因为传递的时候只是传递的数据的指针,数据本身只当自己被修改的时候才会去复制。简称写时复制。数据相同时,执行浅拷贝,仅复制指向数据块的指针,数据不同时则执行深拷贝。隐式共享技术可以大大降低对内存的开销及CPU资源的消耗,很大程度提高应用程序的运行效率。
QString类保存了16位的Unicode值,Unicode把所有语言都统一到一套编码里,Unicode提供UTF-8、UTF-16、UTF-32编码方式。UTF-8以字节为单位对Unicode进行编码,对不同范围的字符使用不同长度的编码。对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同。
QString s1 = s0;处设置断点后调试运行。
断点调试快捷键 | 功能 |
---|---|
F9 | 设置/取消断点 |
F5 | 开始调试/调到下一个断点 |
F10 | 单步 |
F11 | 单步进入 |
Shift + F11 | 单步跳出 |
Ctrl + F10 | 调到指定行 |
Shift + F5 | 结束断点调试 |
新建控制台应用程序,项目名TestQString,main函数中添加如下代码:
#include <QCoreApplication>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString s0 = "Hello"; //数据块"Hello"引用计数1
QString s1 = s0; //数据块"Hello"引用计数2
QString s2 = s0; //数据块"Hello"引用计数3
s2 = "Hi"; //数据块"Hi"引用计数1,//数据块"Hello"引用计数2
QString s3 = s0; //数据块"Hi"引用计数1
QString s4 = s2; //数据块"Hi"引用计数2
QString s5 = s2; //数据块"Hi"引用计数3
return a.exec();
}
构造s1
inline QString::QString(const QString &other) Q_DECL_NOTHROW : d(other.d)
{ Q_ASSERT(&other != this); d->ref.ref(); }
class RefCount
{
public:
inline bool ref() Q_DECL_NOTHROW {
int count = atomic.load();
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
if (count == 0) // !isSharable
return false;
#endif
if (count != -1) // !isStatic
atomic.ref();
return true;
}
static inline bool ref(std::atomic<T> &_q_value)
{
return ++_q_value != 0;
}
QString常用初始化方式
//const char* 初始化
QString str("Hello World");
qDebug()<<str;
//QChar数组初始化
QChar cHello[5] = {'H','E','L','L','O'};
QString strHello(cHello,5);
qDebug()<<strHello;
qDebug()<<sizeof(QChar)<<sizeof(char);
QString 转int, float, double
//QString 转int, float, double
QString strAge = "18";
QString strPI = "3.14159";
int nAge = strAge.toInt(); //第一参数为是否成功,第二参数为进位数
float fPI = strPI.toFloat();
double dPI = strPI.toDouble();
qDebug()<<nAge;
qDebug()<<fPI;
qDebug()<<dPI;
Number 转 QString
//number 转 QString
int year = 1950;
int year1 = 2024;
float height = 1.85f;
float width = 6.6;
QString strYear;
QString strHeight,strWidth,strYear1;
strYear = strYear.number(year); //number方法是将number 转成QString
strHeight = strHeight.number(height);
strWidth = strWidth.setNum(width); //setNum 方法也是将number 转成QString
strYear1 = strYear1.setNum(year1); //setNum 和number 方法都是一样的.
qDebug()<<strYear;
qDebug()<<strHeight;
qDebug()<<strWidth;
qDebug()<<strYear1;
setNum 和 number 区别在于 使用 setNum 时,适用于已经存在的 QString 对象需要更新其内容到某个数字的场景。
使用 number 时,适用于需要直接从数字生成新的 QString 对象的场景。
const char* 转 QString
//const char* 转 QString
const char* hi = "Hello World";
QString strHi(hi);
qDebug()<<strHi;
QString 转 QByteArray, const char*
QString strTom = "Tom";
QByteArray tomArray = strTom.toUtf8();
const char* cTom = tomArray.data(); //QStirng 转 const char* 必须先转成QByteArray 类型先.
qDebug()<<cTom;
QString转时间类型QDateTime
QString strTime = "1949-10-01 10:00:00";
QDateTime dtTime = QDateTime::fromString(strTime,"yyyy-MM-dd hh:mm:ss");
qDebug()<<dtTime;
QDateTime 转 QString
QDateTime dtCurrent = QDateTime::currentDateTime();
QString strCurrent = dtCurrent.toString("yyyy-MM-dd hh-mm-ss");
qDebug()<<strCurrent;
QString 类常用方法
QString 拼接 - append()
QString s = "My";
s = s + " World";
s += " is beautiful";
s.append(". I Love the world");
qDebug()<<s;
QString 格式化 - arg()
QString s0, s1;
s0.sprintf("%s%d%s","Before the year ", 4, " Million ago..");
s1.sprintf("%s%d%s%.1f%s","Before the year ", 4, " Million ago.. There is a ", 0.5, " People");
qDebug()<<s0<<endl<<s1;
//除了sprintf, QT还有更方便的方法可实现.就是使用 .arg方法
QString speople0, speople1;
speople0 = QString ("%1%2%3").arg("Before the year ").arg(4).arg(" Million Ago...");
speople1 = QString ("%1%2%3%4%5").arg("Before the year ").arg(4).arg(" Million Ago... There is a ").arg(0.5).arg(" People");
qDebug()<<speople0<<endl<<speople1;
QString at() 方法
//QString 的 at()方法
QString sAnimal = "crab";
QChar C = sAnimal.at(2); //返回 a 字母,QChar类型(2个字节).
qDebug() << C;
QString插入字符 - insert()
QString sMem = " Three";
QString sFish = "Fish Memory only in second";
sFish.insert(19,sMem.toUtf8()); //注意,第二个参数类型为QByteArray,所以需要转换
qDebug()<<sFish;
QString 在初始位置插入 - prepend()
qDebug()<<sFish.prepend("Sea Water ");
QString 替代字符 - replace()
sFish.replace(30,12,"Three Days"); //第一参数为位置, 第二参数为替代多少个字符,第三参数为替代成什么字符
qDebug()<<sFish;
QString 去除字符两端的空格 - trimmed()
sFish = " " + sFish + " ";
qDebug()<<sFish<<endl<<sFish.trimmed();
QString 去除两端以及空间多出的空格 - simplified()
sFish = " Fish had a bad memory ";
qDebug()<<sFish<<endl<<sFish.simplified();
QString 查询字符 - contains() , startswith(), endswith()
QString sTom = "Tom and cat are friends";
qDebug()<< sTom.startsWith("tom",Qt::CaseInsensitive);
qDebug()<<sTom.startsWith("Tom",Qt::CaseSensitive);
qDebug()<<sTom.endsWith("Friends",Qt::CaseInsensitive);
qDebug()<<sTom.endsWith("Friends",Qt::CaseSensitive);
qDebug()<<sTom.contains("And",Qt::CaseInsensitive);
qDebug()<<sTom.contains("And",Qt::CaseSensitive);
qDebug()<<QString::compare("Tom","tom",Qt::CaseInsensitive); //返回0表示一样
qDebug()<<QString::compare("Tom","tom",Qt::CaseSensitive); //返回-__等于不一样
QString 字符串分割 - split()
QString strTime = "1949/10/1";
QStringList timeList = strTime.split("/"); //分割后需要QStringList变量来接收多个分割后的 QString.
qDebug()<<"After spliting method: ";
foreach(QString ss,timeList)
qDebug()<<ss;
QString 截取字符串 - mid()
QString sFriends = "Tom and Cats are friends";
qDebug()<<sFriends.mid(13,12);
QString 字符串与空字符串
qDebug()<<QString().isNull(); //true
qDebug()<<QString().isEmpty(); //true
qDebug()<<QString("").isNull(); //false
qDebug()<<QString("").isEmpty(); //true
qDebug()<<QString(" ").isEmpty(); //false
const char* cc = '\0';
qDebug()<<QString(cc).isNull(); //true
qDebug()<<QString(cc).isEmpty(); //true
//总结: isNull返回true 仅可能是默认的QString(),或者 (const char*)0
QStringList 类
QStringList weekList;
weekList << "Monday"<<"Tuesday"<<"Wednesday";
weekList<<"Thursday";
weekList<<"Friday";
weekList<<"Saturday";
weekList.append("Sunday");
qDebug()<<weekList;
qDebug()<<weekList[0];
遍历链表 -for, foreach, STL风格迭代器, Java风格迭代器
//For遍历链表
for(int i=0; i<weekList.size(); i++)
{
qDebug()<<weekList.at(i);
}
//Foreach 遍历
qDebug()<<"foreach method: ";
foreach(QString s, weekList)
qDebug()<<s;
//使用STL风格迭代器 it方法遍历
qDebug()<<"Use iterator method";
QStringList::iterator it = weekList.begin();
for(;it!=weekList.end();it++)
{
qDebug()<<*it;
}
//使用Java风格迭代
qDebug()<<"Java method iterator";
QListIterator<QString> itr(weekList);
while(itr.hasNext())
{
qDebug()<<itr.next();
}
QStringList 查询 - contain()
//contains 包含
qDebug()<< "List whether have Holiday:"<<weekList.contains("Holiday");
qDebug()<< "List whether have sunday:"<<weekList.contains("Sunday");
QStringList 插入元素
weekList.insert(0,"Holiday");
qDebug()<<weekList;
QStringList 删除元素 - removeFirst(), removeSecond(), removeOne(), removeAt()
//insert 插入元素
weekList.insert(0,"Holiday");
qDebug()<<weekList;
//removeFirst() 删除头 removeLast()删除尾
weekList.append("Holiday");
qDebug()<<weekList;
weekList.removeFirst();
weekList.removeLast();
qDebug()<<weekList;
//removeOne() 删除某个元素
weekList.removeOne("Monday");
qDebug()<<weekList;
//删除指定位置元素 removeAt()
weekList.removeAt(2);
qDebug()<<weekList;
QStringList 删除所有元素 - clear()
weekList.clear();
qDebug()<<weekList;
QList类 & QLinkedList类
QList是最常用的容器类。QList以列表形态存储并管理数据,并能进行基于快速索引的访问,也可以进行快速的数据删除操作。继承自QList类的子类有QItemSelection、QQueue、QStringList、QTestEventList.
可用运算符<< 、append()、prepend()进行添加元素,insert()进行插入。QList维护了一个指针数组,数组元素指向每一个链表项,因此QList 提供了基于下标的快速访问。
Java风格迭代器迭代点位置:第一个列表项前、两个列表项中间、最后一个列表项之后。STL风格迭代器迭代点位置:直接指向列表项。
Java风格迭代器
容器类 | 只读迭代器 | 读写迭代器 |
---|---|---|
QList,QQueue | QListIterator | QMutableListIterator |
QLinkedList | QLinkedListIterator | QMutableLinkedListIterator |
QVector,QStack | QVectorIterator | QMutableVectorIterator |
STL风格迭代器
容器类 | 只读迭代器 | 读写迭代器 |
---|---|---|
QList,QQueue | QList::const_iterator | QList::iterator |
QLinkedList | QLinkedList::const_iterator | QLinkedList::iterator |
QVector,QStack | QVector::const_iterator | QVector::iterator |
QLinkedList是一个链式列表,不能使用下标访问,与QList相比,当对一个很大的列表进行插入操作时,QLinkedList更高效。
QList 添加元素 - <<, append(), prepend(),insert()
QList<int> list;
//添加元素
list <<1<<2<<3<<4<<5<<6<<7<<8;
list.append(9);
list<<10;
list.prepend(0);
list.insert(1,99);
qDebug()<<list;
QList查找元素- at(),contain()
qDebug()<<"List Third Element: "<<list.at(2);
qDebug()<<"List Third Element: "<<list[2];
qDebug()<<"List have element '8'?: "<<list.contains(8);
qDebug()<<"List size:"<<list.size();
QList 修改元素- replace(), 下标方式
list.replace(5,66);
list[0] = 100;
qDebug()<<"After modify the list: "<<list;
QList 删除元素 - remove(), removeAt(), removeFirst(),removeLast(),removeOne()
list.removeFirst();
qDebug()<<"After remove the first element:"<<list;
list.removeLast();
qDebug()<<"After remove the last element:"<<list;
list.removeAt(2);
qDebug()<<"After remove the third element:"<<list;
list.removeOne(99);
qDebug()<<"After remove the element '99': "<<list;
QList 清除链表 - clear()
list.clear();
qDebug()<<list;
QList使用Java风格迭代器
QList<int> monthList;
QMutableListIterator<int> it_mrw(monthList); //可读可写的迭代器.
qDebug()<<"Java Style Read Write Iterator:";
for(int i=0; i<12; i++)
{
it_mrw.insert(i+1);
}
qDebug()<<"After initiallized the element inside the list from back: ";
for(it_mrw.toBack();it_mrw.hasPrevious();)
qDebug()<<it_mrw.previous();
qDebug()<<"After initiallized the element inside the list from front: ";
for(it_mrw.toFront();it_mrw.hasNext();)
qDebug()<<it_mrw.next(); //next()函数是会让迭代器自动移到下一个位置的.所以没有it_mrw++的操作.
for(it_mrw.toFront();it_mrw.hasNext();)
{
int month = it_mrw.next();
if(month == 1)
it_mrw.remove(); //如果使用迭代器的remove(), 它是会移除迭代器最近通过next()访问的元素,所以这里是删除'1'
if(month == 11)
it_mrw.setValue(12);
}
qDebug()<<"After initiallized the element inside the list from front: ";
for(it_mrw.toFront();it_mrw.hasNext();)
qDebug()<<it_mrw.next();
//只读迭代器
QListIterator<int> it_mr(monthList);
//正遍历
qDebug()<<"Java Stylr only Read iterator element from front: ";
for(it_mr.toFront();it_mr.hasNext();)
{
qDebug()<<it_mr.next();
}
//反遍历
qDebug()<<"Only Read iterator element from back: ";
for(it_mr.toBack();it_mr.hasPrevious();)
{
qDebug()<<it_mr.previous();
}
注意:使用Java风格的迭代器必须时刻想起它的迭代器永远是指向第一个元素前/最后一个元素后的位置.
QStringList STL风格迭代器
QList<int> numList;
numList<<11<<22<<33<<44<<55;
//可读可写迭代器
QList<int>::iterator it_numRW;
it_numRW = numList.begin();
qDebug()<<"Read Write iterator with STL format";
for(;it_numRW!=numList.end(); it_numRW++)
{
*it_numRW = *it_numRW *10;
qDebug()<<*it_numRW;
}
//只读迭代器
QList<int>::const_iterator it_numR;
it_numR = numList.constBegin(); //注意!,必须是constBegin()!因为这个返回的才是只读迭代器的起始位置
qDebug()<<"Only Read iterator with STL format";
for(;it_numR!=numList.constEnd(); it_numR++) //注意!,必须是constEnd()!因为这个返回的才是只读迭代器的末端位置
{
qDebug()<<*it_numR;
}
注意:使用只读STL风格迭代器的话, 需要使用 constBegin()
方法和 constEnd()
方法
QLinkList类
QLinkedList<QString> weekList;
for(int i=1; i<8;i++)
{
weekList <<QString("%1%2").arg("Day").arg(i);
}
QLinkList Java风格 只读迭代器/读写迭代器
//Java风格读写迭代器
QMutableLinkedListIterator<QString> it_wrw(weekList);
for(it_wrw.toFront();it_wrw.hasNext();)
{
QString day = it_wrw.next();
if(day == "Day3")
it_wrw.setValue("Wednesday");
if(day == "Day5")
it_wrw.remove();
}
qDebug()<<"Weeklist after modify the value: ";
for(it_wrw.toFront();it_wrw.hasNext();)
{
qDebug()<< it_wrw.next();
}
for(it_wrw.toFront();it_wrw.hasNext();)
{
QString day = it_wrw.next();
if(day == "Day4")
it_wrw.insert("Friday");
}
qDebug()<<"After add \"Friday\":";
for(it_wrw.toFront();it_wrw.hasNext();)
{
qDebug()<< it_wrw.next();
}
QLinkList 也可使用STL方式迭代器, 与上面的 QList方法一样.
QLinkList
与Qlist
不一样, QLinkList 无法通过下标方式以及.at()
方法访问元素.
那为什么QLinkList还需要呢? 其实QLinkList跟贴近与底层C++的List, 所以其实它的效率更高.
所以,如果数据非常大的话,推荐使用QLinkList, 数据小的话推荐QLink因为用起来更方便.
Qvector 类
QVector在相邻的内存中存储给定数据类型T的一组数据。在QVector前部或中间位置插入操作速度都很慢,因会导致内存中大量的数据移动。访问数据可使用下标,也可使用迭代器。继承自QVector类的子类有QPolygon、QPolygonF、QStack。
Qvector 添加元素 - append(),prepend(),insert(), <<
vStars << " Lisa";
vStars << "Jenny";
vStars << "Jisoo";
vStars.append("Ana");
vStars.prepend("AngelaBaby");
vStars.insert(2,"Luhan");
qDebug()<<vStars;
Qvector 修改元素 - replace()
vStars.replace(4, "BlackPink");
qDebug()<<vStars;
Qvector 删除元素 - remove(), removeAll(), removeAt(), removeFirst()…
vStars.remove(0);
qDebug()<<vStars;
vStars.removeAll("Luhan");
qDebug()<<vStars;
Qvector 下标访问元素
qDebug()<<"The third star is:"<<vStars[2];
Qvector 查找元素 - contain()
qDebug()<<"The stars contain \"Jenny?\""<<vStars.contains("Jenny");
Qvector Java风格只读迭代器
QVectorIterator<QString> it_sr(vStars);
for(it_sr.toFront();it_sr.hasNext();)
{
qDebug()<<it_sr.next();
}
Qvector Java风格读写迭代器
QMutableVectorIterator<QString> it_srw(vStars);
for(it_srw.toFront();it_srw.hasNext();)
{
QString s = it_srw.next();
if(!QString::compare(s,"BlackPink")) // compare如果一样,返回0, 不一样返回1
it_srw.setValue("Blackpink");
}
qDebug()<<"After modify the element inside the vector:";
for(it_srw.toFront();it_srw.hasNext();)
{
qDebug()<<it_srw.next();
}
Qvector STL方式只读迭代器
QVector<QString>::ConstIterator it_ssr;
it_ssr = vStars.constBegin();
qDebug()<<"Use STL method iterator to get element:";
for(;it_ssr!=vStars.constEnd(); it_ssr++)
{
qDebug()<<*it_ssr;
}
Qvector STL 方式读写迭代器
QVector<QString>::iterator it_ssrw;
it_ssrw = vStars.begin();
for(;it_ssrw!=vStars.end(); it_ssrw++)
{
if(!QString::compare(*it_ssrw,"Blackpink"))
*it_ssrw = "BlackPink!";
}
qDebug()<<"STL style read write iterator:";
it_ssrw = vStars.begin();
for(;it_ssrw != vStars.end(); it_ssrw++)
{
qDebug()<<*it_ssrw;
}
QMap类 & QHash类
QMap与QHash差别:
QHash比QMap查找速度更快。
QHash以任意顺序存储,QMap以Key顺序存储数据。
QHash的Key必须提供operator==()及一个全局的qHash(Key)函数,而QMap的Key必须提供operator<()函数。
存储一键多值数据时可使用QMultiMap<Key,T>容器或QMultiHash<Key,T>容器。
Java风格迭代器
容器类 | 只读迭代器 | 读写迭代器 |
---|---|---|
QMap<Key,T>,QMultiMap<Key,T> | QMapIterator<Key,T> | QMutableMapIterator<Key,T> |
QHash<Key,T>,QMultiHash<Key,T> | QHashIterator<Key,T> | QMutableHashIterator<Key,T> |
STL风格迭代器
容器类 | 只读迭代器 | 读写迭代器 |
---|---|---|
QMap<Key,T>,QMultiMap<Key,T> | QMap<Key,T>::const_iterator | QMap<Key,T>::iterator |
QHash<Key,T>,QMultiHash<Key,T> | QHash<Key,T>::const_iterator | QHash<Key,T>::iterator |
QMap 插入元素 - insert(), 方括号
QMap<QString,QString> infoMap;
infoMap.insert("Bruce","163");
infoMap.insert("AngelaBaby","168");
infoMap["Antonio"] = "170"; //[]里面是键, 赋值是值
infoMap["YaoMing"] = "226";
QMap 通过Key寻找Value - 中括号方式/value()
qDebug()<<"YaoMing:"<<infoMap["YaoMing"];
qDebug()<<"Antonio:"<<infoMap.value("Antonio");
QMap 通过Value寻找Key - key()
qDebug()<<"168cm is who? :"<<infoMap.key("168");
QMap Java风格只读迭代器
QMapIterator<QString, QString> it_r(infoMap);
for(it_r.toFront();it_r.hasNext();)
{
it_r.next();
qDebug()<<it_r.key() << ":"<< it_r.value();
}
注意:Map是根据键排序的,所以第一个元素是英文字母靠前的键.
QMap Java风格读写迭代器
QMutableMapIterator<QString, QString> it_rw(infoMap);
if(it_rw.findNext("163")) //参数是值
it_rw.setValue("164"); //修改参数是值
qDebug()<<"After modify the height:";
for(it_rw.toFront();it_rw.hasNext();)
{
it_rw.next();
qDebug()<<it_rw.key()<<":"<<it_rw.value();
}
QMap STL 风格只读迭代器
QMap<QString,QString> wMap;
wMap.insert("Spring", "15");
wMap.insert("Summer", "35");
wMap.insert("Autumn", "23");
wMap.insert("Winter", "-5");
QMap<QString,QString>::const_iterator i_r;
i_r = wMap.constBegin();
qDebug()<<"STL style only read iterator:";
for(; i_r!=wMap.constEnd(); i_r++)
{
qDebug()<<i_r.key()<<" : "<<i_r.value();
}
QMap 风格读写迭代器
QMap<QString,QString>::iterator i_rw;
i_rw = wMap.find("Spring"); // find() 参数为键, 返回为改键的键值
if(i_rw != wMap.end()) //如果有找到该键
i_rw.value() = "17";
qDebug()<<endl<<"After modify the spring temperature";
i_rw = wMap.begin();
for(; i_rw!=wMap.end(); i_rw++)
{
qDebug()<<i_rw.key()<<":"<<i_rw.value();
}
QMultiMap 插入 -insert()
QMultiMap<QString,QString> wMap1; //QMultiMap 一个键对多个值
wMap1.insert("Spring", "15");
wMap1.insert("Summer", "35");
wMap1.insert("Summer", "37");
wMap1.insert("Summer", "39");
wMap1.insert("Autumn", "23");
wMap1.insert("Winter", "-5");
wMap1.insert("Winter", "-9");
wMap1.insert("Winter", "-11");
qDebug()<< endl <<wMap1;
QMultiMap删除元素 - delete()
wMap1.remove("Winter","-11");
qDebug()<<endl<<"After delete the winter of -9 Temperature: "<<wMap1;
QMultiMap 通过Key寻找values - Values()
qDebug()<<endl<<wMap1.values("Winter"); //打印所有Winter的温度(键值)
QMultiMap Java 风格只读迭代器
QMapIterator<QString,QString> i_r1(wMap1);
qDebug()<<endl<<"Initiallized season:";
for(i_r1.toFront();i_r1.hasNext();)
{
//i_r1.next();
qDebug()<<i_r1.key()<<":"<<i_r1.next().value();
//qDebug()<<i_r1.key()<<":"<<i_r1.value();
}
这里注意: 以下两种写法
for(i_r1.toFront();i_r1.hasNext();)
{
qDebug()<<i_r1.key()<<":"<<i_r1.next().value();
}
for(i_r1.toFront();i_r1.hasNext();)
{
i_r1.next();
qDebug()<<i_r1.key()<<":"<<i_r1.value();
}
都可以,都打印出一样的结果.
那为什么一个 .next()
是在 .key()
和.value()
前而另一个是在中间打印的结果也能是一样的呢?
原因是next()
方法都将返回下一个元素,并移动迭代器到该元素。这个返回值是当前元素(即调用 next()
之前的元素),并非之后的元素。这样的设计确保了在迭代中不会丢失对任何元素的访问。
具体来看,下面是对这两种调用方式的解释:
调用
key()
然后next().value()
:- 当你调用
key()
时,迭代器当前指向的元素的键被读取。 - 紧接着调用
next().value()
时,迭代器移动到下一个元素,并返回当前元素的值(此时的“当前”是指next()
调用之前的元素)。
- 当你调用
调用
next()
直接在循环中:在这种情况下,每次迭代开始时,
next()
被调用,迭代器移动到下一个元素,返回值是移动之前的元素。然后通过
key()
和value()
方法访问当前指向的元素的键和值。
QHash 类
QHash<int,QString> moneyHash;
moneyHash.insert(1, "One Dollar");
moneyHash.insert(10,"Ten Dollar");
moneyHash.insert(20,"Twenty Dollar");
moneyHash[50] = "Fifth Dollar";
moneyHash[100] = "Hundred Dollar";
//moneyHash[100] = "Max Dollar";
moneyHash.insertMulti(100,"Max Dollar");
一定要用 insertMulti 才能让一个键对多个值. 为什么Hash需要insertMulti呢?这样的话不就是 QMultiHash类了吗?
虽然 QHash 主要用于键值唯一的映射,insertMulti 方法提供了额外的灵活性,使得 QHash 在需要的时候也可以存储多个值。这主 要是为了提供一种与 QMultiHash 兼容的接口,使得在不需要严格的一对一键值映射时,QHash 也可以适用。
在实际应用中,如果你预期一个键可能会关联多个值,使用 QMultiHash 通常更直观,
* 因为它的默认行为就是支持这种用法,使得代码的意图更清晰易懂。而使用 QHash 的 insertMulti
* 可能会使代码读者误解其真正的意图,除非在上下文中已经明确说明。
所以,你在 QHash 中使用 insertMulti 是完全有效的,但这种用法更适合于你在维持 QHash 的同时,
偶尔需要存储多值的场景。在需要频繁存储多值的情况下,转向使用 QMultiHash 会是一个更自然和清晰的选择。
QHash Java风格只读迭代器
QHashIterator<int, QString> it_mr(moneyHash);
qDebug()<< endl << "Only Read Iterator: ";
for(it_mr.toFront();it_mr.hasNext();)
{
qDebug()<<it_mr.key()<<":"<<it_mr.next().value();
}
QHash Java 风格读写迭代器
QMutableHashIterator<int, QString> it_mrw(moneyHash);
//修改数据
if(it_mrw.findNext("Ten Dollar"))
it_mrw.setValue("Ten Dollars");
qDebug()<<endl<<"Update Ten Dollars";
for(it_mrw.toFront();it_mrw.hasNext();)
qDebug()<<it_mrw.key()<<":"<<it_mrw.next().value();
QVariant 类
QVariant类是Qt的共用体union数据类型,不仅能保存很多Qt类型的值,包括QColor,QBrush,QFont,QRect,QString及QSize等等,而且能存放Qt的容器类型值。
可采用toT()将QVariant对象转换称为T类型并获取其值。如toInt(),toFloat(),toString(),可用type方法获取数据类型,判断数据是否可。对于GUI模块中定义的数据类型没有相应的转换函数,即没有toColor(),没有toImage()这样的函数提供,可通过value()方法实现转换,判断两种类型是否可以转换时可通过canConvert()方法的返回值判断。
widget.cpp
#include "widget.h"
#include <QDebug>
#include <QTimer>
#include <QApplication>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QVariant vNum(100);
qDebug()<<vNum.toInt();
QVariant vPi(3.1415926f);
qDebug()<<vPi.toFloat();
QVariant vStr("Hello!");
qDebug()<<vStr.toString();
QVariant vB(true);
qDebug()<<vB.toBool();
QColor c = QColor(Qt::cyan);
QVariant vC = c;
qDebug()<<vC.type();
qDebug()<<vC.value<QColor>();
//返回数据类型转换的能力
QVariant vss = vStr;
qDebug()<<vss.canConvert(QVariant::Int);
qDebug()<<vss.toString();
//转换成功的条件:有转化能力,数据类型匹配
qDebug()<<vss.convert(QVariant::Int); //String转换成int 失败,所以返回false
qDebug()<<vss.toString(); //String 转int 变0, 0转回String 显示 "0".
QVariant vss1 = 1000;
qDebug()<<vss1.canConvert(QVariant::Int);
qDebug()<<vss1.toString();
qDebug()<<vss1.convert(QVariant::Int); //String转换成int成功,返回true;
qDebug()<<vss1.toString();
QTimer::singleShot(0,qApp,SLOT(quit()));
}
qDebug()<<vC.value<QColor>();
这一行代码需要 <QColor>
是因为从 需要从QVariant
中检索 QColor
类型的值。这里的 <QColor>
指示 QVariant
将其内部存储的值转换为 QColor
类型
QTimer::singleShot(0,qApp,SLOT(quit()));
singlerShot是 QTimer类的静态成员,qApp是当前应用程序的全局指针,SLOT是一个宏.用于将函数名转换为一个字符串,这个字符串在 Qt 的元对象系统中用于识别和调用相应的函数。当你写 SLOT(quit()),这实际上是传递了 “quit()” 这个字符串给函数。