Qt中文文档

本网站github链接

QtDocumentCN

gitee镜像链接 gitee-QtDocument

微信小程序

微信小程序

wechat

[TOC]

QAbstractAnimation 类

QAbstractAnimation 是所有的动画相关的基类。

QAbstractAnimation 定义了所有动画类相关的基础功能,通过继承该类,您可以实现动画的其它功能,或者添加自定义的特效。

属性方法
头文件#include<QAbstractAnimation>
qmakeQT+=core
自从Qt 4.6
继承QObject
派生QAnimationGroupQPauseAnimationQVariantAnimation

公共成员类型

类型方法
enumDeletionPolicy { KeepWhenStopped, DeleteWhenStopped }
enumDirection { Forward, Backward }
enumState { Stopped, Paused, Running }

属性

属性类型属性类型
currentLoopconst intdurationconst int
currentTimeintloopCountint
directionDirectionstateconst State

公共成员函数

返回类型函数名
QAbstractAnimation(QObject *parent = Q_NULLPTR)
virtual~QAbstractAnimation()
intcurrentLoop() const
intcurrentLoopTime() const
intcurrentTime() const
Directiondirection() const
virtual intduration() const = 0
QAnimationGroup *group() const
intloopCount() const
voidsetDirection(Direction direction)
voidsetLoopCount(int loopCount)
Statestate() const
inttotalDuration() const

公共槽

返回类型函数名
voidpause()
voidresume()
voidsetCurrentTime(int msecs)
voidsetPaused(bool paused)
voidstart(QAbstractAnimation::DeletionPolicy policy = KeepWhenStopped)
voidstop()

信号

返回类型函数名
voidcurrentLoopChanged(int currentLoop)
voiddirectionChanged(QAbstractAnimation::Direction newDirection)
voidfinished()
voidstateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)

保护成员函数

返回类型函数名
virtual voidupdateCurrentTime(int currentTime) = 0
virtual voidupdateDirection(QAbstractAnimation::Direction direction)
virtual voidupdateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)

重写保护成员函数

返回类型函数名
virtual boolevent(QEvent *event)

详细介绍

QAbstractAnimation 定义了所有动画共享的功能。通过继承这个类,您可以创建自定义的动画,并将其插入到动画框架中。

动画的进度的当前时间由当前时间(currentLoopTime())控制,数值从0毫秒开始到其最后的持续时间(duration())。该值在动画运行时会自动更新,也可以直接由 setCurrentTime() 来设置。

在任何时候,动画的状态都只有三种:RunningStoppedPaused——由枚举变量 State定义。可以通过函数 start()、stop() 、pause() 或 resume() 来改变当前动画的状态。动画开始时总会重置它的当前时间。若被暂停,则继续播放后会从相同的“当前时间”继续。若动画停止,则无法继续,但会保留“当前时间”(直到再次开始)。QAbstractAnimation 在状态改变时将会发出信号 stateChanged()。

通过设置 loopCount 属性,动画可以循环任意次数。当动画的时间到达 duration() 时,它将重置当前时间并继续运行。循环数为1(默认值)意味着动画只会播放一次。注意,若 duration 为-1,动画将无限播放,直到主动停止;当前时间会无限增长。当当前时间等于 duration() ,并且正在处理最后一次循环时,动画将进入 Stopped 状态,并发送 finished() 信号。

QAbstractAnimation 为子类提供了纯虚函数来跟踪动画的进度:duration() 和 updateCurrentTime()。duration() 函数用于提供动画的持续时间(如上文所述)。当动画时间变化时,动画框架会调用 updateCurrentTime()。通过重新实现该函数,您可以追踪动画进度。注意:此函数的调用间隔和调用次数均未定义,虽然通常每秒会更新60次。

通过重新实现 updateState(),您可以追踪动画状态的改变,这对于不受时间驱动的动画特别有用。

另请参阅:QVariantAnimationQPropertyAnimationQAnimationGroupThe Animation Framework

成员变量文档

enum QAbstractAnimation::DeletionPolicy

函数描述
QAbstractAnimation::KeepWhenStopped0动画停止时不会被删除
QAbstractAnimation::DeleteWhenStopped1动画停止时会被自动删除

enum QAbstractAnimation::Direction

该枚举描述了动画在 Running 状态时的运行的方向。

函数描述
QAbstractAnimation::Forward0“当前时间”随时间递增(即从0向终点/duration 移动)
QAbstractAnimation::Backward1”当前时间“随时间递减(即从终点/duration 向0移动)

enum QAbstractAnimation::State

该枚举描述了动画的状态。

函数描述
QAbstractAnimation::Stopped0动画未运行。这是 QAbstractAnimation 的初始状态,也是 [QAbstractAnimation] 结束后的状态。除非 setCurrentTime() 被调用,或者调用 start() 来启动动画,否则”当前时间“不会改变。
QAbstractAnimation::Paused1动画暂停(即使暂时挂起)。调用 resume() 将恢复动画。
QAbstractAnimation::Running2动画运行中。当控制权处于事件循环中时,QAbstractAnimation 将会有规律地更新”当前时间“,并在适合地时机调用 updateCurrentTime()。

属性文档

currentLoop : const int

此属性存储动画当前的循环数。

此属性描述了动画当前的循环数。默认的循环次数为1,所以当前的循环次数永远为0。若循环总数2的,当动画运行超过其 duration 时将重新开始,并将当前时间重置为0,当前循环数置为1,以此类推。

当当前循环变化时,QAbstractAnimation 会发出 currentLoopChanged() 信号。

存取函数

返回类型函数名
intcurrentLoop() const

通知信号

返回类型函数名
voidcurrentLoopChanged(int currentLoop)

currentTime : int

此属性存储动画当前的时间与进度。

此属性描述了动画当前的时间。您可以通过 setCurrentTime 函数来修改当前时间,也可以调用 start() 让动画运行,当前时间会随动画播放进度自动设置。

动画的当前时间从0开始,在 totalDuration() 结束。

存取函数

返回类型函数名
intcurrentTime() const
voidsetCurrentTime(int msecs)

另请参阅:loopCountcurrentLoopTime()。


direction : Direction

该属性存储动画在 Running 状态时的运行方向。

该方向表明了在 start() 被调用后,时间是从0向动画时长移动,还是从持续时间向0移动。

默认方向为 Forward

存取函数

返回类型函数名
Directiondirection() const
voidsetDirection(Direction direction)

通知信号

返回类型函数名
voiddirectionChanged(QAbstractAnimation::Direction newDirection)

duration : const int

该属性存储动画的持续时间。

如果 duration 为-1,则表示 duration 未定义,此时动画将会忽略 loopCount 属性。

存取函数

属性函数名
virtual intduration() const = 0

loopCount : int

该属性存储动画的循环次数。

此属性用整形描述了动画的循环次数。默认值为1,表示该动画只播放一次,然后停止。通过修改其值,动画会改变循环的次数。当值为0时,动画完全不会运行;当值为-1时,动画将会永远循环直到主动停止。如果动画未设置 duration 属性则无法循环,即只会播放一次。 存取函数

属性函数名
intloopCount() const
voidsetLoopCount(int loopCount)

state : const State

动画状态。 此属性描述了动画的当前状态。当动画状态改变时,QAbstractAnimation 会发射 stateChanged() 信号。

存取函数

属性函数名
Statestate() const

通知信号

属性函数名
voidstateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)

成员函数文档

QAbstractAnimation::QAbstractAnimation(QObject *parent = Q_NULLPTR)

构造 QAbstractAnimation 基类,并将 parent 参数传递给 QObject 的构造函数。

另请参阅:QVariantAnimationQAnimationGroup


[signal] void QAbstractAnimation::currentLoopChanged(int currentLoop)

每当当前循环发生变化时,QAbstractAnimation 会发射该信号。currentLoop 为当前循环。

注意: 属性 currentLoop 的通知信号。

另请参阅:currentLoop() 和 loopCount()。


[signal] void QAbstractAnimation::directionChanged(QAbstractAnimation::Direction newDirection)

每当方向改变时,QAbstractAnimation 会发射该信号。newDirection 为新方向。

注意: 属性 direction 的通知信号。

另请参阅:direction()。


[signal] void QAbstractAnimation::finished()

在动画停止并到达终点之后发送此信号。

该信号在 stateChanged() 之后发射。

另请参阅:stateChanged()。


[slot] void QAbstractAnimation::pause()

暂停动画。当动画暂停时,state() 返回 Paused。在调用 resume() 或 start() 前,currentTime() 的值将会保持不变。若想从当前时间继续,则调用 resume()函数。

另请参阅:start(),state() 和 resume()。


[slot] void QAbstractAnimation::resume()

暂停后恢复动画。当动画恢复时,会发射会发出 resumed() 信号和 stateChanged() 信号。当前时间不会变化。

另请参阅:startpause() 和 state()。


[slot] void QAbstractAnimation::setPaused(bool paused)

pausedtrue,则暂停动画。若 pausefalse,则恢复动画播放。

另请参阅:state(),pause() 和 resume()。


[slot] void QAbstractAnimation::start(QAbstractAnimation::DeletionPolicy policy = KeepWhenStopped)

开始动画。policy 参数表示动画在结束后是否会被删除。动画启动时,会发射 stateChanged() 信号,state() 会返回 Running。当控制权回到事件循环时,动画会自动播放,并随播放进度周期性调用 updateCurrentTime(),

若动画当前已被停止或者已经结束,调用 start() 将会充值动画并从头开始。当动画到达终点时,动画将会停止;或者当其循环次数大于1,则会从头开始。

若动画已经在运行中,此函数不会有任何操作。

另请参阅:stop() 和 state()。


[signal] void QAbstractAnimation::stateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)

每当动画的状态从 oldState 变为 newState 时,QAbstractAnimation 会发射此信号。此信号会在虚函数 updateState()被调用后发射。

注意: 属性 state 的通知信号。

另请参阅:updateState()。


[slot] void QAbstractAnimation::stop()

停止动画。当动画停止时,会发射 stateChanged() 信号,state() 返回 Stopped。当前时间不会变化。

若动画到达终点后自动结束(比如 currentLoopTime() == duration() 或者 currentLoop() > loopCount() - 1),则会发射 finished() 信号。

另请参阅:start() 和 state()。


[virtual] QAbstractAnimation::~QAbstractAnimation()

若在运行中则停止动画,然后销毁 QAbstractAnimation。若当前动画是 QAnimationGroup 的一部分,则会在销毁前被自动移除。


int QAbstractAnimation::currentLoopTime() const

返回当前循环中的当前时间。它的范围为0到 duration()。

另请参阅: duration() 和 currentTime()。


[pure virtual] int QAbstractAnimation::duration() const

此纯虚函数返回动画持续时间,并定义 QAbstractAnimation 应该多久更新一次当前时间。持续时间是本地的,并且不包括循环总数。

返回值-1表示动画没有定义持续时间,动画将永远运行下去,直到被主动停止。这对于非时间驱动的动画或者无法预测其持续时间的动画(例如游戏中事件驱动的音频回放)十分有用。

若该动画是并行的 QAbstractAnimation 是并行的,则持续时间是所有动画中最长的。若此动画是顺序的 QAnimationGroup,则持续时间是所有动画的总和。

注意:duration 属性的获取函数。

另请参阅:loopCount()。


[virtual protected] bool QAbstractAnimation::event(QEvent *event)

QObject::event(QEvent *e) 的重新实现。


QAnimationGroup *QAbstractAnimation::group() const

若此动画是 QAnimationGroup 的一部分,则会返回该动画组的指针,否则返回nullptr

另请参阅:QAnimationGroup::addAnimation()。


int QAbstractAnimation::totalDuration() const

返回动画的有效持续时间的综合,包含循环次数。 另请参阅:duration() 和 currentTime()。


[pure virtual protected] void QAbstractAnimation::updateCurrentTime(int currentTime)

此虚函数会当动画的 currentTime 每次发生变化时被调用。

另请参阅:updateState()。


[virtual protected] void QAbstractAnimation::updateDirection(QAbstractAnimation::Direction direction)

此虚函数会在 QAbstractAnimation 的播放方向改变时被调用。参数 direction 表示新的方向。

另请参阅:setDirection(),direction()。


[virtual protected] void QAbstractAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)

此虚函数会在动画的状态改变从 oldState 切换为 newState 时被调用。

另请参阅:start(),stop(),pause() 和 resume()。

[TOC]

QAbstractAudioDeviceInfo类


QAbstractAudioDeviceInfo是音频后端的基类

属性方法
Header:#include<QAbstractAudioDeviceInfo>
qmake:QT += multimedia
Inherits:QObject

简述

公共功能

类型方法
virtual QStringdeviceName() const = 0
virtual boolisFormatSupported(const QAudioFormat &format) const = 0
virtual QAudioFormatpreferredFormat() const = 0
virtual QListQAudioFormat::EndiansupportedByteOrders() = 0
virtual QListsupportedChannelCounts() = 0
virtual QStringListsupportedCodecs() = 0
virtual QListsupportedSampleRates() = 0
virtual QListsupportedSampleSizes() = 0
virtual QListQAudioFormat::SampleTypesupportedSampleTypes() = 0

详细说明


QAbstractAudioDeviceInfo是音频后端的基类。

该类实现了QAudioDeviceInfo的音频功能,即QAudioDeviceInfo类中会保留一个QAbstractAudioDeviceInfo,并对其进行调用。关于QAbstractAudioDeviceInfo的实现的其它功能,您可以参考QAudioDeviceInfo的类与函数文档


成员函数文档


QString QAbstractAudioDeviceInfo::deviceName() const [纯虚函数] 返回音频设备名称


bool QAbstractAudioDeviceInfo::isFormatSupported(const QAudioFormat &format) const [纯虚函数] 传入参数QAudioFormat(音频流)类,如果QAbstractAudioDeviceInfo支持的话,返回true(真是不好翻译)


QAudioFormat QAbstractAudioDeviceInfo::preferredFormat() const [纯虚函数] 返回QAbstractAudioDeviceInfo更加倾向于使用的音频流。


QListQAudioFormat::Endian QAbstractAudioDeviceInfo::supportedByteOrders() [纯虚函数] 返回当前支持可用的字节顺序(QAudioFormat :: Endian)列表


QList QAbstractAudioDeviceInfo::supportedChannelCounts() [纯虚函数] 返回当前可用的通道(应该是这样翻译)列表


QStringList QAbstractAudioDeviceInfo::supportedCodecs() [纯虚函数] 返回当前可用编解码器的列表


QList QAbstractAudioDeviceInfo::supportedSampleRates() [纯虚函数] 返回当前可用的采样率列表。(突然发现Google翻译真心吊啊)


QList QAbstractAudioDeviceInfo::supportedSampleSizes() [纯虚函数] 返回当前可用的样本大小列表。


QListQAudioFormat::SampleType QAbstractAudioDeviceInfo::supportedSampleTypes() [纯虚函数] 返回当前可用样本类型的列表。

[TOC]

QAbstractAudioInput类


QAbstractAudioInput类为QAudioInput类提供了访问音频设备的方法。(通过插件的形式)

属性方法
Header:#include <QAbstractAudioInput>
qmake:QT += multimedia
Inherits:QObject

简述

公有的函数

类型方法
virtual intbufferSize() const = 0
virtual intbytesReady() const = 0
virtual qint64elapsedUSecs() const = 0
virtual QAudio::Errorerror() const = 0
virtual QAudioFormatformat() const = 0
virtual intnotifyInterval() const = 0
virtual intperiodSize() const = 0
virtual qint64processedUSecs() const = 0
virtual voidreset() = 0
virtual voidresume() = 0
virtual voidsetBufferSize(int value) = 0
virtual voidsetFormat(const QAudioFormat &fmt) = 0
virtual voidsetNotifyInterval(int ms) = 0
virtual voidsetVolume(qreal) = 0
virtual voidstart(QIODevice *device) = 0
virtual QIODevice*start() = 0
virtual QAudio::Statestate() const = 0
virtual voidstop() = 0
virtual voidsuspend() = 0
virtual qrealvolume() const = 0

信号

类型方法
voiderrorChanged(QAudio::Error error)
voidnotify()
voidstateChanged(QAudio::State state)

详细描述

QAbstractAudioInput类为QAudioInput类提供了访问音频设备的方法。(通过插件的形式) QAudioDeviceInput类中保留了一个QAbstractAudioInput的实例,并且调用的函数与QAbstractAudioInput的一致。

译者注:也就是说QAudioDeviceInput调用的函数实际上是QAbstractAudioInput的函数,就封装了一层相同函数名吧。可以自己看看源码。)

这意味着QAbstractAudioInput是实现音频功能的。有关功能的描述,可以参考QAudioInput类。 另见QAudioInput函数


成员函数文档


int QAbstractAudioInput::bufferSize() const [纯虚函数]

以毫秒为单位返回音频缓冲区的大小


int QAbstractAudioInput::bytesReady() const [纯虚函数]

以字节(bytes)为单位返回可读取的音频数据量


qint64 QAbstractAudioInput**::elapsedUSecs() const [纯虚函数]

返回调用start()函数以来的毫秒数,包括空闲时间与挂起状态的时间


QAudio::Error QAbstractAudioInput::error() const [纯虚函数]

返回错误的状态


void QAbstractAudioInput::errorChanged(QAudio::Error error) [信号signal]

当错误状态改变时,该信号被发射


QAudioFormat QAbstractAudioInput::format() const [纯虚函数]

返回正在使用的QAudioFormat(这个类是储存音频流相关的参数信息的) 另参见setFormat()函数


void QAbstractAudioInput::notify() [信号signal]

当音频数据的x ms通过函数setNotifyInterval()调用之后,这个信号会被发射。


int QAbstractAudioInput::notifyInterval() const [纯虚函数]

以毫秒为单位返回通知间隔


int QAbstractAudioInput::periodSize() const [纯虚函数]

以字节为单位返回其周期


qint64 QAbstractAudioInput::processedUSecs() const [纯虚函数]

返回自start()函数被调用之后处理的音频数据量(以毫秒为单位)


void QAbstractAudioInput::reset() [纯虚函数]

将所有音频数据放入缓冲区,并将缓冲区重置为零


void QAbstractAudioInput::resume() [纯虚函数]

在音频数据暂停后继续处理


void QAbstractAudioInput::setBufferSize(int value) [纯虚函数]

将音频缓冲区大小设置为value大小(以毫秒为单位) 另参阅bufferSize()函数


void QAbstractAudioInput::setFormat(const QAudioFormat &fmt) [纯虚函数]

设置音频格式,设置格式的时候只能在QAudio的状态为StoppedState时(QAudio::StoppedState)


void QAbstractAudioInput::setNotifyInterval(int ms) [纯虚函数]

设置发送notify()信号的时间间隔。这个ms时间间隔与操作系统平台相关,并不是实际的ms数。


void QAbstractAudioInput::setVolume(qreal) [纯虚函数]

另见volume()函数 (设置这里应该是设置音量的值,Volume在英文中有音量的意思,官方文档这里根本就没有任何说明,说去参考valume()函数,可是valume()说又去参考SetValume()函数,这是互相甩锅的节奏么???坑爹啊!!!)


void QAbstractAudioInput::start(QIODevice *device) [纯虚函数]

使用输入参数QIODevice *device来传输数据


QIODevice *QAbstractAudioInput::start() [纯虚函数]

返回一个指向正在用于正在处理数据QIODevice的指针。这个指针可以用来直接读取音频数据。


QAudio::State QAbstractAudioInput::state() const [纯虚函数]

返回处理音频的状态


void QAbstractAudioInput::stateChanged(QAudio::State state) [信号signal]

当设备状态改变时,会发出这个信号


void QAbstractAudioInput::stop() [纯虚函数]

停止音频输入(因为这是个QAbstractAudioInput类啊,输入类啊,暂时这么解释比较合理。)


void QAbstractAudioInput::suspend() [纯虚函数]

停止处理音频数据,保存缓冲的音频数据


qreal QAbstractAudioInput::volume() const [纯虚函数]

另见setVolume()(内心os:参考我解释setVolume()函数的说明,这里应该是返回其音量)


[TOC] #QAbstractAudioOutput类


QAbstractAudioOutput类是音频后端的基类

属性方法
头文件包含: #include <QAbstractAudioOutput>
qmake写法:QT += multimedia
继承:QObject

简述

public函数

类型函数名
virtual intbufferSize() const = 0
virtual intbytesFree() const = 0
virtual QStringcategory() const
virtual qint64elapsedUSecs() const = 0
virtual QAudio::Errorerror() const = 0
virtual QAudioFormatformat() const = 0
virtual intnotifyInterval() const = 0
virtual intperiodSize() const = 0
virtual qint64processedUSecs() const = 0
virtual voidreset() = 0
virtual voidresume() = 0
virtual voidsetBufferSize(int value) = 0
virtual voidsetCategory(const QString &)
virtual voidsetFormat(const QAudioFormat &fmt) = 0
virtual voidsetNotifyInterval(int ms) = 0
virtual voidsetVolume(qreal volume)
virtual voidstart(QIODevice *device) = 0
virtual QIODevice *start() = 0
virtual QAudio::Statestate() const = 0
virtual voidstop() = 0
virtual voidsuspend() = 0
virtual qrealvolume() const

信号

类型函数名
voiderrorChanged(QAudio::Error error)
voidnotify()
voidstateChanged(QAudio::State state)

详细描述

QAbstractAudioOutput类是音频后端的基类。 QAbstractAudioOutput类是QAudioOutput类的实现类。QAudioOutput的实现实际上是调用的QAbstractAudioOutput类,有关实现相关的功能,请参考QAudioOutput()类中的函数说明。


成员函数


int QAbstractAudioOutput::bufferSize() const [纯虚函数] 以字节为单位,返回音频缓冲区的大小。

另见setBufferSize()函数


int QAbstractAudioOutput :: bytesFree ()const [纯虚函数] 返回音频缓冲区的可用空间(以字节为单位)


QString QAbstractAudioOutput::category() const [虚函数 virtual] 音频缓冲区的类别(官方文档没有,这是我个人经验,当然可能有误,望指正) 另见setCategory()


qint64 QAbstractAudioOutput::elapsedUSecs() const [纯虚函数 pure virtual] 返回调用start()函数之后的毫秒数,包括处于空闲状态的时间和挂起状态的时间。


QAudio::Error QAbstractAudioOutput::error() const [纯虚函数 pure virtual] 返回错误状态。


void QAbstractAudioOutput::errorChanged(QAudio::Error error) [信号 signal] 当错误状态改变时,该信号被发射。


QAudioFormat QAbstractAudioOutput :: format ()const [纯虚函数 pure virtual] 返回正在使用的QAudioFormat()类 另见setFormat()


void QAbstractAudioOutput::notify() [信号 signal] 当函数setNotifyInterval(x)函数已经调用,即音频数据的时间间隔已经被设置时。该信号被发射。(就是调用setNotifyInterval(x)后,这个信号会被发射。官方文档讲的好详细啊=。=)


int QAbstractAudioOutput::notifyInterval() const [纯虚函数 pure virtual] 以毫秒为单位,返回时间间隔 另见函数setNotifyInterval()


int QAbstractAudioOutput::periodSize() const [纯虚函数 pure virtual] 以字节为单位返回周期大小。


qint64 QAbstractAudioOutput::processedUSecs() const [纯虚函数 pure virtual] 返回自调用start()函数后处理的音频数据量(单位为毫秒)


void QAbstractAudioOutput::reset() [纯虚函数 pure virtual] 将所有音频数据放入缓冲区,并将缓冲区重置为零。


void QAbstractAudioOutput::resume() [纯虚函数 pure virtual] 继续处理暂停后的音频数据 (也就是暂停后继续的意思呗)


void QAbstractAudioOutput::setBufferSize(int value) [纯虚函数 pure virtual] 重新设置音频缓冲区的大小(以字节为单位 即输入参数value) 另见bufferSize()函数


void QAbstractAudioOutput::setCategory(const QString &) [虚函数 virtual] 参见函数category()


void QAbstractAudioOutput::setFormat(const QAudioFormat &fmt) [纯虚函数 pure virtual] QAbstractAudioOutput设置QAudioFormat类,只有当QAudio状态为QAudio::StoppedState时,音频格式才会被设置成功。 另见函数format()


void QAbstractAudioOutput::setNotifyInterval(int ms) [纯虚函数 pure virtual] 设置发送notify()信号的时间间隔。这个ms并不是实时处理的音频数据中的ms数。这个时间间隔是平台相关的。 另见notifyInterval()


void QAbstractAudioOutput::setVolume(qreal volume) [虚函数 virtual] 设置音量。音量的范围为[0.0 - 1.0]。 另见函数volume()


void QAbstractAudioOutput::start(QIODevice device) [纯虚函数 pure virtual] 调用start()函数时,输入参数QIODevice类型的变量device,用于音频后端处理数据传输。


*QIODevice QAbstractAudioOutput::start() [纯虚函数 pure virtual] 返回一个指向正在处理数据传输的QIODevice类型的指针,这个指针是可以被写入的,用于处理音频数据。(参考上边的函数是咋写入的)


QAudio::State QAbstractAudioOutput::state() const [纯虚函数 pure virtual] 返回音频处理的状态。


void QAbstractAudioOutput::stateChanged(QAudio::State state) [信号 signal] 当音频状态变化的时候,该信号被发射


void QAbstractAudioOutput::stop() [纯虚函数 pure virtual] 停止音频输出


void QAbstractAudioOutput::suspend() [纯虚函数 pure virtual] 停止处理音频数据,保存处理的音频数据。(就是暂停的意思啊=。=)


qreal QAbstractAudioOutput::volume() const [虚函数 virtual] 返回音量。音量范围为[0.0 - 1.0] 另参阅函数setVolume()


[TOC]

QAbstractAxis


QAbstractAxis类是用于专门处理坐标轴的类

属性方法
头文件: #include <QAbstractAxis>
实例化:AbstractAxis
继承:QObject
派生:QBarCategoryAxis, QDateTimeAxis, QLogValueAxis, and QValueAxis

简述


公共类型

类型方法
enumAxisType { AxisTypeNoAxis, AxisTypeValue, AxisTypeBarCategory, AxisTypeCategory, AxisTypeDateTime, AxisTypeLogValue }
flagsAxisTypes

属性

函数名类型
alignment :const Qt::Alignment
color :QColor
gridLineColor :QColor
gridLinePen :QPen
gridVisible :bool
labelsAngle :int
labelsBrush :QBrush
labelsColor :QColor
labelsFont :QFont
labelsVisible :bool
linePen :QPen
lineVisible :bool
minorGridLineColor :QColor
minorGridLinePen :QPen
minorGridVisible :bool
orientation :const Qt::Orientation
reverse :bool
shadesBorderColor :QColor
shadesBrush :QBrush
shadesColor :QColor
shadesPen :QPen
shadesVisible :bool
titleBrush :QBrush
titleFont :QFont
titleText :QString
titleVisible :bool
visible :bool

Public Functions

类型函数名
~QAbstractAxis()
Qt::Alignmentalignment() const
QColorgridLineColor()
QPengridLinePen() const
voidhide()
boolisGridLineVisible() const
boolisLineVisible() const
boolisMinorGridLineVisible() const
boolisReverse() const
boolisTitleVisible() const
boolisVisible() const
intlabelsAngle() const
QBrushlabelsBrush() const
QColorlabelsColor() const
QFontlabelsFont() const
boollabelsVisible() const
QPenlinePen() const
QColorlinePenColor() const
QColorminorGridLineColor()
QPenminorGridLinePen() const
Qt::Orientationorientation() const
voidsetGridLineColor(const QColor &color)
voidsetGridLinePen(const QPen &pen)
voidsetGridLineVisible(bool visible = true)
voidsetLabelsAngle(int angle)
voidsetLabelsBrush(const QBrush &brush)
voidsetLabelsColor(QColor color)
voidsetLabelsFont(const QFont &font)
voidsetLabelsVisible(bool visible = true)
voidsetLinePen(const QPen &pen)
voidsetLinePenColor(QColor color)
voidsetLineVisible(bool visible = true)
voidsetMax(const QVariant &max)
voidsetMin(const QVariant &min)
voidsetMinorGridLineColor(const QColor &color)
voidsetMinorGridLinePen(const QPen &pen)
voidsetMinorGridLineVisible(bool visible = true)
voidsetRange(const QVariant &min, const QVariant &max)
voidsetReverse(bool reverse = true)
voidsetShadesBorderColor(QColor color)
voidsetShadesBrush(const QBrush &brush)
voidsetShadesColor(QColor color)
voidsetShadesPen(const QPen &pen)
voidsetShadesVisible(bool visible = true)
voidsetTitleBrush(const QBrush &brush)
voidsetTitleFont(const QFont &font)
voidsetTitleText(const QString &title)
voidsetTitleVisible(bool visible = true)
voidsetVisible(bool visible = true)
QColorshadesBorderColor() const
QBrushshadesBrush() const
QColorshadesColor() const
QPenshadesPen() const
boolshadesVisible() const
voidshow()
QBrushtitleBrush() const
QFonttitleFont() const
QStringtitleText() const
virtual AxisTypetype() const = 0

信号

类型函数名
voidcolorChanged(QColor color)
voidgridLineColorChanged(const QColor &color)
voidgridLinePenChanged(const QPen &pen)
voidgridVisibleChanged(bool visible)
voidlabelsAngleChanged(int angle)
voidlabelsBrushChanged(const QBrush &brush)
voidlabelsColorChanged(QColor color)
voidlabelsFontChanged(const QFont &font)
voidlabelsVisibleChanged(bool visible)
voidlinePenChanged(const QPen &pen)
voidlineVisibleChanged(bool visible)
voidminorGridLineColorChanged(const QColor &color)
voidminorGridLinePenChanged(const QPen &pen)
voidminorGridVisibleChanged(bool visible)
voidreverseChanged(bool reverse)
voidshadesBorderColorChanged(QColor color)
voidshadesBrushChanged(const QBrush &brush)
voidshadesColorChanged(QColor color)
voidshadesPenChanged(const QPen &pen)
voidshadesVisibleChanged(bool visible)
voidtitleBrushChanged(const QBrush &brush)
voidtitleFontChanged(const QFont &font)
voidtitleTextChanged(const QString &text)
voidtitleVisibleChanged(bool visible)
voidvisibleChanged(bool visible)

详细说明


QAbstractAxis是专门用于处理坐标轴的基类。

每一个连续的序列可以绑定到一个或者多个水平轴和垂直轴,但是不同域的混合轴的类型是不支持的。比如在同一个方向指定QValueAxis和QLogValueAxis。

每个轴的元素(比如轴线,标题,标签,网格线,阴影,可见性)都是可以控制的。


成员变量

enum QAbstractAxis::AxisType flags QAbstractAxis::AxisTypes


这个枚举类型指定了轴对象的类型

变量
QAbstractAxis::AxisTypeNoAxis0x0
QAbstractAxis::AxisTypeValue0x1
QAbstractAxis::AxisTypeBarCategory0x2
QAbstractAxis::AxisTypeCategory0x4
QAbstractAxis::AxisTypeDateTime0x8
QAbstractAxis::AxisTypeLogValue0x10

AxisTypesQFlags<AxisType>的typedef。它是AxisType类型的组合。 (也就是个宏呗)


alignment : const Qt::Alignment 该属性是轴的对齐属性 其值可以为 Qt::AlignLeft, Qt::AlignRight, Qt::AlignBottom, or Qt::AlignTop. 相关函数

类型函数名
Qt::Alignmentalignment() const

color : QColor 该属性是指坐标轴与刻度的颜色

相关函数

类型函数名
QColorlinePenColor() const
voidsetLinePenColor(QColor color)

通知信号

类型函数名
voidcolorChanged(QColor color)

gridLineColor : QColor 该属性是指网格线的颜色

相关函数

类型函数名
QColorgridLineColor()
voidsetGridLineColor(const QColor &color)

通知信号

类型函数名
voidgridLineColorChanged(const QColor &color)

gridLinePen : QPen 该属性是指绘制网格线的笔

相关函数

类型函数名
QPengridLinePen() const
voidsetGridLinePen(const QPen &pen)

通知信号

类型函数名
voidgridLinePenChanged(const QPen &pen)

gridVisible : bool 该属性是网格线是否可见

相关函数

类型函数名
QPengridLinePen() const
voidsetGridLinePen(const QPen &pen)

通知信号

类型函数名
voidgridLinePenChanged(const QPen &pen)

labelsAngle : int 该属性以度数保存轴坐标的角度

相关函数

类型函数名
intlabelsAngle() const
voidsetLabelsAngle(int angle)

通知信号

类型函数名
voidlabelsAngleChanged(int angle)

labelsBrush : QBrush 该属性表示用于绘制标签的画笔 只有画刷的颜色是相关的(这句话其实我不太理解Only the color of the brush is relevant.)

相关函数

类型函数名
QBrushlabelsBrush() const
voidsetLabelsBrush(const QBrush &brush)

通知信号

类型函数名
voidlabelsBrushChanged(const QBrush &brush)

labelsColor : QColor 该属性表示轴标签的颜色

相关函数

类型函数名
QColorlabelsColor() const
voidsetLabelsColor(QColor color)

通知信号

类型函数名
voidlabelsColorChanged(QColor color)

labelsFont : QFont 该属性表示轴标签的字体信息

相关函数

类型函数名
QFontlabelsFont() const
voidsetLabelsFont(const QFont &font)

通知信号

类型函数名
voidlabelsFontChanged(const QFont &font)

labelsVisible : bool 该属性表示轴标签是否可见

相关函数

类型函数名
boollabelsVisible() const
voidsetLabelsVisible(bool visible = true)

通知信号

类型函数名
voidlabelsVisibleChanged(bool visible)

linePen : QPen 该属性表示绘制轴线的笔相关

相关函数

类型函数名
QPenlinePen() const
voidsetLinePen(const QPen &pen)

通知信号

类型函数名
voidlinePenChanged(const QPen &pen)

lineVisible : bool 该属性表示轴线是否可见

相关函数

类型函数名
boolisLineVisible() const
voidsetLineVisible(bool visible = true)

通知信号

类型函数名
voidlineVisibleChanged(bool visible)

minorGridLineColor : QColor 该属性表示副格线的颜色 仅适用于支持副网格线的轴

相关函数

类型函数名
QColorminorGridLineColor()
voidsetMinorGridLineColor(const QColor &color)

通知信号

类型函数名
voidminorGridLineColorChanged(const QColor &color)

minorGridLinePen : QPen 该属性表示副格线的笔 仅适用于支持副网格线的轴

相关函数

类型函数名
QPenminorGridLinePen() const
voidsetMinorGridLinePen(const QPen &pen)

通知信号

类型函数名
voidminorGridLinePenChanged(const QPen &pen)

minorGridVisible : bool 该属性表示副格线是否可见 仅适用于支持副网格线的轴

相关函数

类型函数名
boolisMinorGridLineVisible() const
voidsetMinorGridLineVisible(bool visible = true)

通知信号

类型函数名
voidminorGridVisibleChanged(bool visible)

orientation : const Qt::Orientation 该属性表示坐标轴的方向。 当坐标轴被添加到图表时,该属性为Qt::Horizontal或者Qt::Vertical

相关函数

类型函数名
Qt::Orientationorientation() const

reverse : bool 该属性表示是否使用反转轴。 该值默认为false。 反转轴由直线,样条,散列图系列以及笛卡尔图表组成的区域支持。如果一个方向相反,或者行为为定义,则所有相同方向的所有轴必须保持一致。

相关函数

类型函数名
boolisReverse() const
voidsetReverse(bool reverse = true)

通知信号

类型函数名
voidreverseChanged(bool reverse)

shadesBorderColor : QColor 该属性表示坐标轴阴影的边框(笔)颜色

相关函数

类型函数名
QColorshadesBorderColor() const
voidsetShadesBorderColor(QColor color)

通知信号

类型函数名
voidshadesBorderColorChanged(QColor color)

shadesBrush : QBrush 该属性表示用于绘制轴阴影的画笔(网格线之间的区域)

相关函数

类型函数名
QPenshadesPen() const
voidsetShadesPen(const QPen &pen)

通知信号

类型函数名
voidshadesPenChanged(const QPen &pen)

shadesVisible : bool 该属性表示轴阴影是否可见

相关函数

类型函数名
boolshadesVisible() const
voidsetShadesVisible(bool visible = true)

通知信号

类型函数名
voidshadesVisibleChanged(bool visible)

titleBrush : QBrush 该属性表示用于绘制坐标轴标题文本的画笔。 只影响画刷的颜色。

相关函数

类型函数名
QBrushtitleBrush() const
voidsetTitleBrush(const QBrush &brush)

通知信号

类型函数名
voidtitleBrushChanged(const QBrush &brush)

titleFont : QFont 该属性表示坐标轴标题的字体。

相关函数

类型函数名
QFonttitleFont() const
voidsetTitleFont(const QFont &font)

通知信号

类型函数名
voidtitleFontChanged(const QFont &font)

titleText : QString 该属性表示坐标轴的标题 默认为空,坐标轴的标题支持HTML的格式。

相关函数

类型函数名
QStringtitleText() const
voidsetTitleText(const QString &title)

通知信号

类型函数名
voidtitleTextChanged(const QString &text)

titleVisible : bool 该属性表示坐标轴的可见性。 默认值为true。

相关函数

类型函数名
boolisTitleVisible() const
voidsetTitleVisible(bool visible = true)

通知信号

类型函数名
voidtitleVisibleChanged(bool visible)

visible : bool 该属性表示坐标轴的可见性。

相关函数

类型函数名
boolisVisible() const
voidsetVisible(bool visible = true)

通知信号

类型函数名
voidvisibleChanged(bool visible)

##成员函数


QAbstractAxis::~QAbstractAxis() 析构函数,销毁轴对象,当一个坐标轴添加到图表时,该图表获得该坐标轴的所有权。


void QAbstractAxis::colorChanged(QColor color) [信号] 当坐标轴的颜色变化时,该信号被发射。 注意:属性color的通知信号。


void QAbstractAxis::gridLineColorChanged(const QColor &color) [信号] 当绘制网格线的笔的颜色改变时,该信号被发射。 注意:属性gridLineColor的通知信号。


QPen QAbstractAxis::gridLinePen() const 返回用于绘制网格的笔。 注意:属性gridLinePen的Getter函数。 另参见函数setGridLinePen()。


void QAbstractAxis :: gridLinePenChanged(const QPen&pen) [信号] 当用于绘制网格线的笔变化时,会发出此信号。 注意:属性gridLinePen的通知信号。


void QAbstractAxis::gridVisibleChanged(bool visible) [信号] 当坐标轴的网格线的可见性变化时,发出该信号。 注意:属性gridVisible的通知信号。


void QAbstractAxis::hide() 使坐标轴,阴影,标签,网格线不可见。


void QAbstractAxis::labelsAngleChanged(int angle) [信号] 当坐标轴标签的角度变化时,发出该信号。 注意:属性标签角度的通知信号


QBrush QAbstractAxis::labelsBrush() const 返回用于绘制标签的画笔。 注意:属性labelsBrush的Getter函数。 另参见setLabelsBrush().


void QAbstractAxis::labelsBrushChanged(const QBrush &brush) [信号] 当用于绘制坐标轴标签的画笔改变时,会发出此信号。 属性Brush的通知信号。


void QAbstractAxis :: labelsColorChanged(QColor color) [信号signal] 当坐标轴标签的颜色改变时,会发出此信号。 属性labelsColor的通知信号。


**QFont QAbstractAxis::labelsFont()const ** 返回用于绘制标签的字体。 注意:属性labelsFont的Getter函数。 另参见函数setLabelsFont()。


void QAbstractAxis :: labelsFontChanged(const QFont&font)[信号] 当坐标轴的字体改变时,会发出此信号。 属性labelsFont的通知信号。


void QAbstractAxis::labelsVisibleChanged(bool visible) [信号] 当坐标轴标签的可见性变化时,会发出此信号。 属性labelsVisible的通知信号。


QPen QAbstractAxis::linePen() const 返回用于绘制轴线与刻度线的笔。 注意:属性linePen的Getter函数。 另参阅函数setLinePen()。


void QAbstractAxis::linePenChanged(const QPen &pen) [信号] 当绘制坐标轴的笔变化时,会发出此信号。 注意:属性linePen的通知信号。


void QAbstractAxis::lineVisibleChanged(bool visible) [信号] 当坐标轴线的可见性变化时,会发出此信号。 注意:属性lineVisible的通知信号。


void QAbstractAxis::minorGridLineColorChanged(const QColor &color) [信号] 当绘制副格线的笔的颜色变化时,该信号被发射。 注意:属性minorGridLineColor的通知信号。


void QAbstractAxis::minorGridLinePenChanged(const QPen &pen) [信号] 当绘制副格线的笔变化时,该信号被发射。 注意:属性minorGridLinePen的通知信号。


void QAbstractAxis::minorGridVisibleChanged(bool visible) [信号] 当绘制副格线的可见性变化时,该信号被发射。 注意:属性minorGridVisible的通知信号。


Qt::Orientation QAbstractAxis::orientation() const 返回坐标轴的方向(垂直或者水平) 注意:坐标轴方向的Getter函数。


void QAbstractAxis::setGridLinePen(const QPen &pen) 设置绘制网格线的笔。 注意:gridLinePen的Setter函数。 另参见函数gridLinePen()。


void QAbstractAxis::setLabelsBrush(const QBrush &brush) 设置用于绘制标签的画笔。 注意:属性LabelsBrush的Setter函数。 另参阅labelsBrush().


void QAbstractAxis::setLabelsFont(const QFont &font) 设置用于绘制标签的字体相关 注意:属性labelsFont的Setter函数。 另参阅函数labelsFont()。


void QAbstractAxis::setLinePen(const QPen &pen) 设置用于绘制坐标轴线和刻度线的笔。 注意:属性linePen的Setter函数。 另参见函数linePen()。


void QAbstractAxis::setLineVisible(bool visible = true) 设置坐标轴线与刻度线是否可见。 注意:属性lineVisible的Setter函数 另参见函数isLineVisible()。


void QAbstractAxis::setMax(const QVariant &max) 设置坐标轴上显示的最大值。根据当前坐标轴的类型,最大值参数会被转换为适当的值。如果转化失败,该函数设置无效。


void QAbstractAxis::setMin(const QVariant &min) 设置坐标轴上显示的最小值。根据当前坐标轴的类型,最小值参数会被转换为适当的值。如果转化失败,该函数设置无效。


void QAbstractAxis::setRange(const QVariant &min, const QVariant &max) 设置坐标轴的范围。根据当前坐标轴的类型,最大值最小值会被转换为适当的值。如果转化失败,该函数设置无效。


void QAbstractAxis::setShadesBrush(const QBrush &brush) 设置用于绘制阴影的画笔。 注意:属性shadesBrush的Setter函数。 另参见函数shadesBrush().


void QAbstractAxis::setShadesPen(const QPen &pen) 设置用于绘制阴影的笔。 注意:属性shadesPen的Setter函数。 另参见函数shadesPen().


void QAbstractAxis::setTitleBrush(const QBrush &brush) 设置用于绘制标题的画笔。 注意:属性titleBrush的Setter函数。 另参见函数titleBrush().


void QAbstractAxis::setTitleFont(const QFont &font) 设置用于绘制标题的笔。 注意:属性titleFont的Setter函数。 另参见函数titleFont().


void QAbstractAxis::setVisible(bool visible = true) 设置坐标轴,阴影,标签,网格线是否可见。 注意:属性visible的Setter函数。 另参见函数isVisible().


void QAbstractAxis::shadesBorderColorChanged(QColor color) [信号signal] 当绘制坐标轴边框的颜色变化时,会发出此信号。 注意:属性shadesBorderColor的通知信号。


QBrush QAbstractAxis::shadesBrush() const 返回用于绘制阴影的画笔。 注意:属性shadesBrush()的Getter函数。 另参阅函数setShadesBrush().


void QAbstractAxis::shadesBrushChanged(const QBrush &brush) [信号signal] 当绘制坐标轴阴影的画刷变化时,会发出此信号。 注意:属性shadesBrush()的通知信号。


void QAbstractAxis::shadesColorChanged(QColor color) [信号signal] 当绘制坐标轴阴影颜色发生变化时,会发出此信号。 注意:属性shadesColor()的通知信号。


QPen QAbstractAxis::shadesPen() const 返回用于绘制阴影的笔。 注意:属性shadesPen()的Getter函数 另参阅函数setShadesPen().


void QAbstractAxis::shadesPenChanged(const QPen &pen) [信号signal] 当绘制坐标轴阴影的笔发生变化时,会发出此信号。 注意:属性shadesPen()的通知信号。


void QAbstractAxis::shadesVisibleChanged(bool visible) [信号signal] 当坐标轴的阴影可见性变化时,会发出此信号。 注意:属性shadesVisible()的通知信号。


void QAbstractAxis::show() 使坐标轴,阴影,标签,网格线可见。


QBrush QAbstractAxis::titleBrush() const 返回用于绘制标题的画刷 注意:属性titleBrush的Getter函数 另参阅函数setTitleBrush()


void QAbstractAxis::titleBrushChanged(const QBrush &brush) [信号signal] 当绘制坐标轴标题的画刷变化时,该信号被发射 注意:titleBrush的通知信号。


QFont QAbstractAxis::titleFont() const 返回绘制标题的笔相关属性。 注意:titleFont的Getter函数 另参阅函数setTitleFont()


void QAbstractAxis::titleFontChanged(const QFont &font) [信号 signal] 当坐标轴标题的字体属性更改时,会发出此信号。 注意:titleFont的通知信号。


void QAbstractAxis::titleTextChanged(const QString &text) [信号 signal] 当坐标轴标题的文本内容改变时,会发出此信号。 注意:titleText的通知信号。


void QAbstractAxis::titleVisibleChanged(bool visible) [信号 signal] 当坐标轴的标题文本的可见性变化时,会发出此信号。 注意:titleVisible的通知信号


AxisType QAbstractAxis::type() const 返回坐标轴的类型


void QAbstractAxis::visibleChanged(bool visible) 当坐标轴的可见性变化时,会发出此信号。 注意:坐标轴的visible的通知信号

[TOC]

QAbstractBarSeries


QAbstractBarSeries是所有柱状图/条形图系列的基类

属性方法
头文件:#include<QAbstractBarSeries>
实例化:AbstractBarSeries
继承:QAbstractSeries
派生:QBarSeries, QHorizontalBarSeries, QHorizontalPercentBarSeries, QHorizontalStackedBarSeries, QPercentBarSeries, and QStackedBarSeries

简述


公共类型

类型方法
enumLabelsPosition { LabelsCenter, LabelsInsideEnd, LabelsInsideBase, LabelsOutsideEnd }

属性

函数名类型
barWidth :qreal
count :const int
labelsAngle :qreal
5个属性继承自QAbstractSeries
1个属性继承自QObject

Public Functions

类型函数名
virtual~QAbstractBarSeries()
boolappend(QBarSet *set)
boolappend(QList<QBarSet *> sets)
QList<QBarSet *>barSets() const
qrealbarWidth() const
voidclear()
intcount() const
boolinsert(int index, QBarSet *set)
boolisLabelsVisible() const
qreallabelsAngle() const
QStringlabelsFormat() const
QAbstractBarSeries::LabelsPositionlabelsPosition() const
boolremove(QBarSet *set)
voidsetBarWidth(qreal width)
voidsetLabelsAngle(qreal angle)
voidsetLabelsFormat(const QString &format)
voidsetLabelsPosition(QAbstractBarSeries::LabelsPosition position)
voidsetLabelsVisible(bool visible = true)
booltake(QBarSet *set)
15个公共函数继承自QAbstractSeries
32个公共函数继承自QObject

信号

类型函数名
voidbarsetsAdded(QList<QBarSet *> sets)
voidbarsetsRemoved(QList<QBarSet *> sets)
voidclicked(int index, QBarSet *barset)
voidcountChanged()
voiddoubleClicked(int index, QBarSet *barset)
voidhovered(bool status, int index, QBarSet *barset)
voidlabelsAngleChanged(qreal angle)
voidlabelsFormatChanged(const QString &format)
voidlabelsPositionChanged(QAbstractBarSeries::LabelsPosition position)
voidlabelsVisibleChanged()
voidpressed(int index, QBarSet *barset)
voidreleased(int index, QBarSet *barset)

额外继承的

1个公共槽继承自QObject 11个静态成员函数继承自QObject 9个保护函数继承自QObject


详细说明

QAbstractBarSeries类是所有条形柱的抽象类。

在条形图中,条形柱被定义为包含一种数据的集合。条形柱的位置由其类别与数值来决定。条形柱组合则是属于同一类别的条形柱。条形柱的显示则是由创建图表的时候决定的。

如果使用QValueAxis来代替QBarCategoryAxis当做图表的主轴。那么条形柱别按照索引值来分类。

可以参考Qt Example(example 这里我还没有来得及翻译)


成员类型

enum QAbstractBarSeries::LabelsPosition**

这个枚举值表示的是条形柱标签的位置:

枚举值数值描述
QAbstractBarSeries::LabelsCenter0中部
QAbstractBarSeries::LabelsInsideEnd1顶部
QAbstractBarSeries::LabelsInsideBase2底部
QAbstractBarSeries::LabelsOutsideEnd3外部

[TOC]

QAbstractSocket Class

QAbstractSocket 类是Qt中 Socket 通信类的基类,被 QTcpSocketQUdpSocket等类继承。QAbstractSocket 类为所有的socket通信类提供了最基本的功能。

属性方法
头文件#include <QAbstractSocket>
qmake参数QT += network
父类QIODevice
子类QTcpSocketQUdpSocket

公共成员类型

类型方法
enumBindFlag { ShareAddress, DontShareAddress, ReuseAddressHint, DefaultForPlatform }
flagsBindMode
enumNetworkLayerProtocol{ IPv4Protocol, IPv6Protocol, AnyIPProtocol, UnknownNetworkLayerProtocol }
enumPauseMode { PauseNever, PauseOnSslErrors }
flagsPauseModes
enumSocketError { ConnectionRefusedError, RemoteHostClosedError, HostNotFoundError, SocketAccessError, SocketResourceError, …, UnknownSocketError }
enumSocketOption{ LowDelayOption, KeepAliveOption, MulticastTtlOption, MulticastLoopbackOption, TypeOfServiceOption, …, PathMtuSocketOption }
enumSocketState { UnconnectedState, HostLookupState, ConnectingState, ConnectedState, BoundState, …, ListeningState }
enumSocketType { TcpSocket, UdpSocket, SctpSocket, UnknownSocketType }

公共成员函数

类型函数名
QAbstractSocket(QAbstractSocket::SocketType socketType, QObject *parent)
virtual~QAbstractSocket()
voidabort()
boolbind(const QHostAddress &address, quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform)
boolbind(quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform)
virtual voidconnectToHost(const QString &hostName, quint16 port, QIODevice::OpenMode openMode = ReadWrite, QAbstractSocket::NetworkLayerProtocol protocol = AnyIPProtocol)
virtual voidconnectToHost(const QHostAddress &address, quint16 port, QIODevice::OpenMode openMode = ReadWrite)
virtual voiddisconnectFromHost()
QAbstractSocket::SocketErrorerror() const
boolflush()
boolisValid() const
QHostAddresslocalAddress() const
quint16localPort() const
QAbstractSocket::PauseModespauseMode() const
QHostAddresspeerAddress() const
QStringpeerName() const
quint16peerPort() const
QStringprotocolTag() const
QNetworkProxyproxy() const
qint64readBufferSize() const
virtual voidresume()
voidsetPauseMode(QAbstractSocket::PauseModes pauseMode)
voidsetProtocolTag(const QString &tag)
voidsetProxy(const QNetworkProxy &networkProxy)
virtual voidsetReadBufferSize(qint64 size)
virtual boolsetSocketDescriptor(qintptr socketDescriptor, QAbstractSocket::SocketState socketState = ConnectedState, QIODevice::OpenMode openMode = ReadWrite)
virtual voidsetSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)
virtual qintptrsocketDescriptor() const
virtual QVariantsocketOption(QAbstractSocket::SocketOption option)
QAbstractSocket::SocketTypesocketType() const
QAbstractSocket::SocketStatestate() const
virtual boolwaitForConnected(int msecs = 30000)
virtual boolwaitForDisconnected(int msecs = 30000)

重载公共成员函数

类型函数名
virtual boolatEnd() const override
virtual qint64bytesAvailable() const override
virtual qint64bytesToWrite() const override
virtual boolcanReadLine() const override
virtual voidclose() override
virtual boolisSequential() const override
virtual boolwaitForBytesWritten(int msecs = 30000) override
virtual boolwaitForReadyRead(int msecs = 30000) override

信号

类型函数名
voidconnected()
voiddisconnected()
voiderrorOccurred(QAbstractSocket::SocketError socketError)
voidhostFound()
voidproxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
voidstateChanged(QAbstractSocket::SocketState socketState)

保护成员函数

类型函数名
voidsetLocalAddress(const QHostAddress &address)
voidsetLocalPort(quint16 port)
voidsetPeerAddress(const QHostAddress &address)
voidsetPeerName(const QString &name)
voidsetPeerPort(quint16 port)
voidsetSocketError(QAbstractSocket::SocketError socketError)
voidsetSocketState(QAbstractSocket::SocketState state)

重载保护成员函数

类型函数名
virtual qint64readData(char *data, qint64 maxSize) override
virtual qint64readLineData(char *data, qint64 maxlen) override
virtual qint64writeData(const char *data, qint64 size) override

详细介绍

QAbstractSocket 类是 QTcpSocket 类和 QUdpSocket 类的基类,包含了这两个类所有的常规功能。您可以通过以下两种方法使用一个套接字( Socket ):

  • 实例化一个 QTcpSocket 或者 QUdpSocket 对象
  • 声明一个自定义套接字描述符,实例化 QAbstractSocket ,然后调用 setSocketDescriptor() 函数包装该自定义套接字描述符。

TCP(传输控制协议)是一种可靠的,面向流,面向连接的传输协议。 UDP(用户数据报协议)是一种不可靠的,面向数据报的无连接协议。 实际上,这意味着TCP更适合于连续数据传输,而当可靠性不重要时,可以使用更轻量的UDP。

QAbstractSocket 的 API 统一了这两种协议之间的大部分差异。 例如,尽管 UDP 是无连接的,但 connectToHost() 为 UDP 套接字建立了虚拟连接,使您可以忽略底层协议,以几乎相同的方式使用 QAbstractSocket 类。 在 QAbstractSocket 类的内部实现中,QAbstractSocket 记录了传递给 connectToHost() 的地址和端口,并且能在调用 read() 和 write() 之类的成员函数时使用这些值。

任何情况下,QAbstractSocket 类都有一个状态state ,该值可以由 state() 成员函数的返回值获得)。 初始状态为未连接QAbstractSocket :: UnconnectedState )状态。 调用 connectToHost() 成员函数连接主机后,套接字会首先进入寻找主机QAbstractSocket :: HostLookupState )状态。 如果找到了主机,则 QAbstractSocket 会进入连接中QAbstractSocket :: ConnectingState )状态,并发送 hostFound() 信号。 建立连接后,它将进入已连接QAbstractSocket :: ConnectedState )状态并发送 connected() 信号。 如果在以上列出任何阶段发生了错误,则会发出 errorOccurred() 信号。 每当状态发生更改时,QAbstractSocket 都会发出 stateChanged() 信号。 为方便起见,当套接字已准备好进行读取和写入数据操作时,isValid() 成员函数的返回值为 true 。但是要注意一下,在进行读写操作之前,套接字的状态必须为已连接QAbstractSocket :: ConnectedState )状态。

您可以通过调用 read() 或 write() 来进行数据读写操作,同时为了方便进行特殊的数据读入操作,您还可以使用 QAbstractSocket 的父类 QIODevice 提供的成员函数 readLine() 和 readAll() 。当我们需要以字节为单位进行数据读写操作时,可以使用 QAbstractSocket 的父类 QIODevice 提供的成员函数 getChar(),putChar() 和 ungetChar() 。 待数据写入套接字后,QAbstractSocket 会发出继承自父类 QIODevice 的信号 bytesWritten() 。请特别注意一下,Qt并不限制写缓冲区的大小。 您可以通过监听 bytesWritten() 信号来监视其大小。

每当有新的数据块到达时,QAbstractSocket 都会发出继承自父类 QIODevice 的信号 readyRead() 。 您可以通过 bytesAvailable() 成员函数的返回值来获得当前读取缓冲区中可读取的字节数。 通常来讲,您可以将 readyRead() 信号与一个槽函数相连接,然后在该槽函数中读取所有可用的数据。 如果您不一次性读取所有数据,则其余数据以后仍可以读取,并且任何新的传入数据都将追加到 QAbstractSocket 的内部读取缓冲区中。您可以通过调用 setReadBufferSize() 成员函数来限制读取缓冲区的大小。

您可以通过调用 disconnectFromHost() 成员函数关闭套接字。 调用 disconnectFromHost() 成员函数后,QAbstractSocket 会进入关闭中QAbstractSocket :: ClosingState )状态。 待所有未处理数据写入套接字后,QAbstractSocket 将关闭套接字,进入未连接QAbstractSocket :: UnconnectedState )状态,并发送 disconnected() 信号。 如果要立即中止连接,并丢弃所有未处理数据,请调用 abort() 成员函数。 如果远程主机关闭了该连接,QAbstractSocket 将发出 errorOccurred(QAbstractSocket :: RemoteHostClosedError) 错误信号,在此期间套接字状态仍为已连接QAbstractSocket :: ConnectedState )状态,此后将发送 disconnected() 信号。

您可以通过调用 peerPort()peerAddress() 成员函数来获取已连接的对等方的端口和地址。 peerName() 成员函数则会返回传递给 connectToHost() 的对等方的主机名。 另外,localPort()localAddress() 成员函数可返回本地套接字的端口和地址。

QAbstractSocket 提供了一组函数,这些函数可以挂起调用线程,直到发出某些信号为止。 这些函数可用于实现阻塞套接字:

Qt官方提供了如下示例:

int numRead = 0, numReadTotal = 0;
char buffer[50];

forever {
	numRead  = socket.read(buffer, 50);

	// do whatever with array

	numReadTotal += numRead;
	if (numRead == 0 && !socket.waitForReadyRead())
		break;
}

waitForReadyRead() 成员函数返回值为 false,则说明连接已关闭或发生了错误。

使用阻塞套接字进行编程与使用非阻塞套接字进行编程完全不同。 阻塞套接字不需要有一个事件循环,这通常可以简化代码。 但是,在GUI应用程序中,阻塞套接字只能在非GUI线程中使用,以避免冻结用户界面。 有关这两种方法的概述,请参见 fortuneclien 和 blockingfortuneclient 示例。

注意: Qt官方并不推荐将阻塞函数与信号一起使用。

QAbstractSocket 可以与 QTextStreamQDataStream 的流运算符(operator<<() 和operator>>())一起使用。 但是,有一个问题需要注意:在尝试使用operator>>() 读取数据之前,必须确保有足够的数据可用。

您也可以在 QNetworkAccessManager 类和 QTcpServer 类的文档中找到一部分相关的信息。

成员类型文档

enum QAbstractSocket::BindFlag | flags QAbstractSocket::BindMode

该枚举描述了一些不同的标志,这些标志可以传递为 bind() 成员函数的参数,指定了不同的主机绑定模式。

常量描述
QAbstractSocket::ShareAddress0x1允许其他服务绑定到相同的地址和端口。 在多个进程通过侦听相同的地址和端口来分担单个服务的负载的情况下(例如,具有多个预分支侦听器的Web服务器可以大大缩短响应时间),该模式十分有效。 但是,由于该模式下允许任何服务对主机进行重新绑定,因此此选项存在着安全隐患。 请注意,您还可以允许您的服务重新绑定现有的共享地址通过将此选项与 QAbstractSocket::ReuseAddressHint 结合使用。 在 Unix 上,这等效于 SO_REUSEADDR 套接字选项。 在 Windows 上,这是默认行为,因此将忽略此选项。
QAbstractSocket::DontShareAddress0x2绑定主机时独占地址和端口,不允许其他服务重新绑定。 将此选项作为 QAbstractSocket :: bind() 参数,可以确保在绑定主机成功后,您当前的服务是唯一侦听指定地址和端口的服务。 即使其他服务通过指定 QAbstractSocket::ReuseAddressHint 模式来绑定服务,这个操作也是不允许的。 该模式相比 QAbstractSocket::ShareAddress 提供了更高的安全性,但是在某些操作系统上,它要求您以管理员权限运行该服务。 在 Unix 和 macOS 上,独占地址和端口是绑定主机的默认行为,因此该选项将被忽略。 在 Windows 上,此选项使用 SO_EXCLUSIVEADDRUSE 套接字选项。
QAbstractSocket::ReuseAddressHint0x4示意 QAbstractSocket 即使地址和端口已被另一个套接字绑定,它也应尝试重新绑定服务。 在 Windows 和 Unix 上,这等效于 SO_REUSEADDR 套接字选项。
QAbstractSocket::DefaultForPlatform0x0使用当前平台的默认模式。 在 Unix 和 macOS 上,这等效于( QAbstractSocket::DontShareAddress + QAbstractSocket::ReuseAddressHint ),在 Windows 上,它等效于 QAbstractSocket::ShareAddress

该枚举最初在Qt5.0版本引入。

BindMode 类型是 typedef QFlags <BindFlag> 生成的用户自定义类型。 它存储着 BindFlag 值的 OR 组合。


enum QAbstractSocket::NetworkLayerProtocol

该枚举描述了Qt中可以使用的网络层协议。

常量描述
QAbstractSocket::IPv4Protocol0IPv4
QAbstractSocket::IPv6Protocol1IPv6
QAbstractSocket::AnyIPProtocol2IPv4 或 IPv6
QAbstractSocket::UnknownNetworkLayerProtocol-1除IPv4和IPv6之外的协议

您也可以在 QHostAddress::protocol() 中找到有关网络层协议的相关知识。


enum QAbstractSocket::PauseMode | flags QAbstractSocket::PauseModes

该枚举描述了套接字在什么情况下该停止传输中的数据。 当前Qt支持的唯一通知是 QSslSocket :: sslErrors() 。

常量描述
QAbstractSocket::PauseNever0x0“永远”不暂停套接字上的数据传输。 该模式是默认设置,符合Qt 4的标准。
QAbstractSocket::PauseOnSslErrors0x1收到 SSL 错误(即 QSslSocket :: sslErrors() )的通知后,暂停套接字上的数据传输。

该枚举最初在Qt5.0版本引入。

PauseModes 类型是 typedef QFlags<PauseMode> 生成的用户自定义类型。它储存着 PauseMode 值的 OR 组合。


enum QAbstractSocket::SocketError

该枚举描述了常见的套接字错误。

常量描述
QAbstractSocket::ConnectionRefusedError0连接被对方拒绝(或连接超时)。
QAbstractSocket::RemoteHostClosedError1远程主机关闭了连接。 请注意,在发送远程主机连接关闭的通知后,客户端套接字(即此套接字)将被关闭。
QAbstractSocket::HostNotFoundError2未能找到主机地址。
QAbstractSocket::SocketAccessError3程序没有权限来进行特定的套接字操作
QAbstractSocket::SocketResourceError4本地系统资源不足(例如本地系统使用了过多的套接字)。
QAbstractSocket::SocketTimeoutError5套接字操作超时。
QAbstractSocket::DatagramTooLargeError6数据报大于操作系统的限制(该限制可以低至8192字节)。
QAbstractSocket::NetworkError7网络发生错误(例如网线断开)。
QAbstractSocket::AddressInUseError8QAbstractSocket::bind() 指定的主机地址已被另一个套接字使用独占模式绑定。
QAbstractSocket::SocketAddressNotAvailableError9QAbstractSocket :: bind() 指定的主机地址不属于主机。
QAbstractSocket::UnsupportedSocketOperationError10本地操作系统不支持请求的套接字操作(例如,缺少IPv6支持)。
QAbstractSocket::ProxyAuthenticationRequiredError12套接字正在使用代理,并且代理需要身份验证。
QAbstractSocket::SslHandshakeFailedError13SSL / TLS 握手失败,因此连接已关闭(仅在 QSslSocket 中使用)。
QAbstractSocket::UnfinishedSocketOperationError11仅由 QAbstractSocketEngine 使用,上一次尝试的操作尚未完成(仍在后台进行)。
QAbstractSocket::ProxyConnectionRefusedError14代理服务器拒绝了连接。
QAbstractSocket::ProxyConnectionClosedError15与代理服务器的连接意外关闭(在建立与对方的最终连接之前)。
QAbstractSocket::ProxyConnectionTimeoutError16与代理服务器的连接超时或代理服务器在身份验证阶段停止响应。
QAbstractSocket::ProxyNotFoundError17找不到使用 setProxy() (或应用程序代理)设置的代理地址。
QAbstractSocket::ProxyProtocolError18与代理服务器通信失败,因为无法理解来自代理服务器的响应,这通常是代理服务协议错误的锅。
QAbstractSocket::OperationError19套接字所处的状态不允许该操作
QAbstractSocket::SslInternalError20使用的 SSL 库出现内部错误。 这可能是由于 SSL 库安装错误或配置错误造成的。
QAbstractSocket::SslInvalidUserDataError21提供了无效的数据(证书,密钥,密码等),其使用导致 SSL 库出现错误。
QAbstractSocket::TemporaryError22发生临时错误(例如,操作将阻塞套接字,而套接字未阻塞)。
QAbstractSocket::UnknownSocketError-1神了,出现了一个未知错误。

您也可以在 QAbstractSocket::error()QAbstractSocket::errorOccurred() 成员函数的详细介绍中找到一部分套接字错误的介绍。


enum QAbstractSocket::SocketOption

该枚举表示可以在套接字上设置的选项。 如果需要设置这些选项,您可以在套接字接收到 connectd() 信号之后,或者在 QTcpServer 接收到新的套接字之后,对它们进行设置。

常量描述
QAbstractSocket::LowDelayOption0尝试优化套接字以降低延迟。 对于 QTcpSocket ,这将设置 TCP_NODELAY 选项并禁用 Nagle 的算法。 将此设置为1启用。
QAbstractSocket::KeepAliveOption1将此设置为1以启用 SO_KEEPALIVE 套接字选项
QAbstractSocket::MulticastTtlOption2将此设置为整数值以设置 IP_MULTICAST_TTL (多播数据报的 TTL )套接字选项。
QAbstractSocket::MulticastLoopbackOption3将此设置为1以启用 IP_MULTICAST_LOOP (多播环回)套接字选项。
QAbstractSocket::TypeOfServiceOption4Windows 不支持此选项。 这映射到IP_TOS套接字选项。 有关其可能的可取值,请参见下表。
QAbstractSocket::SendBufferSizeSocketOption5在操作系统级别设置套接字发送缓冲区的大小(以字节为单位)。 这映射到 SO_SNDBUF 套接字选项。 该选项不会影响 QIODevice 或 QAbstractSocket 缓冲区。 这个枚举值在Qt 5.3中引入。
QAbstractSocket::ReceiveBufferSizeSocketOption6在操作系统级别设置套接字接收缓冲区的大小(以字节为单位)。 这映射到 SO_RCVBUF 套接字选项。 此选项不会影响 QIODevice 或 QAbstractSocket 缓冲区(请参见 setReadBufferSize() )。 这个枚举值已在Qt 5.3中引入。
QAbstractSocket::PathMtuSocketOption7检索IP堆栈当前已知的路径最大传输单位( PMTU )值(如果该值存在)。 一些IP堆栈还允许设置 MTU 进行传输。 这个枚举值在Qt 5.11中引入。

TypeOfServiceOption 可能的可取值(优先级):

描述
224Network control(网络控制)
192Internetwork control(互联网络控制)
160CRITIC/ECP(至关重要)
128Flash override(火速覆盖)
96Flash(火速)
64Immediate(立即)
32Priority(主要)
0Routine(常规)

该枚举最初在Qt4.6版本引入。

您也可以在 QAbstractSocket::setSocketOption()QAbstractSocket::socketOption() 函数介绍中找到相关内容。


enum QAbstractSocket::SocketState

该枚举描述了套接字不同的状态。

常量ValueDescription
QAbstractSocket::UnconnectedState0套接字未连接。
QAbstractSocket::HostLookupState1套接字正在查询主机。
QAbstractSocket::ConnectingState2套接字开始建立连接。
QAbstractSocket::ConnectedState3新的连接已建立。
QAbstractSocket::BoundState4套接字已绑定到一个地址和端口。
QAbstractSocket::ClosingState6套接字即将关闭(数据可能仍在等待写入)。
QAbstractSocket::ListeningState5套接字仅限内部使用。

您也可以在 QAbstractSocket::state() 成员函数介绍中找到相关内容。


enum QAbstractSocket::SocketType

该枚举描述了传输层的协议。

ConstantValueDescription
QAbstractSocket::TcpSocket0TCP
QAbstractSocket::UdpSocket1UDP
QAbstractSocket::SctpSocket2SCTP
QAbstractSocket::UnknownSocketType-1除 TCP, UDP 和 SCTP 外的协议

您也可以在 QAbstractSocket::socketType() 成员函数介绍中找到相关内容。


成员函数文档

QAbstractSocket::QAbstractSocket(QAbstractSocket::SocketType socketType, QObject *parent)

创建一个新抽象套接字 socketType 。 函数中父对象的参数传递给 QObject 的构造函数。

这就是它的构造函数嘛,没啥好说的。另外由于 QAbstractSocket 类继承自 QObject 类,应注意在 QAbstractSocket 类构造函数中调用一下父类 QObject 类的构造函数。

另外您也可以在 socketType() 成员函数文档,以及 QTcpSocketQUdpSocket 类文档找到相关信息。


[SIGNAL] void QAbstractSocket::connected()

connectToHost() 调用并成功建立一个连接后,QAbstractSocket 类将发送 connectd() 信号。

注意: 在某些操作系统上,connected() 信号可能直接从 connectToHost() 调用发出,以连接到本地主机。

另外您也可以在 connectToHost()disconnected() 成员函数文档中找到相关信息。


[SIGNAL] void QAbstractSocket::disconnected()

套接字断开后会发送该信号。

警告: 如果您想在与这个信号相连接的槽函数中删除信号的发送者( sender() ),请使用 deleteLater() 函数。

另外您也可以在 connectToHost()disconnectFromHost()abort() 函数介绍中找到相关信息。


[SIGNAL] void QAbstractSocket::errorOccurred(QAbstractSocket::SocketError socketError)

当套接字遇到错误时会发送该信号。socketError 参数描述了该错误类型。

发出此信号后,套接字可能并未准备好进行重新连接。 在这种情况下,我们应尝试从事件循环中进行重新连接。 例如我们可以调用一个QTimer::singleShot(),并将0作为时间间隔。

请注意,QAbstractSocket :: SocketError 并不是Qt预注册好的元类型。因此如果您要在队列连接( queued connections )模式的信号与槽的连接中传递该类型的参数,您必须使用 Q_DECLARE_METATYPE() 和 qRegisterMetaType() 注册它为元类型。

该函数在Qt5.15版本引入。

另外您也可以在 error() 和 errorString() 函数介绍以及 Creating Custom Qt Types 文档中找到相关信息。


[SIGNAL] void QAbstractSocket::hostFound()

在 QAbstractSocket 调用 connectToHost() 函数并成功找到主机后,它将发送该消息。

注意: 从Qt 4.6.3开始,由于可能已缓存DNS结果,因此 QAbstractSocket 可能直接在调用 connectToHost() 时发送 hostFound() 信号。

另外您也可以在 connectd() 函数介绍中找到相关信息。


[SIGNAL] void QAbstractSocket::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)

套接字连接的代理服务期需要身份验证时会发送该消息。您可以用所需的详细信息填充 authenticator 对象,以便代理服务期进行身份验证并继续连接。

注意: 如果信号返回时未使用新信息填充 authenticator ,则连接将失败,因此您不能使用队列连接模式( QueuedConnection )连接到该信号。

该函数首次在Qt4.3版本引入。

另外您也可以在 QAuthenticatorQNetworkProxy 文档中找到相关信息。


[SIGNAL] void QAbstractSocket::stateChanged(QAbstractSocket::SocketState socketState)

QAbstractSocket 状态发生改变后会发送该信号。socketState 参数记录了新的状态。

请注意,QAbstractSocket :: SocketState 并不是Qt预注册好的元类型。因此如果您要在队列连接( queued connections )模式的信号与槽的连接中传递该类型的参数,您必须使用 Q_DECLARE_METATYPE() 和 qRegisterMetaType() 注册它为元类型。

另外您也可以在 state() 成员函数介绍和 Create Custom Qt Types 文档中找到相关信息。


[virtual] QAbstractSocket::~QAbstractSocket()

析构函数,销毁套接字。


void QAbstractSocket::abort()

中止当前连接并重置套接字。 与 disconnectFromHost() 不同,此函数会立即关闭套接字,并丢弃写缓冲区中的所有未处理数据。

另外您也可以在 disconnectFromHost()close() 函数介绍中找到相关信息。


[override virtual] bool QAbstractSocket::atEnd() const

该函数重写了父类 QIODevice 的类成员函数 QIODevice::atEnd() const

如果当前没有更多数据可读取,则返回 true ,否则返回 false 。

在循环中从套接字读取数据时,最常使用此功能。 例如:

// This slot is connected to QAbstractSocket::readyRead()
void SocketClass::readyReadSlot()
{
	while (!socket.atEnd()) {
		QByteArray data = socket.read(100);
		....
	}
}

另外您也可以在 bytesAvailable() 和 readyRead() 函数介绍中找到相关信息。


bool QAbstractSocket::bind(const QHostAddress &address, quint16] port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform)

使用 mode 指定的 QAbstractSocket::BindMode 模式绑定到 addressport 指定的地址和端口上。

对于 UDP 套接字来说,在使用 bind() 绑定主机后,只要 UDP 数据报到了达指定的地址和端口,就会发出 QUdpSocket :: readyRead() 的信号。 因此,此功能对于编写UDP服务器很有用。

对于 TCP 套接字来说,此功能可用于指定将哪个接口用于传出连接,这在多个网络接口的情况下很有用。

默认情况下, QAbstractSocket 会使用 DefaultForPlatform BindMode 模式绑定套接字。 如果未指定端口 port ,则 QAbstractSocket 会选择一个随机端口。

成功后,函数返回 true ,套接字进入 已绑定BoundState )状态; 否则返回 false 。

该函数最初在Qt5.0版本引入。


bool QAbstractSocket::bind(quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform)

重载 bind() 函数。

该函数会使用 mode 指定的 QAbstractSocket::BindMode 模式绑定到主机上的任何地址( QHostAddress:Any ) 和 port 指定的端口上。

默认情况下, QAbstractSocket 会使用 DefaultForPlatform BindMode 模式绑定套接字。 如果未指定端口 port ,则 QAbstractSocket 会选择一个随机端口。

该函数最初在Qt5.0版本引入。


[override virtual] qint64 QAbstractSocket::bytesAvailable() const

该函数重写了父类 QIODevice 的类成员函数 QIODevice::bytesAvailable() const

返回等待读取的传入字节数。

另外您也可以在 bytesToWrite() 和 read() 函数介绍中找到相关信息。


[override virtual] qint64 QAbstractSocket::bytesToWrite() const

该函数重写了父类 QIODevice 的类成员函数 QIODevice::bytesToWrite() const

返回等待写入的字节数。 当控制权返回事件循环或调用 flush() 时,这些字节将会被写入。

另外您也可以在 bytesAvailable()flush() 函数介绍中找到相关信息。


[override virtual] bool QAbstractSocket::canReadLine() const

该函数重写了父类 QIODevice 的类成员函数 QIODevice::canReadLine() const

如果能从该套接字中读取一行数据时返回 true,否则返回 false 。

另外您也可以在 readLine() 函数介绍中找到相关信息。


[override virtual] void QAbstractSocket::close()

该函数重写了父类 QIODevice 的类成员函数 QIODevice::close()

关闭套接字的 I/O 设备并调用 disconnectFromHost() 来关闭套接字的连接。

您可以阅读 QIODevice::close() 的介绍来查阅关闭 I/O 设备时发生的操作。

另外您也可以在 abort() 函数介绍中找到相关信息。


[virtual] void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, QIODevice::OpenMode openMode = ReadWrite, QAbstractSocket::NetworkLayerProtocol protocol = AnyIPProtocol)

尝试连接到远程主机 hostname 的给定端口 portprotocol 参数可用于指定要使用的网络协议(例如IPv4或IPv6)。

使用 openMode 给定的模式打开套接字并进入 寻找主机HostLookupState )状态,然后 QAbstractSocket 开始执行主机名查找操作。 成功查找到主机名后, QAbstractSocket 会发出 hostFound() 信号,并进入连接中ConnectingState )状态。 接着 QAbstractSocket 会尝试连接到上一部查找主机后返回的一个或多个地址。 最终建立连接后,QAbstractSocket 会进入已连接ConnectedState )并发送 connectd() 信号。

在以上的任意一阶段中遇到错误,套接字都会发送 errorOccurred() 信号。

hostName 参数可以是一个字符串形式的IP地址(例如"49.195.83.32"),也可以是一个主机名(例如"example.com")。QAbstractSocket 仅在需要时才会进行查找。 port 端口参数按本机字节顺序。

另外您也可以在 state()peerName()peerAddress()peerPort()waitForConnected() 函数介绍中找到相关信息。


[virtual] void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port, QIODevice::OpenMode openMode = ReadWrite)

重载 connectToHost() 函数。

尝试连接到远程主机 hostname 的给定端口 port


[virtual] void QAbstractSocket::disconnectFromHost()

尝试关闭套接字。如果还有未处理的数据等待写入,QAbstractSocket 会进入正在关闭ClosingState )状态并等待所有数据被写入后再关闭套接字。最终 QAbstractSocket 会进入未连接UnconnectedState )状态并发送 disconnected() 信号。

另外您也可以在 connectToHost() 函数介绍中找到相关信息。


QAbstractSocket::SocketError QAbstractSocket::error() const

返回最后一个错误的类型。

另外您也可以在 state() 和 errorString() 函数介绍中找到相关信息。


bool QAbstractSocket::flush()

该函数尽可能多的在不阻塞套接字的前提下将数据从内部写入缓冲区写入基础网络套接字。 如果写入了任何数据,则此函数返回 true , 否则返回 false 。

可成功写入的字节数取决于操作系统。请仅在需要 QAbstractSocket 立即开始发送缓冲的数据的情况下调用此函数。 在大多数情况下,您不需要调用此函数,因为一旦控制权返回事件循环,QAbstractSocket 将自动开始发送数据。 在没有事件循环的情况下,请改为调用 waitForBytesWritten()

另外您也可以在 write() 和 waitForBytesWritten() 函数介绍中找到相关信息。


[override virtual] bool QAbstractSocket::isSequential() const

该函数重写了父类 QIODevice 的类成员函数 QIODevice::isSequential()


bool QAbstractSocket::isValid() const

如果套接字有效并且可以使用,则返回 true ,否则返回 false 。

注意: 在进行读写操作之前,套接字必须处于已连接ConnectedState )状态。

另外您也可以在 state() 函数介绍中找到相关信息。


QHostAddress QAbstractSocket::localAddress() const

如果本地套接字则可用返回主机地址,否则返回 QHostAddress::Null

返回的主机地址通常是主机的IP地址。但是当套接字连接到本地主机时,返回的主机地址为 QHostAddress::LocalAddress

另外您也可以在 localPort()peerAddress()setLocalAddress() 函数介绍中找到相关信息。


quint16 QAbstractSocket::localPort() const

如果本地套接字可用则按照本地字节顺序返回主机端口,否则返回0。

另外您也可以在 localAddress()peerPort()setLocalPort() 函数介绍中找到相关信息。


QAbstractSocket::PauseModes QAbstractSocket::pauseMode() const

返回该套接字的数据传输暂停模式。

该函数最初在Qt5.0版本引入。

另外您也可以在 setPauseMode()resume() 函数介绍中找到相关信息。


QHostAddress QAbstractSocket::peerAddress() const

如果套接字处于已连接ConnectedState )状态则返回对等端的地址,否则返回 QHostAddress::Null

另外您也可以在 peerName()peerPort()localAddress()setPeerAddress() 函数介绍中找到相关信息。


QString QAbstractSocket::peerName() const

返回在 connectToHost() 函数中指定的主机名。如果 connectToHost() 函数未被调用过则返回一个空的 QString 。

另外您也可以在 peerAddress()peerPort()setPeerName() 函数介绍中找到相关信息。


quint16 QAbstractSocket::peerPort() const

如果套接字处于已连接ConnectedState )状态则返回对等端的端口,否则返回0。

另外您也可以在 peerAddress()localPort()setPeerPort() 函数介绍中找到相关信息。


QString QAbstractSocket::protocolTag() const

返回此套接字的协议标签。 如果设置了协议标签,则在 QAbstractSocket 内部创建协议标签时将其传递给QNetworkProxyQuery,以指示要使用的协议标签。

该函数最初在Qt5.13版本引入。

另外您也可以在 setProtocolTag() 函数介绍和 QNetworkProxyQuery 文档中找到相关信息。


QNetworkProxy QAbstractSocket::proxy() const

返回该套接字的网络代理。默认情况下套接字会使用 QNetworkProxy::DefaultProxy ,套接字会查询应用程序的默认网络代理设置。

该函数最早在Qt4.1版本引入。

另外您也可以在 setProxy() 函数介绍以及 QNetworkProxyQNetworkProxyFactory 文档中找到相关信息。


qint64 QAbstractSocket::readBufferSize() const

返回内部读取缓冲区的大小。 这限制了客户端在调用 read() 或 readAll() 之前可以接收的数据量。

读取缓冲区大小为0(默认值)意味着缓冲区没有大小限制,从而确保没有数据丢失。

另外您也可以在 setReadBufferSize() 和 read() 函数介绍中找到相关信息。


[override virtual protected] qint64 QAbstractSocket::readData(char *data, qint64 maxSize)

该函数重写了父类 QIODevice 的类成员函数 QIODevice::readData()


[override virtual protected] qint64 QAbstractSocket::readLineData(char *data, qint64 maxlen)

该函数重写了父类 QIODevice 的类成员函数 QIODevice::readLineData()


[virtual] void QAbstractSocket::resume()

继续套接字数据传输。 仅当将套接字设置为收到暂停的消息后暂停数据传输,并接收到暂停的消息后暂停了数据传输,才能使用此方法。 当前支持的唯一通知是 QSslSocket :: sslErrors() 。 如果套接字未暂停,则调用此方法将导致 未定义的行为undefined behavior )。

该函数最初在Qt5.0版本引入。

另外您也可以在 pauseMode()setPauseMode() 函数介绍中找到相关信息。


[protected] void QAbstractSocket::setLocalAddress(const QHostAddress &address)

将套接字连接中本地的地址设置为 address

套接字连接建立后,您可以在 QAbstractSocket 的子类中调用此函数以更改 localAddress() 函数的返回值。 代理连接通常将此功能用于虚拟连接设置。

请注意,此函数在连接之前不会绑定套接字的本地地址(例如 QAbstractSocket :: bind() )。

该函数最初在Qt4.1版本引入。

另外您也可以在 localAddress()setLocalPort()setPeerAddress() 函数介绍中找到相关知识。


[protected] void QAbstractSocket::setLocalPort(quint16 port)

将套接字连接中本地的端口设置为 port

套接字连接建立后,您可以在 QAbstractSocket 的子类中调用此函数以更改 localPort() 函数的返回值。 代理连接通常将此功能用于虚拟连接设置。

请注意,此函数在连接之前不会绑定套接字的本地端口(例如 QAbstractSocket :: bind() )。

该函数最初在Qt4.1版本引入。

另外您也可以在 localPort()localAddress()setLocalPort()setPeerAddress()


void QAbstractSocket::setPauseMode(QAbstractSocket::PauseModes pauseMode)

设置套接字是否在收到通知后暂停数据传输。 pauseMouse 参数指定套接字暂停的条件。目前唯一支持的暂停套接字数据传输的信号是 QSslSocket::sslErrors() 。如果将套接字暂停模式设置为 PauseOnSslErrors ,在接收到 QSslSocket::sslErrors() 信号后套接字上的数据传输将被暂停,并且需要通过调用 resume() 再次显式启用。默认情况下 QAbstractSocket 暂停模式为 PauseNever 。 若要修改暂停模式,您必须在连接服务器前调用此函数,否则对此函数的调用将被视为未定义行为。

该函数最初在Qt5.0版本引入。

另外您也可以在 pauseMode()resume() 函数介绍中找到相关信息。


[protected] void QAbstractSocket::setPeerAddress(const QHostAddress &address)

设将套接字连接中远程的地址设置为 address

您可以在 QAbstractSocket 的一个子类中去改变 peerAddress() 函数的返回值。 代理连接通常将此功能用于虚拟连接设置。

该函数最早在Qt4.1版本引入。

另外您也可以在 peerAddress()setPeerPort()setLocalAddress() 函数介绍中找到相关信息。


[protected] void QAbstractSocket::setPeerName(const QString &name)

设将套接字连接中远程的主机名设置为 name

套接字连接建立后,您可以在 QAbstractSocket 的子类中调用此函数以更改 peerName() 函数的返回值。 代理连接通常将此功能用于虚拟连接设置。

该函数最早在Qt4.1版本引入。

另外您也可以在 peerName()函数介绍中找到相关信息。


[protected] void QAbstractSocket::setPeerPort(quint16 port)

设将套接字连接中远程的端口设置为 port

套接字连接建立后,您可以在 QAbstractSocket 的子类中调用此函数以更改 peerPort() 函数的返回值。 代理连接通常将此功能用于虚拟连接设置。

该函数最早在Qt4.1版本引入。

另外您也可以在 peerPort() 函数介绍中找到相关信息。


void QAbstractSocket::setProtocolTag(const QString &tag)

将此套接字的协议标签设置为 tag

该函数最早在Qt5.13版本引入。

另外您也可以在 protocolTag() 函数介绍中找到相关信息。


void QAbstractSocket::setProxy(const QNetworkProxy &networkProxy )

将此套接字的显式网络代理设置为 networkProxy

您可以通过指定 networkProxyQNetworkProxy::NoProxy 以禁止套接字使用代理网络:

socket->setProxy(QNetworkProxy::NoProxy);

套接字默认使用的代理为 QNetworkProxy::DefaultProxy 即套接字会使用应用程序的代理设置,将代理设置为 QNetworkProxy::setApplicationProxy 也可以达到同样的效果。如果您使用 QNetworkProxyFactory :: setApplicationProxyFactory 设置了 QNetworkProxy 插件工厂,它将使用 QNetworkProxyQuery :: TcpSocket 类型查询该插件工厂。

该函数最早在Qt4.1版本引入。

另外您也可以在 proxy() 函数介绍以及 QNetworkProxy 和 QNetworkProxyFactory::queryProxy() 文档中找到相关信息。


[virtual] void QAbstractSocket::setReadBufferSize(qint64 size)

设置 QAbstractSocket 内部读入缓冲区的大小(字节为单位)。

如果将该缓冲区的大小设置为一个确定的值,QAbstractSocket 内部读入缓冲区内的值大小超过该值后,便不会再缓冲数据。与此相反,缓冲区大小为0意味着读缓冲区不受限制,所有传入数据都将被缓冲,同时这也是默认值。

该功能在特定的情况下(例如,在实时流应用程序中)读取数据,或者要保护套接字避免接收过多的数据(可能最终导致应用程序用尽内存)很有用。

只有QTcpSocket使用 QAbstractSocket 的内部缓冲区。 QUdpSocket 根本不使用任何缓冲,而是依赖于操作系统提供的隐式缓冲。 因此,在 QUdpSocket 上调用此函数无效。

另外您也可以在 readBufferSize() 和 read() 函数介绍中找到相关信息。


[virtual] bool QAbstractSocket::setSocketDescriptor(qintptr socketDescriptor, QAbstractSocket::SocketState socketState = ConnectedState, QIODevice::OpenMode openMode = ReadWrite)

使用本机套接字描述符 socketDescriptor 初始化 QAbstractSocket 。 如果 socketDescriptor 为有效的套接字描述符,则返回 true ,否则返回 false 。 套接字以 openMode 指定的模式打开,并进入由 socketState 指定的套接字状态。 读取和写入缓冲区将会丢弃所有未决数据并清空。

注意: 无法使用相同的本机套接字描述符初始化两个抽象套接字。

另外您也可以在 socketDescriptor() 函数介绍中找到相关信息。


[proteched] void QAbstractSocket::setSocketError(QAbstractSocket::SocketError socketError)

将套接字最后一个出现的错误的类型设置为 socketError

另外您也可以在 setSocketState() 和 setErrorString() 函数中找到相关信息。


[virtual] void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)

option 指定的套接字选项设置为 value 指定的值。

注意: 当应用程序在 Windows 下运行时, QAbstractSocket::KeepAliveOption 选项的值必须在套接字连接之前指定。

该函数最初在Qt4.6版本引入。

另外您也可以在 socketOption() 函数介绍中找到相关信息。


[protected] void QAbstractSocket::setSocketState(QAbstractSocket::SocketState state)

将套接字的状态设置为 state

另外您也可以在state() 函数介绍中找到相关信息。


[virtual] qintptr QAbstractSocket::socketDescriptor() const

当 QAbstractSocket 类对象的本地套接字描述符可用时,则返回该描述符,否则返回-1。

如果套接字使用了网络代理,则返回的描述符可能无法与本机套接字一起使用。

当 QAbstractSocket 类处于未连接UnconnectedState)状态时,该描述符是不可用的。

另外您也可以在 setSocketDescriptor() 函数介绍中找到相关信息。


[virtual] QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption) option)

返回 option 指定的套接字选项的值。

该函数最早在Qt4.6版本引入。

另外您也可以在 setSocketDescriptor() 函数介绍中找到相关信息。


QAbstractSocket::SocketType QAbstractSocket::socketType() const

返回套接字类型( TCP,UDP,或者其他)。

另外您也可以在 QTcpSocketQUdpSocket 文档中找到相关信息。


QAbstractSocket::SocketState QAbstractSocket::state() const

返回套接字的当前状态。

另外您也可以在 error() 函数介绍中找到相关信息。


[override virtual] bool QAbstractSocket::waitForBytesWritten(int msecs = 30000)

该函数重写了父类 QIODevice 的类成员函数 QIODevice::waitForBytesWritten(int msecs)

该函数将阻塞套接字通信直到至少一个字节被写入到套接字并发送 bytesWritten() 信号为止。 函数将会在 msecs 指定的时间后超时,默认的超时时间设置为30000毫秒。

bytesWritten() 信号发出后该函数返回值为 true,偶则返回 false (如果有一个错误发生或者该操作超时)。

注意: 在 Windows 上,此功能可能会随机性地失败。 如果您的软件将在 Windows 上运行,请考虑使用事件循环和 bytesWritten() 信号。

另外您也可以在 waitForReadyRead() 函数介绍中找到相关信息。


[virtual] bool QAbstractSocket::waitForConnected(int msecs = 30000)

msecs 指定的时间内等待套接字连接到主机。如果连接成功建立,则返回 true ,否则返回 false 。如果该函数返回 false ,您可以调用 error() 函数来确定连接中出现的错误的类型。

这里Qt官方给了一个1秒内等待套接字建立到主机的连接的示例:

socket->connectToHost("imap", 143);
if (socket->waitForConnected(1000))
	qDebug("Connected!");

如果将 msecs 设置为-1,这个函数将不会超时。

注意: 由于套接字完成主机的查询需要一定时间,这个函数超时的时间可能会比 msecs 设定的时间要轻微的长一些。

注意: 多次调用这个函数并不会累积超时时间。如果函数超时,套接字的连接进程就会被中断。

注意: 在 Windows 上,此功能可能会随机性地失败。 如果您的软件将在 Windows 上运行,请考虑使用事件循环和 connected() 信号。

另外您也可以在 connectToHost()connected() 函数中找到相关信息。


[virtual] bool QAbstractSocket::waitForDisconnected(int msecs = 30000)

msecs 指定的时间内等待套接字从主机断开连接。 如果连接成功断开,则返回 true ,否则返回 false (操作超时,出现错误,或者已经断开连接)。 如果该函数返回 false ,您可以调用 error() 函数来确定断开连接时出现的错误的类型。

这里Qt官方给了一个1秒内等待套接字从主机断开连接的示例:

socket->disconnectFromHost();
if (socket->state() == QAbstractSocket::UnconnectedState
	|| socket->waitForDisconnected(1000)) {
		qDebug("Disconnected!");
 }

如果 msecs 设为-1,该函数将不会超时。

注意: 在 Windows 上,此功能可能会随机性地失败。 如果您的软件将在 Windows 上运行,请考虑使用事件循环和 disconnected() 信号。

另外您也可以在 disconnectFromHost() 和 close() 函数介绍中找到相关信息。


[override virtual] bool QAbstractSocket::waitForReadyRead(int msecs = 30000)

该函数重写了父类 QIODevice 的类成员函数 QIODevice::waitForReadyRead(int msecs)

该函数将阻塞套接字通信直到有新的字节可以读取并发送 readyRead() 信号为止。 函数将会在 msecs 指定的时间后超时,默认的超时时间设置为30000毫秒。

readyRead() 信号发出后该函数返回值为 true,偶则返回 false (如果有一个错误发生或者该操作超时)。

注意: 在 Windows 上,此功能可能会随机性地失败。 如果您的软件将在 Windows 上运行,请考虑使用事件循环和 readyRead() 信号。

另外您也可以在 waitForBytesWritten()() 函数介绍中找到相关信息。


[overrude virtual protected] qint64 QAbstractSocket::writeData(const char *data, qint64 size)

该函数重写了父类 QIODevice 的类成员函数 QIODevice::writeData(const char data, qint64 size)。

最佳实践指南

这些文档为使用 Qt 解决特定的技术问题提供指南和最佳实践,可用以指导开发者,如何尽可能使用 Qt 技术创建具有卓越的可用性和软件设计的应用程序。

页面描述
可访问性如何让残障人士能够访问您的应用程序。
Qt Quick 应用程序的可访问性如何让残障人士能够访问您的应用程序。
QML 和 Qt Quick 最佳实践列举了使用 QML 和 Qt Quick 时的最佳实践。
有关 GUI 设计的书籍有关 GUI 设计的一些推荐书籍。
从 Unix 信号处理程序调用 Qt 函数这做不到。但不用灰心,我们依然有办法变通……
坐标系统关于绘制系统使用到的坐标系统的一些信息。
创建自定义 Qt 类型如何使用 Qt 创建和注册新类型。(译者注:即被元对象系统支持的动态类型
创建共享库如何创建共享库。
为 Qt Designer 创建和使用组件如何创建和使用自定义 Widget 插件。
桌面集成与用户桌面环境进行集成。
异常安全Qt 中的异常安全指南。
如何创建 Qt 插件如何创建用于扩展 Qt 应用和功能的插件的指南。
实现原子操作关于如何在新的 CPU 架构上实现原子操作。
QtTest教程使用 Qt Test 进行测试的简短介绍。
还原窗口几何形状如何保存/还原窗口的几何形状。
富文本处理关于 Qt 的富文本处理、编辑和显示的概述。
可伸缩性如何在不同的屏幕设置和界面约定的设备中开发可以良好适配的应用程序。
会话管理如何使用 Qt 管理会话。
设置应用程序图标如何设置应用程序的图标。
标准加速键推荐的加速键(译者注:即快捷键)。
定时器如何在应用程序中使用 Qt 定时器。
使用 Qt D-Bus 适配器如何在 Qt 中创建和使用 DBus 适配器。

容器

引言

Qt 提供了一系列基于模板的通用容器类,可以用于存储指定类型的元素。例如,如果你需要一个可动态调整大小的 QString 数组,那么你可以使用 QVector<QString>。

这些类的设计目标是比 STL 容器更轻量,更安全,更易用。如果你不熟悉 STL,或想更有“ Qt 范”,使用这些类代替 STL 会是更好的选择。

这些类都是隐式共享可重入的,并且针对几个方面做了优化,一是速度,二是较低的内存占用,三是尽可能少的内联代码,减少生成程序的体积。另外,在所有线程都以只读的方式访问容器时,这些类是线程安全的。

要遍历容器中的元素,你可以使用两种风格迭代器:Java 风格迭代器STL 风格迭代器。Java 风格迭代器有更好的易用性和更高级的函数,而 STL 风格迭代器则在效率上会略有优势,并且可以用于 Qt 和 STL 提供的泛型算法中。

Qt 还提供了 foreach 关键字,可以方便地遍历容器。

:从 Qt 5.14 开始,绝大部分容器类已经支持范围构造函数,但需要注意的是 QMultiMap 是一个例外。我们推荐使用该特性代替各种 from/to 方法。例如:

译者注:这里的 from/to 方法指的是 Qt 容器类提供的以 from/to 开头的转换方法,如QVector::toList

QVector<int> vector{1, 2, 3, 4, 4, 5};
QSet<int> set(vector.begin(), vector.end());
/*
    将会生成一个 QSet,包含元素 1, 2, 4, 5。
*/

容器类

Qt 提供了以下几种顺序容器:QListQLinkedListQVectorQStackQQueue。对于大多数的应用,QList 是最适用的。虽然其基于数组实现,但支持在头部和尾部快速插入。如果确实需要一个基于链表的列表,你可以使用 QLinkedList;如果要求元素以连续内存的形式保存,那么可以使用 QVectorQStack QQueue提供了 LIFO 和 FIFO 语义的支持。

Qt也提供了一系列关联容器:QMapQMultiMapQHashQMultiHashQSet。"Multi" 容器可以方便地支持键值一对多的情形。“Hash” 容器提供了快速查找的能力,这是通过使用哈希函数代替对有序集合进行二分查找实现的。

较为特殊的是 QCacheQContiguousCache,这两个类提供了在有限的缓存中快速查找元素的能力。

综述
QList这是目前使用最普遍的容器类,其保存了一个元素类型为T的列表,支持通过索引访问。QList 内部通过数组实现,以确保基于索引的访问足够快。
元素可以通过 QList::append()QList::prepend() 插入到首尾,也可以通过 QList::insert() 插入到列表中间,和其他容器类不同的是,QList 为生成尽可能少的代码做了高度优化。QStringList 继承于 QList<QString>。
QLinkedList)这个类和 QList 很像,不同的是这个类使用迭代器进行而不是整形索引对元素进行访问。和 QList 相比,其在中间插入大型列表时其性能更优,而且其具有更好的迭代器语义。(在 QLinkedList 中,指向一个元素的迭代器只要该元素存在,则会一直保持有效,而在 QList 的迭代器则可能会在任意的元素插入或删除后失效。)
QVector这个类以数组的形式保存给定类型的元素,在内存中元素彼此相邻。在一个 vector 的头部或中部插入可能会相当慢,因为这可能会导致大量元素需要在内存中移动一个位置。
QVarLengthArray<T, Prealloc>这个类提供了一个底层的变长数组,在速度极其重要的情况下可以用来代替 QVector
QStack这个类继承于 QVector,用于为”后进,先出”(LIFO )提供便捷的语义支持。其为 QVector 添加了以下方法:QVector::push()pop()top()
QQueue这个类继承于 QVector,用于为”先进,先出”(FIFO )提供便捷的语义支持。其为 QVector 添加了以下方法:QList::enqueue()dequeue()head()
QSet这个类提供了一个单值数学集合,支持快速查找
QMap<Key, T>这个类提供了一个将类型为Key的键映射到类型为T的值的字典(关联数组)。通常情况下键值是一一对应的。QMap 根据Key进行排序,如果排序无关紧要,使用 QHash 代替速度会更快
QMultiMap<Key, T>这个类继承于 QMap,其为诸如键值一对多的多值映射提供了一个友好的接口
QHash<Key, T>这个类几乎与 QMap 有完全一致的 API ,但查找效率会有明显的提高。QHash 的数据是无序的。
QMultiHash这个类继承于 QMap,其为多值映射提供了一个友好的接口

容器可以嵌套。例如在键为 QString,值为 QList 时,完全可以使用 QMap<QString, QList>。

容器类的定义位于和容器同名的独立头文件中(例如,<QLinkedList>)。为了方便,在 <QtContainerFwd> 中对所有容器类进行了前置声明。

保存在各个容器中的值类型可以是任意可复制数据类型。为了满足这一要求,该类型必须提供一个复制构造函数和一个赋值运算符。某些操作可能还要求类型支持默认构造函数。对于大多数你想要在容器中保存的类型都满足这些要求,包括基本类型,如 intdouble,指针类型,以及 Qt 数据类型,如 QStringQDate QTime,但并不包括 QObject 及其子类(QWidgetQDialogQTimer 等)。如果你尝试实例化一个 QList<QWidget>,编译器将会抱怨道 QWidget 的复制构造函数和赋值运算符被禁用了。如果需要在容器中保存这些类型的元素,可以保存其指针,如 QList<QWidget *>。

下面是一个满足可赋值数据类型要求的自定义数据类型的例子:

class Employee
{
public:
    Employee() {}
    Employee(const Employee &other);

    Employee &operator=(const Employee &other);

private:
    QString myName;
    QDate myDateOfBirth;
};

如果我们没有提供一个复制构造函数或一个赋值运算符,C++ 将会提供一个表现为逐个复制成员的默认实现。在上面的例子中,默认行为就足够了。同样的,如果没有提供默认构造函数,C++ 会提供一个默认构造函数,对成员进行默认构造。尽管没有提供任何的构造函数或赋值运算符,下面的数据类型可以被保存于容器中。

struct Movie
{
    int id;
    QString title;
    QDate releaseDate;
};

一些容器对它们所能保存的数据类型有额外的要求。举个例子,QMap<Key, T> 的键类型 Key 必须提供 operator<() 方法。这些特殊要求在类的详细描述中有说明。在某些情况下,特定函数会有特定的要求,这在函数的描述中有说明。如果条件不满足,编译器将总是会报错。

Qt容器提供了 operator<<()operator>>(),因此这些类可以很方便地通过 QDataStream 进行读写。这意味着存储在容器中的元素类型也必须支持 operator<<()operator>>()。支持这一点是很简单的;以下是我们使上面的 Movie 结构体支持这一点所做的改动:

QDataStream &operator<<(QDataStream &out, const Movie &movie)
{
    out << (quint32)movie.id << movie.title
        << movie.releaseDate;
    return out;
}

QDataStream &operator>>(QDataStream &in, Movie &movie)
{
    quint32 id;
    QDate date;

    in >> id >> movie.title >> date;
    movie.id = (int)id;
    movie.releaseDate = date;
    return in;
}

某些容器类的文档中会提到默认值;举个例子,QVector 会自动使用默认值初始化其元素;QMap::value() 在指定键不存在时会返回一个默认值。对于大部分的值类型,这就是简单地代表通过默认构造函数创建的值(例如对于 QString 即空字符串)。但是对于基本类型,如 intdouble 和指针类型,C++ 语言并没有规定任何的初始化方式,因此在这些情况下,Qt 容器将会自动将其初始化为0。

迭代器类

迭代器提供了一个统一的方式去访问容器中的元素。Qt 容器类提供了两种风格迭代器:Java 风格迭代器和 STL 风格迭代器。两种迭代器均会在容器中的数据被修改或因调用非 const 成员函数导致数据从隐式共享中分离后失效。

Java 风格迭代器

Java 风格迭代器在 Qt4 中引入,作为标准使用在Qt应用中。和 STL 风格迭代器相比,其易用性更高,代价是性能略低。该风格迭代器 API 以 Java 迭代器类为原型设计。

对于每一个容器类,同时提供了两种数据类型的 Java 风格迭代器:一种支持只读访问,另一种支持读写访问。

容器只读迭代器读写迭代器
QList, QQueueQListIteratorQMutableListIterator
QLinkedListQLinkedListIteratorQMutableLinkedListIterator
QVector, QStackQVectorIteratorQMutableVectorIterator
QSetQSetIteratorQMutableSetIterator
QMap<Key, T>, QMultiMap<Key, T>QMapIterator<Key, T>QMutableMapIterator<Key, T>
QHash<Key, T>, QMultiHash<Key, T>QHashIterator<Key, T>QMutableHashIterator<Key, T>

在接下来的讨论中,我们将重点关注 QListQMapQLinkedListQVectorQSet 的迭代器类型和 QList 有完全一样的接口,类似的,QHashQMap 的迭代器类型的接口也是相同的。

和 STL 风格迭代器(下一节介绍)不同,Java 风格迭代器指向的是元素间隙而不是元素本身。因此,Java 风格迭代器可以指向容器最前(在第一个元素之前),也可以指向容器最后(在最后一个元素之后),还可以指向两个元素之间。下图用红色箭头展示了一个四个元素的列表容器合法的迭代器位置。

Java style iterator

这是一个通过循环迭代有序遍历 QList<QString> 中的所有元素并打印到终端的常用写法:

QList<QString> list;
list << "A" << "B" << "C" << "D";

QListIterator<QString> i(list);
while (i.hasNext())
    qDebug() << i.next();

它的工作原理如下:需要被迭代的 QList 对象作为参数传递给 QListIterator 的构造函数。此时迭代器位于列表中第一个元素的前面(位于元素“A“之前)。接着我们调用 hasNext() 检查迭代器之后是否有元素,如果有,我们调用 next() 跳过这个元素。next() 方法会返回其跳过的元素。对于 QList<QString> 类型,元素的类型为 QString

下列代码展示了如何反向迭代一个 QList

QListIterator<QString> i(list);
i.toBack();
while (i.hasPrevious())
    qDebug() << i.previous();

这段代码的逻辑和前向迭代是对称的除了在开始我们调用了 toBack() 将迭代器移动到列表中最后一个元素之后。

下图说明了在迭代器上调用 next()previous() 产生的效果:

list iterator

下表总结了QListIterator的 API:

函数行为
toFront()移动迭代器到列表最前(第一个元素之前)
toBack()移动迭代器到列表最后(最后一个元素之后)
hasNext()如果迭代器不在列表最后则返回 true
next()返回下一个元素并将迭代器前移一位
peekNext()不移动迭代器,仅返回迭代器下一个元素
hasPrevious()如果迭代器不在列表最前则返回 true
previous()返回前一个元素并将迭代器后移一位
peekPrevious()不移动迭代器,仅返回迭代器前一个元素

QListIterator 没有提供任何在迭代列表时插入或删除元素的方法。要实现这一点,你必须使用 QMutableListIterator。这是一个使用 QMutableListIteratorQList 中移除所有奇数的例子:

QMutableListIterator<int> i(list);
while (i.hasNext()) {
    if (i.next() % 2 != 0)
        i.remove();
}

next() 方法会在每次循环时调用,用于跳过下一个元素。remove() 方法用于移除上一个我们跳过的元素。对 remove() 方法的调用不会导致迭代器的失效,因此继续使用迭代器是安全的。这种方式在反向迭代时也是没问题的。

QMutableListIterator<int> i(list);
i.toBack();
while (i.hasPrevious()) {
    if (i.previous() % 2 != 0)
        i.remove();
}

如果仅仅想修改已存在元素的值,我们可以使用 setValue()。下列代码中,我们将所有大于 128 的值替换成 128:

QMutableListIterator<int> i(list);
while (i.hasNext()) {
    if (i.next() > 128)
        i.setValue(128);
}

remove() 一样, setValue() 对我们跳过的最后一个元素进行操作。如果我们向前迭代,这个元素就是迭代器之前的元素;如果我们向后迭代,这个元素就是迭代器之后的元素。

next() 方法返回的是列表中元素的非常量引用,对于简单的操作,我们并不需要调用 setValue()

QMutableListIterator<int> i(list);
while (i.hasNext())
    i.next() *= 2;

正如上面提到的,QLinkedListQVectorQSet 的迭代器的 API 和 QList 的完全一致。接下来我们来看看在某些方面不太一样的 QMapIterator,因为其用于迭代(键,值)对。

QListIterator 一样,QMapIterator 提供了 toFront(), toBack(), hasNext(), next(), peekNext(), hasPrevious(), previous()peekPrevious() 方法。我们可以对 next(), peekNext(), previous()peekPrevious() 返回的对象调用 key()value() 方法来获得键和值。

下列代码用于移除所有首都名以”city“结尾的(首都名,国家名)键值对。

QMap<QString, QString> map;
map.insert("Paris", "France");
map.insert("Guatemala City", "Guatemala");
map.insert("Mexico City", "Mexico");
map.insert("Moscow", "Russia");
...

QMutableMapIterator<QString, QString> i(map);
while (i.hasNext()) {
    if (i.next().key().endsWith("City"))
        i.remove();
}

QMapIterator 也提供了 key()value() 方法用于操作迭代器及迭代器上一个跳过的元素的键和值。举个例子,下列代码用于将 QMap 的内容拷贝到 QHash 中。

QMap<int, QWidget *> map;
QHash<int, QWidget *> hash;

QMapIterator<int, QWidget *> i(map);
while (i.hasNext()) {
    i.next();
    hash.insert(i.key(), i.value());
}

如果想要迭代遍历所有值相同的元素,我们可以使用 findNext()findPrevious()。这里的例子用于移除带有指定值的元素。

QMutableMapIterator<int, QWidget *> i(map);
while (i.findNext(widget))
    i.remove();

STL 风格迭代器

STL 风格迭代器在 Qt 2.0 中被引入,可用于 Qt 和 STL 的泛型算法,且为速度做了优化。

对于每一个容器类,同时提供了两种类型的 STL 风格迭代器:一种支持只读访问,另一种支持读写访问。

容器只读迭代器读写迭代器
QList, QQueueQList::const_iteratorQList::iterator
QLinkedListQLinkedList::const_iteratorQLinkedList::iterator
QVector, QStackQVector::const_iteratorQVector::iterator
QSetQSet::const_iteratorQSet::iterator
QMap<Key, T>, QMultiMap<Key, T>QMap<Key, T>::const_iteratorQMap<Key, T>::iterator
QHash<Key, T>, QMultiHash<Key, T>QHash<Key, T>::const_iteratorQHash<Key, T>::iterator

STL 风格迭代器的 API 以 数组指针为原型设计。例如,++ 运算符将迭代器向前移动至下一个元素,* 运算符返回迭代器指向的元素。实际上,对于 QVectorQStack 这类元素存储在连续内存的容器来说,iterator 类型仅仅是 T * 的别名,const_iteratorconst T * 的一个别名。

在接下来的讨论中,我们将重点关注 QListQMapQLinkedListQVectorQSet 的迭代器类型和QList有完全一样的接口,类似的,QHashQMap 的迭代器类型的接口也是相同的。

这是一个通过循环迭代有序遍历 QList<QString> 中的所有元素并将它们转成小写的常见写法:

QList<QString> list;
list << "A" << "B" << "C" << "D";

QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
    *i = (*i).toLower();

Java 风格迭代器不同,STL 风格迭代器直接指向元素本身。容器的 begin() 方法会返回一个指向容器第一个元素的迭代器,end() 方法返回的迭代器指向一个虚拟的元素,该元素位于容器最后一个元素的下一个位置。end() 标记了一个非法的位置,永远不要对其解引用。其通常被用作循环的结束条件。对于空列表,begin()end() 是相等的,因此我们永远不会执行循环。

下图用红色箭头展示了一个四个元素的列表容器中合法的迭代器位置。

STL Style Iterator

使用 STL 风格迭代器反向遍历可以通过反向迭代器实现

QList<QString> list;
list << "A" << "B" << "C" << "D";

QList<QString>::reverse_iterator i;
for (i = list.rbegin(); i != list.rend(); ++i)
    *i = i->toLower();
}

上面的代码片段中,我们通过一元运算符 * 来获取保存在指定迭代器位置的元素(此处类型为QString),并对其调用了 QString::toLower() 方法。大部分 C++ 编译器也同时支持i->toLower()这种写法,但也有一些不支持。

对于只读访问,你可以使用 const_iterator , constBegin()constEnd()。例如:

QList<QString>::const_iterator i;
for (i = list.constBegin(); i != list.constEnd(); ++i)
    qDebug() << *i;

下表整理了 STL 风格迭代器的 API:

表达式行为
*i返回当前元素
++i向前移动迭代器至下一元素
i += n向前移动迭代器 n 次
--i向后移动迭代器至前一个元素
i -= n向后移动迭代器 n 次
i - j返回迭代器 i 和 j 间隔的元素个数

++-- 运算符既可以作为前缀( ++i--i) ,也可以作为后缀(i++i--)运算符。前缀版本先修改迭代器,然后返回修改后的迭代器的引用。后缀版本在修改迭代器之前先将其复制一份,然后返回副本。在返回值被忽略的表达式中,我们建议使用前缀版本,因为这样会略快一点。

对于非常量迭代器类型,一元运算符 * 的返回值可以作为赋值运算符的左侧。

对于 QMapQHash* 运算符返回一个元素的值,如果你想获取键,可以调用迭代器的 key() 方法。相对应的,迭代器类型也提供了 value() 用于获取值。下面是一个将 QMap 中所有元素打印到终端的例子:

QMap<int, int> map;
...
QMap<int, int>::const_iterator i;
for (i = map.constBegin(); i != map.constEnd(); ++i)
    qDebug() << i.key() << ':' << i.value();

正是因为隐式共享,调用一个返回容器的函数的开销不会很大。Qt API 中包含几十个返回值为 QListQStringList 的函数(例如 QSplitter::sizes() )。如果需要通过 STL 迭代器遍历这些返回值,你应当总是将返回的容器复制一份然后迭代其副本。例如:

// 正确
const QList<int> sizes = splitter->sizes();
QList<int>::const_iterator i;
for (i = sizes.begin(); i != sizes.end(); ++i)
    ...

// 错误
QList<int>::const_iterator i;
for (i = splitter->sizes().begin();
        i != splitter->sizes().end(); ++i)
    ...

如果函数返回的是一个容器的常量或非常量引用,那么是不存在这个问题的。

隐式共享迭代器问题

隐式共享给 STL 风格迭代器带来了另一个后果是:当一个容器的迭代器在使用时你应当避免复制该容器。迭代器指向了一个内部结构,当你复制容器时你需要特别小心的处理迭代器。比如:

QVector<int> a, b;
a.resize(100000); // 创建一个填充0的大数组.

QVector<int>::iterator i = a.begin();
// 迭代器i的错误用法:
b = a;
/*
    现在我们应当小心地使用迭代器`i`,因为 i 指向的是共享的数据。
    如果我们执行 *i = 4 那么我们可能改变共享的实例(两个数组共享)
    这个行为和 STL 容器是不同的。在 Qt 中不要这样做。
*/

a[0] = 5;
/*
    容器 a 现在已经和共享数据脱离,
    即使 i 之前是容器 a 的迭代器,但现在它是作为 b 的迭代器而存在。
    此时 (*i) == 0
*/

b.clear(); // 此时 i 彻底失效

int j = *i; // 未定义行为!
/*
    b 中的数据(即i 指向的)已经被释放,
    在 STL 容器中这是有明确定义的((*i) == 5),
    但对于 QVector 来说这样做很有可能导致崩溃。
*/

译注:STL 容器std::vector在调用clear()方法后内存不会被释放,因此迭代器并不会立即失效

上面的例子仅仅说明了QVector的问题,但实际上所有支持隐式共享的容器都存在该问题。

foreach 关键字

如果仅仅是需要有序迭代容器中的每个元素,你可以使用 Qt 提供的关键字 foreach。这个关键字对 C++ 语言的一个特定于 Qt 的补充,其通过预处理器实现。

使用的语法是:foreach(变量容器) 语句。下面是一个使用 foreach 迭代 QLinkedList<QString> 的例子:

QLinkedList<QString> list;
...
QString str;
foreach (str, list)
    qDebug() << str;

和使用迭代器实现相同功能的代码相比,使用foreach的代码明显简洁很多。

QLinkedList<QString> list;
...
QLinkedListIterator<QString> i(list);
while (i.hasNext())
    qDebug() << i.next();

除了数据类型包含逗号(例如 QPair<int, int>)以外,用于迭代的变量可以在 foreach 语句中被定义:

QLinkedList<QString> list;
...
foreach (const QString &str, list)
    qDebug() << str;

和其他 C++ 循环结构类似,你可以使用大括号将循环体包围,也可以使用 break 跳出循环。

QLinkedList<QString> list;
...
foreach (const QString &str, list) {
    if (str.isEmpty())
        break;
    qDebug() << str;
}

对于 QMapQHashforeach 会自动访问(键,值)对中的值,因此你不需要再调用容器的 values() 方法(这样可能会产生不必要的复制,见后续说明)。如果你想要同时迭代键和值,可以使用迭代器(会更快),或者也可以先获取所有的键,再通过它们获取对应的值:

QMap<QString, int> map;
...
foreach (const QString &str, map.keys())
    qDebug() << str << ':' << map.value(str);

对于一个多值映射:

QMultiMap<QString, int> map;
...
foreach (const QString &str, map.uniqueKeys()) {
    foreach (int i, map.values(str))
        qDebug() << str << ':' << i;
}

当进入一个 foreach 循环,Qt 会自动产生一个容器的副本。如果在迭代过程中修改了容器,并不会影响到这次循环。(如果没有修改容器,副本依然会占用空间,但由于隐式共享的缘故,复制一个容器是非常快的)。

因为 foreach 会产生容器的副本,使用是个变量的非常量引用也是无法修改原容器的,它仅仅会影响副本,这可能不是你想要的。

一个对 Qt 的 foreach 循环的替代方案是 C++ 11 或更新标准中引入的基于范围的 for 循环。然而,基于范围的 for 循环可能强行导致 Qt 容器脱离,但 foreach 不会。由于使用 foreach 总是会复制一份容器,对 STL 容器来说这通常会导致一定的开销。如果不知道用哪个,建议对 Qt 容器选择 foreach,而对 STL 容器选择基于范围的 for 循环。

除了 foreach,Qt 还提供了一个 forever 伪关键字用于执行无限循环。

forever {
    ...
}

如果你担心命名空间污染,你可以在 .pro 文件中添加以下代码以禁用这些宏:

CONFIG += no_keywords

其他类似容器类

Qt 包含了在某些方面和容器相似的其他模板类。这些类没有提供迭代器,也无法在foreach关键字中使用。

  • QCache<Key, T> 提供了一个缓存,用于保存与键类型Key相关联的类型T的对象。
  • QContiguousCache 提供了一个高效的方式用于缓存那些通常以连续的方式访问的数据。
  • QPair<T1, T2> 保存了一对元素。

其他基于 Qt 的模板容器实现的非模板类型有 QBitArray, QByteArray, QStringQStringList

算法复杂度

算法复杂度关注的是当容器中元素数量增加时,每个函数有多快(或者说多慢)。例如,无论 QLinkedList 中的元素数量有多少,在其中间插入一个元素是一个极快的操作,然而在一个具有非常多的元素的 QVector 中间插入一个元素可能会非常慢,因为半数的元素需要在内存中移动一个位置。

为了描述算法复杂度,我们使用以下基于“大O”符号的术语:

  • 常量时间:O(1)。函数有常量时间复杂度表示无论容器中有多少元素,执行该函数的时间都是相等的。一个例子是 QLinkedList::insert());
  • 对数时间:O(log n)。函数有对数时间复杂度表示该函数的运行时间和容器中元素个数的对数成正比。一个例子是二分查找算法。
  • 线性时间:O(n)。函数有线性时间复杂度表示该函数的运行时间和容器中元素的个数成正比。一个例子是 QVector::insert()
  • 线性对数时间:O(n log n)。线性对数时间复杂度的函数要比线性复杂度的函数慢,且随着数量的增加差距会越来越大,但依然比平方时间的函数快。
  • 平方时间:O(n²)。函数有平方时间复杂度表示该函数的运行时间和容器中元素的个数的平方成正比。

下表总结了 Qt 线性容器类的算法复杂度。

线性查找插入头部追加尾部追加
QLinkedListO(n)O(1)O(1)O(1)
QListO(1)O(n)Amort. O(1)Amort. O(1)
QVectorO(1)Amort. O(n)O(n)Amort. O(1)

表中的 “Amort. ” 表示“均摊行为”。例如,“Amort. O(1)” 表示如果仅调用一次该方法,你可能耗费 O(n) 的时间,但如果多次调用(假设调用 n 次),平均下来复杂度将会是 O(1)。

下表总结了 Qt 关联容器的算法复杂度。

查找键插入
平均最坏情况平均最坏情况
QMap<Key, T>O(log n)O(log n)O(log n)O(log n)
QMultiMap<Key, T>O(log n)O(log n)O(log n)O(log n)
QHash<Key, T>Amort. O(1)O(n)Amort. O(1)O(n)
QSetAmort. O(1)O(n)Amort. O(1)O(n)

对于 QVectorQHashQSet,在尾部追加元素的时间复杂度均摊下来是 O(log n)。但如果在插入元素之前以计划插入的元素个数 n 为参数调用 QVector::reserve(), QHash::reserve()QSet::reserve(),复杂度将会降低至 O(1) 。下一节将会针对该问题做更深入的讨论。

增长策略

QVectorQStringQByteArray 使用连续内存保存元素;QList 维护了一个保存所有元素的指针(除非 T 本身是一个指针类型或一个指针大小的基本类型,这种情况下数组中保存的的是元素本身的值)的数组,用于提供基于索引的快速访问;QHash<Key, T> 内部有一个和大小元素个数成正比的哈希表。为了避免每次在尾部添加元素时重新分配内存,这些类通常会分配比实际需要要多的内存。

考虑下列代码,它用另一个 QString 构建出一个新的 QString

QString onlyLetters(const QString &in)
{
    QString out;
    for (int j = 0; j < in.size(); ++j) {
        if (in[j].isLetter())
            out += in[j];
    }
    return out;
}

我们通过每次追加一个字符的方式动态的构建了一个字符串。假设我们追加 15000 个字符到这个 QString 字符串,接下来会在已分配内存耗尽时触发 QString 进行 18 次内存重新分配,分别在已分配量为:4,8,12,16,20,52,116,244,500,1012,2036,4048,6132,8180,10228,12276,14324,16372时。最后,这个 QString 占用了 16372 个 Unicode 字符的空间,其中 15000 个被实际使用了。

上面的数字可能有些奇怪,这里是分配的指导原则。

  • QString 每次分配 4 个字符指导达到 20 个字符。
  • 从 20 到 4084 个字符,每次将当前的大小翻倍。更准确的说,是翻倍后再减去 12。(一些内存分配器在分配刚好两倍的内存时性能较差,因为它们需要使用每块内存块中的一些字节用于登记。
  • 从 4084 开始,每次分配将会在原来的基础上增加 2048 个字符大小(4096 字节)。这么做的意义是现代操作系统在重新分配内存时并不会复制全部的数据,而仅仅是简单地将物理内存页重新排序,只有第一页和最后一页的数据需要复制。

QByteArrayQList 使用和 QString 差不多的增长算法。

当数据类型支持通过 memcpy() 在内存中进行移动时(包括基本 C++ 类型,指针类型和 Qt 的隐式共享类),QVector 也使用相同的算法。但在数据类型只支持通过复制构造函数和析构函数进行移动时则使用不同的算法。在这种情况下,重新分配的开销会大的多,因此 QVector 将总是会在空间耗尽时直接申请两倍的内存,以减少重新分配的次数。

QHash<Key, T> 则是完全不同。QHash 内部的哈希表每次增长两倍,每次增长时,保存的元素将会重新分配到一个通过 qHash(key) % QHash::capacity()(簇序号)计算出来的新的簇中。此说明同样适用于 QSetQCache<Key, T>。

对于大部分的应用,Qt 提供的增长策略完全可以满足需要。如果你需要进行自定义调整,QVector, QHash<Key, T>, QSet, QStringQByteArray 提供了一个提供了三个方法用于检查和指定保存元素使用的内存量。

  • capacity() 用于返回已分配内存的元素个数(对于QHashQSet,返回的是哈希表的簇的个数)。
  • reserve(size) 用于显式的预分配 size 个元素大小的内存。
  • squeeze() 用于释放所有没有用来保存元素的内存。

如果预先知道容器要保存元素的大概数量,可以提前调用 reserve() 方法分配内存,最后在填充完容器后,再调用 squeeze() 来释放多余的预分配内存。

Reserved by leytou until 2020-07-31

Reserved by leytou until 2020-07-31

[TOC]

QDate Class

属性方法
头文件#include <QDate>
qmakeQT += core

注意: 该类提供的所有函数都是可重入的。

公共成员类型

类型名称
enumMonthNameType{ DateFormat, StandaloneFormat }

公共成员函数

类型函数名
QDate(int y, int m, int d)
QDate()
QDateaddDays(qint64 ndays) const
QDateaddMonths(int nmonths, QCalendar cal) const
QDateaddMonths(int nmonths) const
QDateaddYears(int nyears, QCalendar cal) const
QDateaddYears(int nyears) const
intday(QCalendar cal) const
intday() const
intdayOfWeek(QCalendar cal) const
intdayOfWeek() const
intdayOfYear(QCalendar cal) const
intdayOfYear() const
intdaysInMonth(QCalendar cal) const
intdaysInMonth() const
intdaysInYear(QCalendar cal) const
intdaysInYear() const
qint64daysTo(const QDate &d) const
QDateTimeendOfDay(Qt::TimeSpec spec = Qt::LocalTime, int offsetSeconds = 0) const
QDateTimeendOfDay(const QTimeZone &zone) const
voidgetDate(int *year, int *month, int *day) const
boolisNull() const
boolisValid() const
intmonth(QCalendar cal) const
intmonth() const
boolsetDate(int year, int month, int day)
boolsetDate(int year, int month, int day, QCalendar cal)
QDateTimestartOfDay(Qt::TimeSpec spec = Qt::LocalTime, int offsetSeconds = 0) const
QDateTimestartOfDay(const QTimeZone &zone) const
qint64toJulianDay() const
QStringtoString(Qt::DateFormat format = Qt::TextDate) const
QStringtoString(const QString &format) const
QStringtoString(const QString &format, QCalendar cal) const
QStringtoString(QStringView format) const
QStringtoString(QStringView format, QCalendar cal) const
intweekNumber(int *yearNumber = nullptr) const
intyear(QCalendar cal) const
intyear() const
booloperator!=(const QDate &d) const
booloperator<(const QDate &d) const
booloperator<=(const QDate &d) const
booloperator==(const QDate &d) const
booloperator>(const QDate &d) const
booloperator>=(const QDate &d) const

静态公共成员

类型函数名
QDatecurrentDate()
QDatefromJulianDay(qint64 jd)
QDatefromString(const QString &string, Qt::DateFormat format = Qt::TextDate)
QDatefromString(const QString &string, const QString &format)
QDatefromString(const QString &string, const QString &format, QCalendar cal)
boolisLeapYear(int year)
boolisValid(int year, int month, int day)

相关非成员函数

类型函数名
QDataStream &operator<<(QDataStream &out, const QDate &date)
QDataStream &operator>>(QDataStream &in, QDate &date)

详细描述

无论系统的日历和地域设置如何,一个QDate对象代表特定的一天。它可以告诉您某一天的年、月、日,其对应着格里高利历或者您提供的QCalendar

一个QDate对象一般由显式给定的年月日创建。注意QDate将1~99的年数保持,不做任何偏移。静态函数currentDate()会创建一个从系统时间读取的QDate对象。显式的日期设定也可以使用 setDate()。fromString() 函数返回一个由日期字符串和日期格式确定的日期。

year()、month()、day() 函数可以访问年月日。另外,还有 dayOfWeek()dayOfYear() 返回一周或一年中的天数。文字形式的信息可以通过 toString()获得。天数和月数可以由 QLocale 类映射成文字。

QDate 提供比较两个对象的全套操作,小于意味着日期靠前。

您可以通过 addDays() 给日期增减几天,同样的还有 addMonths()、addYears() 增减几个月、几年。daysTo() 函数返回两个日期相距的天数。

daysInMonth() 和 daysInYear() 函数返回一个月、一年里有几天。isLeapYear() 用于判断闰年。

特别注意

  • 年数没有0 第0年的日期是非法的。公元后是正年份,公元前是负年份。QDate(1, 1, 1) 的前一天是 QDate(-1, 12, 31)

  • 合法的日期范围 日期内部存储为儒略日天号,使用连续的整数记录天数,公元前4714年11月24日是格里高利历第0天(儒略历的公元前4713年1月1日)。除了可以准确有效地表示绝对日期,此类也可以用来做不同日历系统的转换。儒略历天数可以通过 QDate::toJulianDay() 和 QDate::fromJulianDay()读写。 由于技术原因,储存的儒略历天号在 -784350574879~784354017364 之间,大概是公元前2亿年到公元后2亿年之间。

另请参阅:QTimeQDateTimeQCalendarQDateTime::YearRangeQDateEditQDateTimeEditQCalendarWidget

成员类型文档

enum QDate::MonthNameType

此枚举描述字符串中月份名称的表示类型

常量数值描述
QDate::DateFormat0用于日期到字符串格式化。
QDate::StandaloneFormat1用于枚举月份和一周七天。通常单独的名字用首字母大写的单数形式书写。

此枚举在Qt 4.5中引入或修改。

成员函数文档

QString QDate::toString(QStringView format) const

QString QDate::toString(QStringView format, QCalendar cal) const

QString QDate::toString(const QString &format) const

QString QDate::toString(const QString &format, QCalendar cal) const

返回字符串形式的日期。 参数format控制输出的格式。如果传入参数cal, 它决定了使用的日历系统,默认是格里高利历。

日期格式的表示如下:

占位符输出格式
d无占位0的日期 (1 to 31)
dd有占位0的日期 (01 to 31)
ddd简写的一周七天名称(例如 'Mon' to 'Sun')。使用系统地域设置来格式化, 也就是 QLocale::system()
dddd长版的一周七天名称 (例如 'Monday' to 'Sunday')。使用系统地域设置来格式化, 也就是 QLocale::system()
M无占位0的月份 (1 to 12)
MM有占位0的月份 (01 to 12)
MMM缩写的月份名称 (例如 'Jan' to 'Dec')。使用系统地域设置来格式化, 也就是 QLocale::system()
MMMM长版的月份名称 (例如 'January' to 'December')。使用系统地域设置来格式化, 也就是 QLocale::system()
yy两位数年份 (00 to 99)
yyyy四位数年份。 如果是负数,加上符号是五个字符

单引号包裹的内容将会一字不差地放进输出到字符串中(不带外面的单引号),尽管包含上述格式占位符。连续两个单引号('')会被转义成一个单引号输出。所有其他字符不会被转义,将原封不动输出。

没有分隔符的多个占位符(例如 "ddMM")受支持,但要慎用。因为结果的可读性不好,容易引发歧义(例如难以区分“dM”的输出"212"是清2.12还是21,2)

假设今天是1969年7月20日,下面是一些格式字符串的例子

格式结果
dd.MM.yyyy20.07.1969
ddd MMMM d yySun July 20 69
'The day is' ddddThe day is Sunday

如果日期非法,会返回空字符串。

注意: 如果需要本地化的日期表达, 请使用 QLocale::system().toString(),因为 QDate 将在 Qt 6 使用英文表达(C语言风格)。

另请参阅:fromString()、QDateTime::toString()、QTime::toString() 和 QLocale::toString()。


QDateTime QDate::endOfDay(Qt::TimeSpec spec = Qt::LocalTime, int offsetSeconds = 0) const

QDateTime QDate::endOfDay(const QTimeZone &zone) const

返回这一天的最后一刻的时间。通常来说,是午夜前1ms:然而,如果时区转换让这一天跨过午夜(如夏令时),返回的是真实的最后一刻时间。这种情况只可能在使用时区参数QTimeZone zone或者本地时间参数 Qt::LocalTime spec时发生。

参数 offsetSeconds 会被忽略,除非参数 specQt::OffsetFromUTC,其表示出了时区信息。如果UTC和那个时区间没有过渡,一天的结束是 QTime(23, 59, 59, 999)。

在罕见的日期被整体跳过(只在从东向西跨越国际日期变更线时),返回可能是非法的。将 Qt::TimeZone 作为 spec 参数传递 (而不是一个 QTimeZone) 也会造成非法结果,如果那一时刻超过了 QDateTime 的表示范围。

函数在 Qt 5.14 中引入。

另请参阅:startOfDay()。


QDateTime QDate::startOfDay(Qt::TimeSpec spec = Qt::LocalTime, int offsetSeconds = 0) const

QDateTime QDate::startOfDay(const QTimeZone &zone) const

返回一天的开始时刻。通常来说应该是午夜那一时刻:然而,如果时区转换让这一天跨过午夜(如夏令时),返回的是真实的最早的一刻时间。这种情况只可能在使用时区参数QTimeZone zone或者本地时间参数 Qt::LocalTime spec时发生。

参数 offsetSeconds 会被忽略,除非参数 specQt::OffsetFromUTC,其表示出了时区信息。如果UTC和那个时区间没有过渡,一天的结束是 QTime(0, 0)。

在罕见的日期被整体跳过(只在从东向西跨越国际日期变更线时),返回可能是非法的。

Qt::TimeZone 作为 spec 参数传递 (而不是一个 QTimeZone) 也会造成非法结果,如果那一时刻超过了 QDateTime 的表示范围。

函数在 Qt 5.14 中引入。

另请参阅:endOfDay()。


QDate::QDate(int y, int m, int d)

从年月日构造对象,使用格里高利历。如果日期非法,对象不会修改,且 isValid() 返回false

警告: 1~99年就对应于它本身,不会偏移。第0年是非法的。

另请参阅:isValid() 和 QCalendar::dateFromParts()。


QDate::QDate()

构造一个空的日期,也是不可用的。

另请参阅:isNull() 和 isValid()。


QDate QDate::addDays(qint64 ndays) const

返回一个 ndays 天之后的新日期对象(负数意味着往前减日期)。

当当前日期或新日期是非法日期时,返回非法日期。

另请参阅:addMonths()、addYears() 和 daysTo()。


QDate QDate::addMonths(int nmonths) const

QDate QDate::addMonths(int nmonths, QCalendar cal) const

返回一个 nmonths 月之后的新日期对象(负数意味着往前减日期)。

如果传入 cal 参数,会使用日历系统使用,否则使用格里高利历。

注意: 如果新的日期超出年、月的合理范围,函数讲返回那个月中最接近的日期。

另请参阅:addDays() 和 addYears()。


QDate QDate::addYears(int nyears) const

QDate QDate::addYears(int nyears, QCalendar cal) const

返回一个 nyears 年之后的新日期对象(负数意味着往前减日期)。

如果传入 cal 参数,会使用日历系统使用,否则使用格里高利历。

注意: 如果新的日期超出年、月的合理范围,函数讲返回那个月中最接近的日期。

另请参阅:addDays() 和 addMonths()。


[static] QDate QDate::currentDate()

返回系统时钟所示的今天的日期对象。

另请参阅:QTime::currentTime() 和 QDateTime::currentDateTime()。


int QDate::day() const

int QDate::day(QCalendar cal) const

返回日期是当前月份的第几天。

如果传入 cal 参数,会使用此日历系统,否则使用格里高利历。非法日期则返回0。

一些日历系统中,返回值可能大于7。

另请参阅:year()、month() 和 dayOfWeek()。


int QDate::dayOfWeek() const

int QDate::dayOfWeek(QCalendar cal) const

返回日期是当前周的第几天(1=周一,7=周日)。

如果传入 cal 参数,会使用此日历系统,否则使用格里高利历。非法日期则返回0。

一些日历系统中,返回值可能大于7。

另请参阅:day()、dayOfYear() 和 Qt::DayOfWeek


int QDate::dayOfYear() const

int QDate::dayOfYear(QCalendar cal) const

返回日期是当年的第几天(第一天返回1)。

如果传入 cal 参数,会使用此日历系统,否则使用格里高利历。非法日期则返回0。

另请参阅:day() 和 dayOfWeek()。


int QDate::daysInMonth() const

int QDate::daysInMonth(QCalendar cal) const

返回日期对应的月份中总天数。

如果传入 cal 参数,会使用此日历系统,否则使用格里高利历 (返回值是 28~31)。非法日期则返回0。

另请参阅:day() 和 daysInYear()。


int QDate::daysInYear() const

int QDate::daysInYear(QCalendar cal) const

返回日期对应的一年中的总天数。

如果传入 cal 参数,会使用此日历系统,否则使用格里高利历 (返回值是 365 或 366)。非法日期则返回0。

另请参阅:day() 和 daysInMonth()。


qint64 QDate::daysTo(const QDate &d) const

返回两个日期相差的天数 (d 日期靠前则返回为负)。

如果比较的二者中有非法日期,返回0。

示例:

QDate d1(1995, 5, 17);  // May 17, 1995
QDate d2(1995, 5, 20);  // May 20, 1995
d1.daysTo(d2);          // returns 3
d2.daysTo(d1);          // returns -3

另请参阅:addDays()。


[static]QDate QDate::fromJulianDay(qint64 jd)

将jd表示的儒略日解析为日期并返回。

另请参阅:toJulianDay()。


[static]QDate QDate::fromString(const QString &string, Qt::DateFormat format = Qt::TextDate)

返回 string 表示的 QDate 对象,非法字符串不会被解析。

注意 Qt::TextDate:建议使用英文的简写月份名称(例如 "Jan")。尽管本地化的月份名称在 Qt 5 中也可使用,但它们会依赖于用户的区域设置。

注意: 对本地化的日期的支持,包括 Qt::SystemLocaleDateQt::SystemLocaleShortDateQt::SystemLocaleLongDateQt::LocaleDateQt::DefaultLocaleShortDateQt::DefaultLocaleLongDate,将在 Qt 6 被移除。使用 QLocale::toDate() 代替。

另请参阅:toString() 和 QLocale::toDate()。


[static]QDate QDate::fromString(const QString &string, const QString &format)

[static]QDate QDate::fromString(const QString &string, const QString &format, QCalendar cal)

返回 string 表示的 QDate 对象,非法字符串不会被解析。

如果传入 cal 参数,会使用此日历系统,否则使用格里高利历。下面的格式描述针对于格里高利历,其它日历可能不同。

日期格式的表示如下:

占位符输出格式
d无占位0的日期 (1 to 31)
dd有占位0的日期 (01 to 31)
ddd简写的一周七天名称(例如 'Mon' to 'Sun')。使用系统地域设置来格式化, 也就是 QLocale::system()
dddd长版的一周七天名称 (例如 'Monday' to 'Sunday')。使用系统地域设置来格式化, 也就是 QLocale::system()
M无占位0的月份 (1 to 12)
MM有占位0的月份 (01 to 12)
MMM缩写的月份名称 (例如 'Jan' to 'Dec')。使用系统地域设置来格式化, 也就是 QLocale::system()
MMMM长版的月份名称 (例如 'January' to 'December')。使用系统地域设置来格式化, 也就是 QLocale::system()
yy两位数年份 (00 to 99)
yyyy四位数年份。 如果是负数,加上符号是五个字符

注意: 不行此函数的其他版, 日期和月份名必须使用用户本地语言。只有用户语言是英语时,英文名称才适用。

所有其他字符将会被舍弃,单引号('')包裹的内容,无论是否包含格式占位符也会被舍弃。例如:

QDate date = QDate::fromString("1MM12car2003", "d'MM'MMcaryyyy");
// date is 1 December 2003

如果字符串不符合格式,一个将返回一个非法的 QDate。不需要前面补0的格式是贪婪的,这意味着他们会读取两位数,尽管数字大小超出日期合法范围,并且会占据给后续格式读取的数字位置。例如,下面的日期可以表示1月30日,但M格式会捕获两位数字,导致日期返回非法:

QDate date = QDate::fromString("130", "Md"); // invalid

年月日中,没有出现在格式中的,将会使用如下默认值:

字段默认值
Year1900
Month1
Day1

下面是默认值的示例:

QDate::fromString("1.30", "M.d");           // January 30 1900
QDate::fromString("20000110", "yyyyMMdd");  // January 10, 2000
QDate::fromString("20000110", "yyyyMd");    // January 10, 2000

注意: 如果使用本地化的日期表达, 请使用 QLocale::system().toDate(), 因为 QDate 将在 Qt 6使用英文表达 (C语言风格) 。

另请参阅:toString()、QDateTime::fromString()、QTime::fromString() 和 QLocale::toDate()。


void QDate::getDate(int *year, int *month, int *day) const

读取日期,储存到 *year, *month*day。指针可以是空指针。

如果日期非法,返回的是0。

注意: 在 Qt 5.7 之前, 这个函数不是常函数。

此函数在 Qt 4.5 中引入。

另请参阅:year(),month(),day(),isValid() 和 QCalendar::partsFromDate()。


[static]bool QDate::isLeapYear(int year)

判断是否是格里高利历的闰年,是则返回 true

另请参阅:QCalendar::isLeapYear()。


bool QDate::isNull() const

判断日期是否为空,日期为空则返回 true。 空日期是非法的。

注意: 行为与 isValid() 等价。

另请参阅:isValid()。


bool QDate::isValid() const

判断日期是否合法,合法返回 true

另请参阅:isNull() 和 QCalendar::isDateValid()。


[static]bool QDate::isValid(int year, int month, int day)

是上述方法的重载。

判断日期(以格里高利历解析)是否合法,合法返回 true

例如:

QDate::isValid(2002, 5, 17);  // true
QDate::isValid(2002, 2, 30);  // false (2月没有20日)
QDate::isValid(2004, 2, 29);  // true (是闰年)
QDate::isValid(2000, 2, 29);  // true (是闰年)
QDate::isValid(2006, 2, 29);  // false (不是闰年)
QDate::isValid(2100, 2, 29);  // false (不是闰年)
QDate::isValid(1202, 6, 6);   // true (即使这一年在格里高利历之前)

另请参阅:isNull(),setDate() 和 QCalendar::isDateValid()。


int QDate::month() const

int QDate::month(QCalendar cal) const

返回月份编号,第一个月返回1。

如果传入 cal 参数,会使用日历系统使用,否则使用格里高利历。

对于格里高利历,1月就是咱中文说的阳历一月。

对于非法日期返回0。注意有一些日历中,月份可能多于12个。

另请参阅:year() 和 day()。


bool QDate::setDate(int year, int month, int day)

设置对应的日期,使用的是格里高利历。 如果设置的日期合法,将返回 true,否则日期标记为非法并返回 false

此函数在 Qt 4.2 中引入。

另请参阅:isValid() 和 QCalendar::dateFromParts()。


bool QDate::setDate(int year, int month, int day, QCalendar cal)

设置对应的日期,使用的是cal对应的日历。如果设置的日期合法,将返回 true,否则日期标记为非法并返回 false

函数在 Qt 5.14 中引入。

另请参阅:isValid() 和 QCalendar::dateFromParts()。


qint64 QDate::toJulianDay() const

将日期转换为儒略日。

另请参阅:fromJulianDay()。


QString QDate::toString(Qt::DateFormat format = Qt::TextDate) const

这是一个重载函数,返回日期的字符串。 format 参数决定字符串格式。

如果 formatQt::TextDate,日期使用默认格式。日期月份将使用系统地域设置,也就是 QLocale::system()。一个例子是 "Sat May 20 1995"。

如果 formatQt::ISODate,字符串按照 ISO 8601 格式展开,格式形如 yyyy-MM-dd。例如2002-01-05

format 中的 Qt::SystemLocaleDateQt::SystemLocaleShortDateQt::SystemLocaleLongDate 将在 Qt 6 中删除。这些应当由 QLocale::system().toString(date, QLocale::ShortFormat)QLocale::system().toString(date, QLocale::LongFormat) 替代。

format 中的 Qt::LocaleDateQt::DefaultLocaleShortDateQt::DefaultLocaleLongDate 将在 Qt 6 中删除。这些应当由 QLocale().toString(date, QLocale::ShortFormat)QLocale().toString(date, QLocale::LongFormat) 替代。

如果 formatQt::RFC2822Date, 字符串会转换成 RFC 2822 兼容的格式。示例其一是 "20 May 1995"。

如果日期非法,返回空字符串。

警告: Qt::ISODate 格式只在0~9999年可用。

另请参阅:fromString() 和 QLocale::toString()。


int QDate::weekNumber(int *yearNumber = nullptr) const

返回 ISO 8601 周序号 (1 到 53)。对于非法日期返回0。

如果 yearNumber 不是 nullptr(默认参数), 年号返回值存于 *yearNumber

根据 ISO 8601, 格里高利历中,跨年的周属于天数更多的那年。 由于 ISO 8601 规定一周始于周一,周三在哪一年这周就属于哪一年。 大多数年有52周,但也有53周的年份。

注意: *yearNumber 不总是与 year() 相等。例如, 2000.1.1是1999年第52周, 2002.12.31是2003年第1周。

另请参阅:isValid()。


int QDate::year() const

int QDate::year(QCalendar cal) const

返回整数型的年份。

如果传入参数 cal , 它决定了使用的日历系统,默认是格里高利历。

如果日期不合法,返回0。在一些日历系统中,比第一年早的年份非法。

如果使用包含第0年的日历系统,在返回0时通过 isValid() 判断是否合法。这些日历正常的使用负年份,-1年的下一年是0年,再下一年是1年。

一些日历中,没有0年份但是有负数年份。例如格里高利历,公元前x年就是年份-x。

另请参阅:month(),day(),QCalendar::hasYearZero() 和 QCalendar::isProleptic()。


bool QDate::operator!=(const QDate &d) const

bool QDate::operator<(const QDate &d) const

bool QDate::operator<=(const QDate &d) const

bool QDate::operator==(const QDate &d) const

bool QDate::operator>(const QDate &d) const

bool QDate::operator>=(const QDate &d) const

对于日期A和B,大于意味着日期靠后,小于意味着日期靠前,相等就是同一天。

相关非成员函数

QDataStream &operator<<(QDataStream &out, const QDate &date)

向数据流写入日期

另请参阅:Serializing Qt Data Types


QDataStream &operator>>(QDataStream &in, QDate &date)

从数据流读出日期

另请参阅:Serializing Qt Data Types

QFile Class

QFile 类提供读写文件的接口。

属性方法
Header:#include
qmake:QT += core
Inherits:QFileDevice
Inherited By:QTemporaryFile

注意: 类中所有函数都是 可重入的

公共成员类型

类型方法
typedefDecoderFn

公共成员函数

类型方法
QFile(const QString &name, QObject *parent)
QFile(QObject *parent)
QFile(const QString &name)
QFile()
virtual~QFile()
boolcopy(const QString &newName)
boolexists() const
boollink(const QString &linkName)
boolmoveToTrash()
boolopen(FILE *fh, QIODevice::OpenMode mode, QFileDevice::FileHandleFlags handleFlags = DontCloseHandle)
boolopen(int fd, QIODevice::OpenMode mode, QFileDevice::FileHandleFlags handleFlags = DontCloseHandle)
boolremove()
boolrename(const QString &newName)
voidsetFileName(const QString &name)
QStringsymLinkTarget() const

重写公共函数

类型方法
virtual QStringfileName() const override
virtual boolopen(QIODevice::OpenMode mode) override
virtual QFileDevice::Permissionspermissions() const override
virtual boolresize(qint64 sz) override
virtual boolsetPermissions(QFileDevice::Permissions permissions) override
virtual qint64size() const override

静态公共成员

类型方法
boolcopy(const QString &fileName, const QString &newName)
QStringdecodeName(const QByteArray &localFileName)
QStringdecodeName(const char *localFileName)
QByteArrayencodeName(const QString &fileName)
boolexists(const QString &fileName)
boollink(const QString &fileName, const QString &linkName)
boolmoveToTrash(const QString &fileName, QString *pathInTrash = nullptr)
QFileDevice::Permissionspermissions(const QString &fileName)
boolremove(const QString &fileName)
boolrename(const QString &oldName, const QString &newName)
boolresize(const QString &fileName, qint64 sz)
boolsetPermissions(const QString &fileName, QFileDevice::Permissions permissions)
QStringsymLinkTarget(const QString &fileName)

详细描述

QFile 是用于读写文本及二进制的文件及资源的I/O设备。 一个QFile可以单独使用,或者更简单的,可以与 QTextStreamQDataStream 一同使用。

文件名通常在构造时传递,但也可以在随时使用 setFileName()设置。QFile 需要目录分隔符为 '/' 而不是依照操作系统。其他分隔符 (如 '\') 不受支持。

您可以通过 exists() 判断文件是否存在。(更多操作系统相关的操作在 QFileInfoQDir 中提供)

文件通过 open() 打开,通过 close() 关闭,通过 flush() 刷新。数据通常使用 QDataStream or QTextStream 读写,但您也可以使用 由 QIODevice 的继承函数 read(), readLine(), readAll(), write()。单字符的操作也可以使用 getChar(), putChar(), and ungetChar()。

size() 返回文件大小。您可以通过 pos() 获取当前文件位置,或通过 seek() 移动到新的位置(译者注:此句中的“位置”指文件内操作的字节位置)。当您读到文件结尾, atEnd() 返回 true

直接读文件

如下例子逐行地直接读取文本文件:

    QFile file("in.txt");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    while (!file.atEnd()) {
        QByteArray line = file.readLine();
        process_line(line);
    }

QIODevice::Text flag传递给 open() ,其告诉Qt将Windows风格的换行符 ("\r\n") 转换为 C++风格的换行符("\n")。默认情况下,QFile 假设为二进制模式读取,不做字节转换。

通过流来读文件

如下例子逐行地通过 QTextStream 读取文本文件:

    QFile file("in.txt");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    QTextStream in(&file);
    while (!in.atEnd()) {
        QString line = in.readLine();
        process_line(line);
    }

QTextStream 会特意把8位文件中字节数据转换为QString中16位UTF-16字符。默认情况下,其假设用户使用系统默认编码(例如unix平台上是UTF-8;详情参看 QTextCodec::codecForLocale() )。编码可以通过 QTextStream::setCodec() 改变。

要写入文本,您可以使用左移运算符运算符 operator<<(),在 QTextStream 中,其重载用于讲右侧内容追加的左侧,示例如下:

    QFile file("out.txt");
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
        return;

    QTextStream out(&file);
    out << "The magic number is: " << 49 << "\n";

QDataStream 和上文很相似,详情请见相当应类的文档。

当你使用 QFile, QFileInfo 以及 QDir 来访问系统中文件,你可以使用Unicode文件名。在Unix平台,文件名会转换为8位编码。如果您想使用C++标准API (<cstdio><iostream>) 或平台相关API来访问文件而不是使用 QFile,你可以使用 encodeName() 和 decodeName() 来在Unicode文件名和8位文件名间转换。

在Unix平台,有一些特殊的系统文件 (例如 /proc 下的文件) ,对于这些文件,size() 会返回0,但你依然可以读取更多数据;这些数据在你调用 read() 时即时产生。在这种情况下,您便不能使用 atEnd() 来判断是否已经没有更多数据。(因为 atEnd() 通过文件大小是否到达结尾)。然而您可以通过连续调用 readAll(), read() 或 readLine() 指导没有数据来替代此功能。下面的例子使用 QTextStream 逐行读取/proc/modules

    QFile file("/proc/modules");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    QTextStream in(&file);
    QString line = in.readLine();
    while (!line.isNull()) {
        process_line(line);
        line = in.readLine();
    }

信号

不像其他 QIODevice 的实现,例如 QTcpSocket,QFile 不会发出 aboutToClose(), bytesWritten(), 及 readyRead() 这些信号。这个实现细节意味着 QFile 不适用于读写某些特定类型的文件,比如Unix上的设备文件。

平台相关信息

文件权限和Unix和Windows上的处理并不相同。在Unix平台上,一个非 可写入 的目录,文件无法创建。但对于Windows并不一定如此,例如 'My Documents' (我的文档)目录通常不可写入,但是在其中依然可以创建文件。

Qt对于文件权限的理解有局限,尤其对于 QFile::setPermissions() 有影响。在Windows上,仅当没有任何 Write* flags被设置时,Qt 会设置旧版的只读 flag。Qt不会操作访问过滤表(access control lists , ACLs)这是的此函数在NTFS卷上基本上没什么用。对于VFAT格式的U盘,倒是有可能可用。POSIX 的 ACLs 也不会被修改。

另请参见 QTextStream, QDataStream, QFileInfo, QDir, 以及 The Qt Resource System

成员类型文档

typedef QFile::DecoderFn

这个类型定义了一个如下形式的函数的指针:

QString myDecoderFunc(const QByteArray &localFileName);

另请参见 setDecodingFunction().

成员函数文档

QFile::QFile(const QString &name, QObject *parent)

构造基于给定的父对象 parent 、文件名 name 指定的QFile对象。

QFile::QFile(QObject *parent)

构造基于给定的父对象 parent 的QFile对象。

QFile::QFile(const QString &name)

构造文件名 name 指定的QFile对象。

QFile::QFile()

构造一个QFile对象。

[virtual]QFile::~QFile()

销毁此QFile对象,如需要会自动关闭文件。

bool QFile::copy(const QString &newName)

将当前 fileName() 指定的文件复制为文件名 newName 指定的文件。如果成功,返回 true ;否则返回 false

注意如果 newName 文件名的文件已存在,函数不会覆盖,直接返回 false

源文件会在复制前关闭。

另请参见 setFileName().

[static]bool QFile::copy(const QString &fileName, const QString &newName)

这是一个重载函数。

将文件 fileName 复制为文件名 newName。如果成功,返回 true ;否则返回 false

注意如果 newName 文件名的文件已存在,函数不会覆盖,直接返回 false

另请参见 rename().

[static]QString QFile::decodeName(const QByteArray &localFileName)

QFile::encodeName() 操作恰恰相反。返回 localFileName 的Unicode形式。

另请参见 encodeName().

[static]QString QFile::decodeName(const char *localFileName)

这是一个重载函数。返回 localFileName 的Unicode形式。

详情参见 encodeName() 。

[static]QByteArray QFile::encodeName(const QString &fileName)

基于用户区域设置,将 fileName 转换为本地的8为表示。这对于用户选择的文件名足够使用。硬编码到程序中的文件名应当只使用7位ASCII字符。

另请参见 decodeName().

[static]bool QFile::exists(const QString &fileName)

如果 fileName 对应的文件存在,返回 true 否则返回 false

注意: 如果 fileName 是指向不存在文件的符号链接,返回 false

bool QFile::exists() const

这是一个重载函数。

如果 fileName() 对应的文件存在,返回 true 否则返回 false

另请参见 fileName() and setFileName().

[override virtual]QString QFile::fileName() const

重写函数: QFileDevice::fileName() const.

返回 setFileName() 或构造函数设置的文件名。

另请参见 setFileName() and QFileInfo::fileName().

bool QFile::link(const QString &linkName)

创建一个名为 linkName 的、指向 fileName() 文件的链接。链接的形式取决于底层文件系统(Windows上的快捷方式或Linux下的符号链接symlink)。如果成功,返回 true ;返回 false

此函数不会覆盖文件系统上已经存在的链接;如果已存在,link() 将返回 false 并设置 error()RenameError

注意: 对于Windows平台,一个合法的链接名称必须包含 .lnk 后缀名。

另请参见 setFileName().

[static]bool QFile::link(const QString &fileName, const QString &linkName)

这是一个重载函数。

创建一个名为 linkName 的、指向 fileName 文件的链接。链接的形式取决于底层文件系统(Windows上的快捷方式或Linux下的符号链接symlink)。如果成功,返回 true ;否则返回 false

另请参见 link().

bool QFile::moveToTrash()

fileName() 文件移入回收站。如果成功返回 true ,并将 fileName() 设置为回收站中对应文件的路径;否则返回 false

注意: 在API不能返回回收站中文件的路径的操作系统中,一旦文件被移动 fileName() 会被设置为空字符串。在没有回收站的操作系统,此函数总返回 false

此函数引入自: Qt 5.15.

[static]bool QFile::moveToTrash(const QString &fileName, QString *pathInTrash = nullptr)

这是一个重载函数。

fileName 文件移入回收站。如果成功返回 true ,并将 *pathInTrash 设置为回收站中对应文件的路径字符串的指针;否则返回 false

注意: 在API不能返回回收站中文件的路径的操作系统中,一旦文件被移动 *pathInTrash 会被设置为空字符串。在没有回收站的操作系统,此函数总返回 false

此函数引入自: Qt 5.15.

[override virtual]bool QFile::open(QIODevice::OpenMode mode)

重写函数: QIODevice::open(QIODevice::OpenMode mode)。

使用 OpenMode mode 模式打开文件,如果成功,返回 true ;否则返回 false

模式 mode 必须是 QIODevice::ReadOnly, QIODevice::WriteOnly, 或 QIODevice::ReadWrite。也可以有附加flags,例如 QIODevice::TextQIODevice::Unbuffered

注意:WriteOnlyReadWrite 模式,如果相关文件不存在,此函数会尝试在打开前新建。

另请参见 QIODevice::OpenMode and setFileName().

bool QFile::open(FILE *fh, QIODevice::OpenMode mode, QFileDevice::FileHandleFlags handleFlags = DontCloseHandle)

这是一个重载函数。

使用给出的模式 mode 打开已有的文件句柄 fhhandleFlags 可能会被用于指定附加选项。如果成功,返回 true ;否则返回 false

例如:

#include <stdio.h>

void printError(const char* msg)
{
    QFile file;
    file.open(stderr, QIODevice::WriteOnly);
    file.write(msg, qstrlen(msg));        // 写入 stderr
    file.close();
}

当一个 QFile 通过此函数被被打开,close() 的行为由 AutoCloseHandle flag决定。如果指定了 AutoCloseHandle ,且此函数执行成功,那么 close() 会关闭传入的句柄。否则,close() 不会关闭文件,只会刷新数据(flush)。

警告:

  1. 如果 fh 并非指向常规文件,例如 stdin, stdout, 或 stderr,你可能不能够使用 seek(),且size() 返回0。详见 QIODevice::isSequential()。
  2. 由于使用此函数打开的文件没有指定文件名,你不能通过 QFileInfo 读取相关信息。

Windows平台的注意事项

当访问文件或其他随机存取设备时,fh 必须以二进制模式打开(也就是 fopen() 的模式串必须包含'b')。Qt 会转换行末字节如果您指定 QIODevice::Textmode。顺序读取设备,例如标准输入输出,不受此限制影响。

您需要启用控制台应用支持,才能在控制台使用标准输入输出。要启用,可以在项目文件中加入:

CONFIG += console

另请参见 close().

bool QFile::open(int fd, QIODevice::OpenMode mode, QFileDevice::FileHandleFlags handleFlags = DontCloseHandle)

这是一个重载函数。

使用给出的模式 mode 打开已有的文件描述符 fhhandleFlags 可能会被用于指定附加选项。如果成功,返回 true ;否则返回 false

当一个 QFile 通过此函数被被打开,close() 的行为由 AutoCloseHandle flag决定。如果指定了 AutoCloseHandle ,且此函数执行成功,那么 close() 会关闭传入的句柄。否则,close() 不会关闭文件,只会刷新数据(flush)。

通过此函数打开的文件会被自动设置为 raw 模式;这意味着文件I/O函数会很慢。如果您遇到了性能问题,可以尝试其他 open() 函数。

警告:

  1. 如果 fd 不是一个常规文件,例如 0 (stdin), 1 (stdout), 或 2 (stderr),你可能不能够使用 seek(),且size() 返回0。详见 QIODevice::isSequential()。

  2. 由于使用此函数打开的文件没有指定文件名,你不能通过 QFileInfo 读取相关信息。

另请参见 close().

[override virtual]QFileDevice::Permissions QFile::permissions() const

重写函数: QFileDevice::permissions() const.

另请参见 setPermissions().

[static]QFileDevice::Permissions QFile::permissions(const QString &fileName)

这是一个重载函数。

返回 fileName 文件经 OR(位或)后的权限 QFile::Permission 组合。

bool QFile::remove()

删除文件名 fileName() 的文件。

如果成功,返回 true ;否则返回 false

文件会在删除前关闭。

另请参见 setFileName().

[static]bool QFile::remove(const QString &fileName)

这是一个重载函数。

删除文件名 fileName 的文件。

如果成功,返回 true ;否则返回 false

另请参见 remove().

bool QFile::rename(const QString &newName)

把文件 fileName() 重命名为 newName。如果成功,返回 true ;否则返回 false

注意如果 newName 文件名的文件已存在,函数不会覆盖,直接返回 false

文件在重命名前关闭。

如果直接重命名失败,Qt会尝试拷贝数据到 newName 新文件并删除旧文件来实现重命名。如果拷贝或删除失败,Qt会撤回新文件创建,返回原先状态。

另请参见 setFileName().

[static]bool QFile::rename(const QString &oldName, const QString &newName)

这是一个重载函数。

把文件 oldName 重命名为 newName。如果成功,返回 true ;否则返回 false

注意如果 newName 文件名的文件已存在,函数不会覆盖,直接返回 false

另请参见 rename().

[override virtual]bool QFile::resize(qint64 sz)

重写函数: QFileDevice::resize(qint64 sz).

[static]bool QFile::resize(const QString &fileName, qint64 sz)

这是一个重载函数。

这是文件名 fileName 文件的大小为 sz 字节。如果修改大小成功返回 true,否则返回 false。如果 sz 比当前文件大小大,后面的数据会填充0;如果 sz 比当前文件大小小,会裁剪文件数据。

警告: 如果文件不存在,调用会失败。

另请参见 resize().

void QFile::setFileName(const QString &name)

设置文件名 name。名称可以不包含目录,包含相对目录或绝对目录。

请不要在文件已经打开后调用此函数。

如果文件名不包含路径,或者是相对路径,路径会基于应用程序调用 open() 时的当前路径。

例如:

QFile file;
QDir::setCurrent("/tmp");
file.setFileName("readme.txt");
QDir::setCurrent("/home");
file.open(QIODevice::ReadOnly);      // 打开Unix下文件 "/home/readme.txt"

注意Qt中目录分隔符统一使用"/".

另请参见 fileName(), QFileInfo, and QDir.

[override virtual]bool QFile::setPermissions(QFileDevice::Permissions permissions)

重写函数: QFileDevice::setPermissions(QFileDevice::Permissions permissions).

为文件设置 permissions 权限。如果成功返回 true ,如果权限不能修改返回 false

警告: 此函数不会操作修改 ACLs,这会限制函数功能。

另请参见 permissions() and setFileName().

[static]bool QFile::setPermissions(const QString &fileName, QFileDevice::Permissions permissions)

这是一个重载函数。

为文件名 fileName 的文件设置 permissions 权限。

[override virtual]qint64 QFile::size() const

重写函数: QFileDevice::size() const.

[static]QString QFile::symLinkTarget(const QString &fileName)

返回符号链接(Unix上的symlink或Windows上快捷方式)fileName 指向的文件或目录的绝对路径。如果 fileName 不是一个符号链接,返回空字符串。

名称可能并不是一个存在的文件,只是一个字符串路径。QFile::exists() 可以用来判断是否存在。

此函数引入自: Qt 4.2.

QString QFile::symLinkTarget() const

这是一个重载函数。

返回QFile对象对应的符号链接(Unix上的symlink或Windows上快捷方式)指向的文件或目录的绝对路径。如果 fileName 不是一个符号链接,返回空字符串。

名称可能并不是一个存在的文件,只是一个字符串路径。QFile::exists() 可以用来判断是否存在。

此函数引入自: Qt 4.2.

另请参见 fileName() and setFileName().

Reserved by ZgblKylin until 2020-07-31

[TOC]

QIODevice Class

Reserved by Skykey until 2020-07-31

Reserved by leytou until 2020-07-31

QLibrary Class

Qlibrary用于运行时加载库。

属性内容
头文件:#include <QLibrary>
qmake:QT += core
继承于:QObject

注意: 此类中全部函数可重入。

公共成员类型

类型名称
enumLoadHint { ResolveAllSymbolsHint, ExportExternalSymbolsHint, LoadArchiveMemberHint, PreventUnloadHint, DeepBindHint }
flagsLoadHints

属性

公共成员函数

类型函数名
QLibrary(const QString &fileName, const QString &version, QObject *parent = nullptr)
QLibrary(const QString &fileName, int verNum, QObject *parent = nullptr)
QLibrary(const QString &fileName, QObject *parent = nullptr)
QLibrary(QObject *parent = nullptr)
virtual~QLibrary()
QStringerrorString() const
QStringfileName() const
boolisLoaded() const
boolload()
QLibrary::LoadHintsloadHints() const
QFunctionPointer[resolve](-> #qfunctionpointer-qlibraryresolveconst-char-symbol)(const char *symbol)
voidsetFileName(const QString &fileName)
voidsetFileNameAndVersion(const QString &fileName, int versionNumber)
voidsetFileNameAndVersion(const QString &fileName, const QString &version)
voidsetLoadHints(QLibrary::LoadHints hints)
boolunload()

静态公共成员

类型函数名
boolisLibrary(const QString &fileName)
QFunctionPointerresolve(const QString &fileName, const char *symbol)
QFunctionPointerresolve(const QString &fileName, int verNum, const char *symbol)
QFunctionPointerresolve(const QString &fileName, const QString &version, const char *symbol)

详细描述

QLibrary的实例用于操作一个动态链接库文件(文中称为库,也就是DLL)。QLibrary提供访问库中函数的一种平台无关方式。您可以在构造时传递库文件名,也可以通过 setFileName() 给对象显式设置。加载库时,QLibrary在所有系统指定的位置搜索 (例如: Unix上的 LD_LIBRARY_PATH), 除非文件名是绝对路径。

如果文件路径是绝对路径,则会首先尝试在这个位置加载。如果找不到,QLibrary尝试不同系统相关的前后缀的文件名,比如Unix系的前缀“lib”,后缀“.so”,Mac及IOS的后缀".dylib",Windows的后缀".dll"。

如果文件路径不是绝对路径,Qlibrary改变搜索顺序,首先尝试系统特定的前后缀,之后是特定文件路径。

这让使用除去前后缀的库基本名称来指定库文件变得可能。因此代码可以在不同操作系统里执行,但不用太多代码尝试各种文件名称。

最重要的函数是 load() 用于动态加载库,isLoaded() 用于检查是否加载成功,以及 resolve() 来解析库中的符号。如果库还没加载,resolve() 函数隐式地加载这个库。多个QLibrary实例访问同一个物理库文件是可行的。一旦被加载,库在内存中一直保留到程序结束。您可以通过 unload() 尝试卸载一个库,但如果有其他QLibrary实例在使用同一个库文件,调用会失败。只有在每一个实例都调用过 unload() 后,库才会真正卸载。

Qlibrary 的一种典型用法是解析库中的导出符号,并调用其对应的C语言函数。这叫做显式链接,对应于隐式链接。隐式链接是构建中的链接可执行文件和静态库的步骤。

下面的代码片段加载了个库,解析"mysymbol"符号,并在一切就绪的情况下调用这个函数。如果出现了问题, 例如库文件不存在或者符号未定义,函数指针将会是nullptr,且不会调用。

QLibrary myLib("mylib");
typedef void (*MyPrototype)();
MyPrototype myFunction = (MyPrototype) myLib.resolve("mysymbol");
if (myFunction)
    myFunction();

符号必须作为C函数导出,resolve()才能工作。这意味着用C++编译器编译的函数必须由extern "C"块包裹。在Windows上,还要求导出函数要使用dllexport宏;实现详情见 resolve()。方便起见,resolve() 函数有静态形式,您可以在不现实加载库的情况下使用:

typedef void (*MyPrototype)();
MyPrototype myFunction =
        (MyPrototype) QLibrary::resolve("mylib", "mysymbol");
if (myFunction)
    myFunction();

另请参阅: QPluginLoader.

成员类型介绍

enum QLibrary::LoadHint flags QLibrary::LoadHints

这个枚举描述了可能的可以用来改变库的加载行为的指示。这些取值指示在库加载后如何解析符号,通过 setLoadHints() 指定。

常量描述
QLibrary::ResolveAllSymbolsHint0x01在加载库的时候解析符号,而不是简单等到 resolve() 调用。
QLibrary::ExportExternalSymbolsHint0x02导出库中未解析的符号和外部符号,这些符号可以在后续动态加载的库中解析。
QLibrary::LoadArchiveMemberHint0x04运行库的文件名指定压缩包中的特定对象。如果设置了这个指示,文件名包含一个路径,其指向归档文件,接着是其中的成员名称。
QLibrary::PreventUnloadHint0x08阻止库从地址空间通过close()卸载。如果之后再有open()调用,库中的静态变量不会重新初始化。
QLibrary::DeepBindHint0x10Instructs the linker to prefer definitions in the loaded library over exported definitions in the loading application when resolving external symbols in the loaded library. This option is only supported on Linux.
命令链接器在解析加载过的库中的外部符号时,优先使用加载了的库中的定义,而不是在应用程序加载中的定义。【译者注:翻译存疑,故保留原文参考,详情参考globc--dlopen()--RTLD_DEEPBIND】

LoadHints是一个 QFlags<LoadHint> 类型的typedef。 它储存了LoadHint取值的OR(位或)方式的组合。

另请参阅: loadHints.

属性文档

fileName : QString

这个属性容纳库的文件名。

我们建议忽略库的后缀名,因为Qlibrary会自动寻找带有合适后缀名的文件。 (参见 isLibrary())

当加载库时,在所有系统指定的位置搜索 (例如: Unix上的 LD_LIBRARY_PATH),除非文件名是绝对路径。加载成功后,fileName() 返回返回库文件的全名。如果在构造对象或setFileName() 中包含路径,讲返回文件的全路径。

例如在Unix平台成功加载"GL"库后,fileName() 会返回 "libGL.so"。如果传递参数的文件路径是 "/usr/lib/libGL", fileName() 会返回 "/usr/lib/libGL.so"。

访问函数:

类型函数名
QStringfileName() const
voidsetFileName(const QString &fileName)

loadHints : LoadHints

load() 函数一些关于如何执行的指示。

您可以对于符号如何解析做指示。通常来说,符号不是在加载库时解析的,而是惰性解析的(也就是调用 resolve() 时)。如果您设置loadHints 为ResolveAllSymbolsHint,那么如果平台支持,所有符号会在库加载时一齐解析。

设置 ExportExternalSymbolsHint 会使库中的外部符号在后续库解析中可用。

如果设置了 LoadArchiveMemberHint ,文件名会被分解为两部分:归档文件的路径和归档成员的名称. 例如, fileName libGL.a(shr_64.o) 指向归档文件 libGL.a中的库文件 shr_64.o . 这个特性只在AIX平台生效。

loadHints 的解释是平台相关的,如果您用这些特效,您大概已经对编译的系统平台做了一些假设。因此请仅在您明白您这些操作的结果的情况下设置这些指示。

默认情况下,此属性没有设置任何flag,所有库文件会惰性加载,并且不会导出共其他动态链接库使用的外部符号。

注意: 在库已经加载后设置这个属性没有效果。 并且 loadHints() 不会体现出这些修改。

注意: 这个属性是所有指向同一个库的 QLibrary 实例共享的。

访问函数:

类型函数名
QLibrary::LoadHintsloadHints() const
voidsetLoadHints(QLibrary::LoadHints hints)

成员函数文档

QLibrary::QLibrary(const QString &fileName, const QString &version, QObject *parent = nullptr)

基于给定的父对象 parent 构造一个库对象。它会加载文件名fileName、完整版本号 version 指定的库文件。如今,版本号在Windows上被忽略。

我们建议在 fileName 中忽略文件名的前后缀,因为QLibrary会基于不同平台自动寻找合适的前后缀。比如Unix系的前缀“lib”,后缀“.so”,Mac及IOS的后缀".dylib",Windows的后缀".dll"。(参见fileName


QLibrary::QLibrary(const QString &fileName, int verNum, QObject *parent = nullptr)

基于给定的父对象 parent 构造一个库对象。它会加载文件名fileName、主版本号 verNum 指定的库文件。如今,版本号在Windows上被忽略。

我们建议在 fileName 中忽略文件名的前后缀,因为QLibrary会基于不同平台自动寻找合适的前后缀。比如Unix系的前缀“lib”,后缀“.so”,Mac及IOS的后缀".dylib",Windows的后缀".dll"。(参见fileName


QLibrary::QLibrary(const QString &fileName, QObject *parent = nullptr)

基于给定的父对象 parent 构造一个库对象。它会加载文件名fileName 指定的库文件。

我们建议在 fileName 中忽略文件名的前后缀,因为QLibrary会基于不同平台自动寻找合适的前后缀。比如Unix系的前缀“lib”,后缀“.so”,Mac及IOS的后缀".dylib",Windows的后缀".dll"。(参见fileName


QLibrary::QLibrary(QObject *parent = nullptr)

基于给定的父对象 parent 构造一个库对象。


[virtual]QLibrary::~QLibrary()

删除此QLibrary对象。

除非显式调用 unload(),库会在一直驻留在内存中,知道应用结束。

另请参阅: isLoaded() 和 unload().


QString QLibrary::errorString() const

返回一个描述上一个发生的错误的文本字符串。截至现在,errorString 只会在 load(), unload() 或 resolve() 调用由于一些原因失败时才会设置。

此函数引入自:Qt 4.2.


[static]bool QLibrary::isLibrary(const QString &fileName)

如果 fileName 包含一个合法的可加载的后缀,返回true;否则返回false。

平台合法后缀
Windows.dll, .DLL
Unix/Linux.so
AIX.a
HP-UX.sl, .so (HP-UXi)
macOS and iOS.dylib, .bundle, .so

Unix平台上的名字后的版本号会被忽略。


bool QLibrary::isLoaded() const

如果库已经被加载,返回true,否则返回false。

另请参阅: load().


bool QLibrary::load()

加载一个库,如果成功加载则返回true;否则返回false。因为 resolve() 内部会自动调用此方法,您没必要显示调用这个函数。如果在某些情况下您想提前加载库,您可以主动调用它。

另请参阅: unload().


QFunctionPointer QLibrary::resolve(const char *symbol)

返回导出符号 symbol 对应的地址。如果需要,库会自动加载。如果库无法加载或符号无法解析,返回 nullptr

例如:

typedef int (*AvgFunction)(int, int);

AvgFunction avg = (AvgFunction) library->resolve("avg");
if (avg)
    return avg(5, 8);
else
    return -1;

符号必须作为C语言函数导出。这意味着如果使用C++编译器,函数必须由 extern "C" 包裹。在Windows平台,您还必须显式通过 __declspec(dllexport) 指导编译器导出符号,例如:

extern "C" MY_EXPORT int avg(int a, int b)
{
    return (a + b) / 2;
}

MY_EXPORT 定义如下

#ifdef Q_OS_WIN
#define MY_EXPORT __declspec(dllexport)
#else
#define MY_EXPORT
#endif

[static]QFunctionPointer QLibrary::resolve(const QString &fileName, const char *symbol)

这是一个重载函数。

加载文件名 fileName 对应的库,并返回 symbol 对应导出符号的地址。注意 fileName 不应该包含平台相关前后缀(详情见 fileName). 库会一直保留到应用程序退出。

如果库无法加载或符号无法解析,返回 nullptr

另请参阅: resolve().


[static]QFunctionPointer QLibrary::resolve(const QString &fileName, int verNum, const char**symbol*)

这是一个重载函数。

加载文件名 fileName 、主版本号 verNum 对应的库,并返回 symbol 对应导出符号的地址。注意 fileName 不应该包含平台相关前后缀(详情见 fileName). 库会一直保留到应用程序退出。version 参数在 Windows 上无效。

如果库无法加载或符号无法解析,返回 nullptr

另请参阅: resolve().


[static]QFunctionPointer QLibrary::resolve(const QString &fileName, const QString &version, const char *symbol)

这是一个重载函数。

加载文件名 fileName 、完整版本号 version 对应的库,并返回 symbol 对应导出符号的地址。注意 fileName 不应该包含平台相关前后缀(详情见 fileName). 库会一直保留到应用程序退出。version 参数在 Windows 上无效。

如果库无法加载或符号无法解析,返回 nullptr

此函数引入自:Qt 4.4.

另请参阅: resolve().

void QLibrary::setFileNameAndVersion(const QString &fileName, int versionNumber)

设置 fileName 属性,以及相对应的文件名和版本号信息。versionNumber 参数在 Windows 上无效。

另请参阅: setFileName().

void QLibrary::setFileNameAndVersion(const QString &fileName, const QString &version)

设置 fileName 属性,以及相对应的文件名和完整版本号信息。version 参数在 Windows 上无效。

此函数引入自:Qt 4.4.

另请参阅: setFileName().

bool QLibrary::unload()

卸载一个库;如果成功返回true,否则false

在应用程序结束是,此函数自动调用,因此您不应该手动调用。

如果有其他 QLibrary 示例在使用同一个库,调用会失败。卸载只会在所有实例都调用过此函数之后发生。

注意:在 Mac OS X 10.3 (Panther),无法卸载动态链接库。

另请参阅: resolve() 和 load().

QList Class

template <typename T> class QList

QList 类是一个用于提供列表支持的模板类。更多...

头文件:#include
qmake:QT += core
子类:QByteArrayList, QItemSelection, QQueueQStringList

注意: 本页面提到的方法都是可重入的

公共成员类型

classconst_iterator
classiterator
typedefConstIterator
typedefIterator
typedefconst_pointer
typedefconst_reference
typedefconst_reverse_iterator
typedefdifference_type
typedefpointer
typedefreference
typedefreverse_iterator
typedefsize_type
typedefvalue_type

公共成员方法

QList(InputIterator first, InputIterator last)
QList(std::initializer_list args)
QList(QList &&other)
QList(const QList &other)
QList()
QList &operator=(QList &&other)
QList &operator=(const QList &other)
~QList()
voidappend(const T &value)
voidappend(const QList &value)
const T &at(int i) const
T &back()
const T &back() const
QList::iteratorbegin()
QList::const_iteratorbegin() const
QList::const_iteratorcbegin() const
QList::const_iteratorcend() const
voidclear()
QList::const_iteratorconstBegin() const
QList::const_iteratorconstEnd() const
const T &constFirst() const
const T &constLast() const
boolcontains(const T &value) const
intcount(const T &value) const
intcount() const
QList::const_reverse_iteratorcrbegin() const
QList::const_reverse_iteratorcrend() const
boolempty() const
QList::iteratorend()
QList::const_iteratorend() const
boolendsWith(const T &value) const
QList::iteratorerase(QList::iterator pos)
QList::iteratorerase(QList::iterator begin, QList::iterator end)
T &first()
const T &first() const
T &front()
const T &front() const
intindexOf(const T &value, int from = 0) const
voidinsert(int i, const T &value)
QList::iteratorinsert(QList::iterator before, const T &value)
boolisEmpty() const
T &last()
const T &last() const
intlastIndexOf(const T &value, int from = -1) const
intlength() const
QListmid(int pos, int length = -1) const
voidmove(int from, int to)
voidpop_back()
voidpop_front()
voidprepend(const T &value)
voidpush_back(const T &value)
voidpush_front(const T &value)
QList::reverse_iteratorrbegin()
QList::const_reverse_iteratorrbegin() const
intremoveAll(const T &value)
voidremoveAt(int i)
voidremoveFirst()
voidremoveLast()
boolremoveOne(const T &value)
QList::reverse_iteratorrend()
QList::const_reverse_iteratorrend() const
voidreplace(int i, const T &value)
voidreserve(int alloc)
intsize() const
boolstartsWith(const T &value) const
voidswap(QList &other)
voidswapItemsAt(int i, int j)
TtakeAt(int i)
TtakeFirst()
TtakeLast()
QSettoSet() const
std::listtoStdList() const
QVectortoVector() const
Tvalue(int i) const
Tvalue(int i, const T &defaultValue) const
booloperator!=(const QList &other) const
QListoperator+(const QList &other) const
QList &operator+=(const QList &other)
QList &operator+=(const T &value)
QList &operator<<(const QList &other)
QList &operator<<(const T &value)
booloperator==(const QList &other) const
T &operator[](int i)
const T &operator[](int i) const

静态公共成员

QListfromSet(const QSet &set)
QListfromStdList(const std::list &list)
QListfromVector(const QVector &vector)

相关非成员函数

uintqHash(const QList &key, uint seed = 0)
booloperator<(const QList &lhs, const QList &rhs)
QDataStream &operator<<(QDataStream &out, const QList &list)
booloperator<=(const QList &lhs, const QList &rhs)
booloperator>(const QList &lhs, const QList &rhs)
booloperator>=(const QList &lhs, const QList &rhs)
QDataStream &operator>>(QDataStream &in, QList &list)

详细描述

QListQt 泛型容器之一,通过列表保存元素,提供了基于索引的快速访问以及基于索引的插入和删除功能。

QList,QLinkedListQVector 提供了类似的接口和功能。 大部分情况下它们之间是可以互相替换的,但可能会带来一些性能问题。这里有一个各自适用场景的总结:

  • QVector 应当是你的默认首选。QVector 的性能通常要优于 QList, 因为 QVector 总是在内存中连续存储其元素,而 QList 则只会在sizeof(T) <= sizeof(void*) 且通过 Q_DECLARE_TYPEINFO 将 T 声明为 Q_MOVABLE_TYPEQ_PRIMITIVE_TYPE 的情况下才会这么做,否则将会在堆上分配其元素的内存。QList 使用利弊分析 一文对此做了解释。
  • 然而,QList 在 Qt API 中总是被用来传递参数和保存返回值,和这些 API 交互时请使用 QList。
  • 如果你需要一个真正的基于链表实现的列表,以保证列表中间插入元素是常量时间复杂度以及基于迭代器而不是索引来访问元素,你可以使用 QLinkedList。

注意: QVectorQVarLengthArray 都提供了对 C 数组内存布局的兼容,但 QList 不保证这一点。这一点在你的应用需要和 C API 交互时可能会非常重要。

注意: QLinkedList 的迭代器和在堆上分配内存的 QList 的引用只要其指向的元素还在容器中,将会一直保持有效。但 QVector 和非在堆上分配内存的的 QList 的迭代器以及引用并不保证这一点。

内部实现中,如果 sizeof(T) <= sizeof(void*) 且通过 Q_DECLARE_TYPEINFO 将 T 声明为 Q_MOVABLE_TYPEQ_PRIMITIVE_TYPE 时,QList 将表现为一个 T 类型的数组。否则,QList 表现为一个 T* 类型的数组,元素实际在堆上分配内存。

基于数组的实现的 QList 支持快速插入和基于索引的访问。prepend() and append() 操作也非常快,因为 QList 在内部数组的头尾均预分配了内存。(详见算法复杂度

注意,如果上面的条件不能满足,每一次追加或插入一个新的元素都需要在堆上分配这个新元素的内存。这会导致在有大量元素的追加和插入时使用 QVector 成为一个更好的选择,因为 QVector 可以在一次性为多个元素在堆上分配内存。

另一个需要注意的是内部数组在列表的整个生命周期内只会不断增大,永远不会缩小。内部数组将会在列表析构时调用的析构函数或列表被赋予另一个列表时调用的赋值运算符函数中被析构。

下方是使用 QList 保存整型数字和使用 QList 保存 QDate 的例子:

QList<int> integerList;
QList<QDate> dateList;

Qt 提供了 QStringList 类,其继承于 QList<QString> ,提供了一些快捷方法,例如 QStringList::join() 和 QStringList::filter()。QString::split() 用于从 QString 创建 QStringList。

QList 以列表的形式保存元素,默认构造函数会创建一个空列表,你可以使用带有初始化列表的构造函数创建出一个带有元素的的列表:

QList<QString> list = { "one", "two", "three" };

QList 提供了这些基础方法用于添加,移动和删除元素:insert(), replace(), removeAt(), move() 和 swap()。另外,它还提供了下列快捷方法:append(), operator<<(), operator+=(), prepend(), removeFirst() 和 removeLast()。

operator<<() 可以方便地添加多个元素到列表中:

list << "four" << "five";

和 C++ 数组一样,QList 索引从 0 开始。要访问在指定位置的元素,你可以使用 operator[]()。对于非常量列表,operator[]() 用于返回一个元素的引用,可以被用在赋值运算符的左侧(译注:即可作为左值):

if (list[0] == "Bob")
    list[0] = "Robert";

由于对于大小大于一个指针或不可移动的元素类型,QList 基于该类型的指针数组实现,因此该操作需要(常量时间复杂度)。对于只读访问,一个可替代的语法是使用 at():

for (int i = 0; i < list.size(); ++i) {
    if (list.at(i) == "Jane")
        cout << "Found Jane at position " << i << Qt::endl;
}

at() 可能会比 operator[]() 快,因为其永远不会导致深拷贝的发生。

从列表中移除一个元素,然后对其做一些处理是一个很常用的操作。QList 提供了 takeAt(), takeFirst() 和 takeLast() 来实现该操作。下面是一个将元素逐个从列表中移除并对该元素调用 delete 的循环实现:

QList<QWidget *> list;
..。
while (!list.isEmpty())
    delete list.takeFirst();

在列表两端插入或删除元素是非常快的(通常是常量时间复杂度),因为QList在内部缓存的两端都预分配了额外的内存空间用于支持列表两端的快速增长。

如果需要在列表中查找所有特定值的元素的索引,可以使用 indexOf() 或 lastIndexOf()。前一个用于从给定的索引位置向列表尾部方向查找,后一个则相反。二者都会在找到时返回匹配元素的索引,未找到时返回 -1。例如:

int i = list.indexOf("Jane");
if (i != -1)
    cout << "Jane 首次出现的位置是 " << i << Qt::endl;

如果你仅仅是想简单地检查特定值是否存在于列表中,可以使用 contains()。如果你想要统计特定值在列表中出现的次数,可以使用 count()。如果你想将所有特定值替换为一个另一个指定值,可以使用 replace()。

QList 中的元素类型必须是 可赋值数据类型。绝大部分常用数据类型都满足这一点,但某些情况编译器可能会报错,例如以值的形式保存 QWidget,可改成保存 QWidget * 来代替。一些方法会有额外的要求,例如,indexOf() 和 lastIndexOf() 要求值类型支持 operator==() 运算符。这些要求在每个函数的文档中有说明。

正如其他的容器类一样,QList 提供了 Java 风格迭代器(QListIteratorQMutableListIterator) 和 STL 风格迭代器 (QList::const_iteratorQList::iterator)。实际使用中,这些迭代器其实很少被使用,因为你可以使用列表索引。QList 的实现使得直接基于索引访问的方式实现和使用迭代器一样快。

QList 并 支持通过其元素的引用来进行插入,头部追加,尾部追加和替换,这样做会导致你的应用崩溃并显示错误信息。

为了使 QList 尽可能高效,其成员函数在使用前并不会对输入进行校验,但 isEmpty() 例外,成员函数通常会假定列表 为空。带有索引值作为参数的的成员函数总是会假定索引值位于合法的范围内。这意味着 QList 成员函数可能会调用失败。如果在编译时定义了 QT_NO_DEBUG,这些错误将不会被检测到。而如果 没有 定义 QT_NO_DEBUG,此类错误将会通过 Q_ASSERT() 或 Q_ASSERT_X() 被检测到并显示对应的错误信息。

为了避免在在列表可能为空时报错,在调用其他成员函数前应先调用 isEmpty() 检查。如果你必须传递一个可能不在有效范围内的索引值,应先检查其是否小于 size() 的返回值且 小于0。

更多成员

如果 T 是 QByteArray 类型,这个类会提供更多可以使用的成员,详见 QByteArrayList

如果 T 是 QString 类型,这个类提供了这些额外的成员函数:filter, join, removeDuplicates, sort

使用 Qt 容器的更多信息

如果想要详细了解 Qt 和 STL 对应容器之间的对比,可阅读 理解 Qt 容器一文。

另请参阅: QListIterator, QMutableListIterator, QLinkedListQVector

成员类型文档

typedef QList::ConstIterator

Qt 风格的 QList::const_iterator 的同义词。

typedef QList::Iterator

Qt 风格的 QList::iterator 的同义词。

typedef QList::const_pointer

const T * 的类型别名,提供了对 STL 的兼容。

typedef QList::const_reference

const T & 的类型别名,提供了对 STL 的兼容。

typedef QList::const_reverse_iterator

QList::const_reverse_iterator 仅仅是 std::reverse_iterator<const_iterator> 的类型别名,用于提供 STL 风格的 QList 常量反向迭代器。

警告: 支持隐式共享的容器的迭代器的行为和 STL 迭代器并不完全一样。当这类容器的迭代器在使用时你应当避免容器的拷贝。更多信息请阅读 隐式共享迭代器问题 一文。

该类型在 Qt 5.6 中引入。

另请参阅 QList::rbegin(), QList::rend(), QList::reverse_iteratorQList::const_iterator

typedef QList::difference_type

ptrdiff_t 的别名,提供了对 STL 的兼容。

typedef QList::pointer

T * 的别名,提供了对 STL 的兼容。

typedef QList::reference

T & 的别名,提供了对 STL 的兼容。

typedef QList::reverse_iterator

QList::reverse_iterator 仅仅是 std::reverse_iterator<iterator> 的类型别名,用于提供 STL 风格的 QList 非常量反向迭代器。

警告: 支持隐式共享的容器的迭代器的行为和 STL 迭代器并不完全一样。当这类容器的迭代器在使用时你应当避免容器的拷贝。更多信息请阅读 隐式共享迭代器问题 一文。

该类型在 Qt 5.6 中引入。

另请参阅 QList::rbegin(), QList::rend(), QList::const_reverse_iteratorQList::iterator

typedef QList::size_type

int 类型的别名,提供了对 STL 的兼容。

typedef QList::value_type

T 类型的别名,提供了对 STL 的兼容。

成员函数文档

template QList::QList(InputIterator first, InputIterator last)

使用迭代器范围 [first, last)指定的内容构造一个 QList。

InputIterator 的值类型必须可转换为 T

该方法在 Qt 5.14 中引入。

QList::QList(std::initializer_list args)

从由 args 指定的 std::initializer_list 构造一个列表。

此构造函数仅在编译器支持 C++11 初始化列表特性时可用。

该方法在 Qt 4.8 中引入。

QList::QList(QList &&other)

移动构造一个 QList 实例,使它和 other 指向同一个对象。

该方法在 Qt 5.2 中引入。

QList::QList(const QList &other)

构造一个 other 的拷贝。

该操作为 常量时间复杂度,因为 QList 是隐式共享的,所以一个函数返回 QList 是非常快的。如果一个共享实例被修改了,其将会被复制一份(写时拷贝),复杂度为线性时间复杂度

另请参阅 operator=()。

QList::QList()

构造一个空列表。

QList &QList::operator=(QList &&other)

移动赋值 other 给该 QList 实例。

该方法在 Qt 5.2 中引入。

QList &QList::operator=(const QList &other)

other 赋值给当前列表,然后返回当前列表的引用。

QList::~QList()

析构列表。列表中的值的引用及所有的迭代器都将失效。

void QList::append(const T &value)

插入 value 到列表尾部。

示例:

QList<QString> list;
list.append("one");
list.append("two");
list.append("three");
// list: ["one", "two", "three"]

该方法等同于 list.insert(size(), value)。

如果该列表是非共享的,那么此操作通常会非常快(均摊下来为 常量时间复杂度),因为QList 在内部缓存的两端都预分配了额外的内存空间用于支持列表两端的快速增长。

另请参阅 operator<<(), prepend() 和 insert()。

void QList::append(const QList &value)

这是个重载函数。

插入另一个列表 value 中的元素到列表尾部。

该方法在 Qt 4.5 中引入。

另请参阅 operator<<() 和 operator+=()。

const T &QList::at(int i) const

返回位于列表索引位置为 i 的元素。i 必须是列表中合法的索引位置 (例如,0 <= i < size())。

该方法非常快,为(常量时间复杂度)。

另请参阅 value() 和 operator[]()。

T &QList::back()

该方法用于提供对 STL 的兼容,等同于 last()。该方法要求列表不能为空, 如果列表可能为空,应先调用 isEmpty() 进行检查。

const T &QList::back() const

这是个重载函数。

QList::iterator QList::begin()

返回一个指向列表第一个元素的 STL 风格迭代器

另请参阅 constBegin() 和 end()。

QList::const_iterator QList::begin() const

这是个重载函数。

QList::const_iterator QList::cbegin() const

返回指向列表中第一个元素的常量 STL 风格迭代器

该方法在 Qt 5.0 中引入。

另请参阅 begin() and cend()。

QList::const_iterator QList::cend() const

返回一个指向位于最后一个元素之后的虚拟元素的常量 STL 风格迭代器

该方法在 Qt 5.0 中引入。

另请参阅 cbegin() and end()。

void QList::clear()

移除列表中所有的元素。

另请参阅 removeAll()。

QList::const_iterator QList::constBegin() const

返回指向列表中第一个元素的常量 STL 风格迭代器

另请参阅 begin() 和 constEnd()。

QList::const_iterator QList::constEnd() const

返回一个指向位于最后一个元素之后的虚拟元素的常量 STL 风格迭代器

另请参阅 constBegin() 和 end()。

const T &QList::constFirst() const

返回一个列表中第一个元素的常量引用,列表必须不为空。如果列表可能为空,应先调用 isEmpty() 进行检查。

该方法在 Qt 5.6 中引入。

另请参阅 constLast(), isEmpty() 和 first()。

const T &QList::constLast() const

返回一个列表中最后一个元素的常量引用,列表必须不为空。如果列表可能为空,应先调用 isEmpty() 进行检查。

该方法在 Qt 5.6 中引入。

另请参阅 constFirst(), isEmpty() 和 last()。

bool QList::contains(const T &value) const

如果列表中包含 value 则返回 true,否则返回false

该方法要求值类型实现了 operator==()

另请参阅 indexOf() 和 count()。

int QList::count(const T &value) const

返回 value 在列表中的出现次数。

该方法要求值类型实现了 operator==()

另请参阅 contains() 和 indexOf()。

int QList::count() const

返回列表中元素的数量。该方法的性能等同于 size()。

QList::const_reverse_iterator QList::crbegin() const

返回指向逆序列表的第一个元素的常量 STL 风格迭代器

该方法在 Qt 5.6 中引入。

另请参阅 begin(), rbegin() 和 rend()。

QList::const_reverse_iterator QList::crend() const

返回指向逆序列表的最后一个元素的下一个元素的常量 STL 风格迭代器

该方法在 Qt 5.6 中引入。

另请参阅 end(), rend() 和 rbegin()。

bool QList::empty() const

该方法用于提供对 STL 的兼容,等同于 isEmpty(),当列表为空时返回 true

QList::iterator QList::end()

返回一个指向位于最后一个元素之后的虚拟元素的常量 STL 风格迭代器

另请参阅 begin() 和 constEnd()。

QList::const_iterator QList::end() const

这是个重载函数。

bool QList::endsWith(const T &value) const

如果列表非空且最后一个元素等于 value 则返回true 否则返回 false

该方法在 Qt 4.5 中引入。

另请参阅 isEmpty() 和 contains()。

QList::iterator QList::erase(QList::iterator pos)

从列表中移除和迭代器 pos 关联的元素,然会返回列表中下一个元素的迭代器 (可能是 end())。

另请参阅 insert() 和 removeAt()。

QList::iterator QList::erase(QList::iterator begin, QList::iterator end)

这是个重载函数。

移除从 begin 到 (但不包括) end 的所有元素,然会返回调用该方法之前 end 所指向元素的迭代器。

T &QList::first()

返回列表中第一个元素的引用,列表必须非空。如果列表可能为空,应先调用 isEmpty() 进行检查。

另请参阅 constFirst(), last() 和 isEmpty()。

const T &QList::first() const

这是个重载函数。

[static] QList QList::fromSet(const QSet &set)

返回一个包含且仅包含 set 中所有的数据的 QList 对象。QList 中元素的顺序是未定义的。

示例:

QSet<int> set;
set << 20 << 30 << 40 << ... << 70;

QList<int> list = QList<int>::fromSet(set);
std::sort(list.begin(), list.end());

注意: 从 Qt 5.14 开始,Qt 泛型容器类支持范围构造函数,建议用来取代这个方法。

另请参阅 fromVector(), toSet() 和 QSet::toList()。

[static] QList QList::fromStdList(const std::list &list)

返回一个包含且仅包含 list 中所有的数据的 QList 对象。QList 中元素的顺序和 list 一致。

示例:

std::list<double> stdlist;
list.push_back(1.2);
list.push_back(0.5);
list.push_back(3.14);

QList<double> list = QList<double>::fromStdList(stdlist);

注意: 从 Qt 5.14 开始,Qt 泛型容器类支持范围构造函数,建议用来取代这个方法。

另请参阅 toStdList() 和 QVector::fromStdVector()。

[static] QList QList::fromVector(const QVector &vector)

返回包含且仅包含 vector 中所有的元素的 QList 对象。

示例:

QVector<double> vect;
vect << 20.0 << 30.0 << 40.0 << 50.0;

QList<double> list = QVector<T>::fromVector(vect);
// list: [20.0, 30.0, 40.0, 50.0]

注意: 从 Qt 5.14 开始,Qt 泛型容器类支持范围构造函数,建议用来取代这个方法。

另请参阅 fromSet(), toVector() 和 QVector::toList()。

T &QList::front()

该方法用于提供对 STL 的兼容,等同于 first()。要求列表不能为空, 如果列表可能为空,应先调用 isEmpty() 进行检查。

const T &QList::front() const

这是个重载函数。

int QList::indexOf(const T &value, int from = 0) const

返回从索引位置 from 开始向列表尾部方向搜索,在列表中 value 第一次出现的索引位置。如果没有找到则返回 -1。

示例:

QList<QString> list;
list << "A" << "B" << "C" << "B" << "A";
list.indexOf("B");          // 返回 1
list.indexOf("B", 1);       // 返回 1
list.indexOf("B", 2);       // 返回 3
list.indexOf("X");          // 返回 -1

该方法要求值类型实现了 operator==()

需要注意的是 QList 和 C 数组类似,索引也是从 0 开始。除了上面提到的值,其他的负索引值不被支持。

另请参阅 lastIndexOf() 和 contains()。

void QList::insert(int i, const T &value)

value 插入到列表的索引位置 i

如果 i == 0,该值将会被追加到列表头部。如果 i == size(),该值将会被追加到列表尾部。

示例:

QList<QString> list;
list << "alpha" << "beta" << "delta";
list.insert(2, "gamma");
// list: ["alpha", "beta", "gamma", "delta"]

另请参阅 append(), prepend(), replace() 和 removeAt()。

QList::iterator QList::insert(QList::iterator before, const T &value)

这是个重载函数。

value 插入到迭代器 before 指向元素的前面,并返回一个指向插入元素的迭代器。需要注意的是传递给该函数的迭代器在调用完成后将会失效,返回的迭代器可以用来代替它。

bool QList::isEmpty() const

如果列表中没有任何元素则返回 true ,否则返回 false

另请参阅 size()。

T &QList::last()

返回列表最后一个元素的引用,列表必须非空。如果列表可能为空,应先调用 isEmpty() 进行检查。

另请参阅 constLast(), first() 和 isEmpty()。

const T &QList::last() const

这是个重载函数。

int QList::lastIndexOf(const T &value, int from = -1) const

返回从索引位置 from 开始向列表头部方向搜索,在列表中 value 最后一次出现的索引位置。如果 from 是 -1,将会从最后一个元素开始搜索。如果没有找到则返回 -1。

示例:

QList<QString> list;
list << "A" << "B" << "C" << "B" << "A";
list.lastIndexOf("B");      // 返回 3
list.lastIndexOf("B", 3);   // 返回 3
list.lastIndexOf("B", 2);   // 返回 1
list.lastIndexOf("X");      // 返回 -1

该方法要求值类型实现了 operator==()

需要注意的是 QList 和 C 数组类似,索引也是从 0 开始。除了上面提到的值,其他的负索引值不被支持。

另请参阅 indexOf()。

int QList::length() const

该方法等同于 count()。

该方法在 Qt 4.5 中引入。

另请参阅 count()。

QList QList::mid(int pos, int length = -1) const

返回一个包含从列表的 pos 位置开始的元素的子列表,如果 length 为 -1(默认值),那么从 pos 开始的所有元素都会被子列表包含,否则子列表将会包含 length 个(如果剩余元素个数不足length 则为剩下的全部)元素。

void QList::move(int from, int to)

将位于索引位置 from 的元素移动到索引位置 to

示例:

QList<QString> list;
list << "A" << "B" << "C" << "D" << "E" << "F";
list.move(1, 4);
// list: ["A", "C", "D", "E", "B", "F"]

等同于 insert(to, takeAt(from))。该方法会假定 fromto 都不小于 0 且小于 size()。为了避免调用出错,应提前检查 fromto 是否不小于 0 且小于 size()。

另请参阅 swap(), insert() 和 takeAt()。

void QList::pop_back()

该方法用于提供对 STL 的兼容,等同于 removeLast()。该方法要求列表不能为空,如果列表可能为空,应先调用 isEmpty() 进行检查。

void QList::pop_front()

该方法用于提供对 STL 的兼容,等同于 removeFirst()。该方法要求列表不能为空,如果列表可能为空,应先调用 isEmpty() 进行检查。

void QList::prepend(const T &value)

在列表头部插入 value

示例:

QList<QString> list;
list.prepend("one");
list.prepend("two");
list.prepend("three");
// list: ["three", "two", "one"]

该方法等同于 list.insert(0, value)。

如果该列表是非共享的,那么此操作通常会非常快(均摊下来为 常量时间复杂度),因为 QList 在内部缓存的两端都预分配了额外的内存空间用于支持列表两端的快速增长。

另请参阅 append() 和 insert()。

void QList::push_back(const T &value)

该方法用于提供对 STL 的兼容,等同于 append(value)。

void QList::push_front(const T &value)

该方法用于提供对 STL 的兼容,等同于 prepend(value)。

QList::reverse_iterator QList::rbegin()

返回一个指向列表在逆序遍历时第一个元素 STL 风格的反向迭代器。

该方法在 Qt 5.6 中引入。

另请参阅 begin(), crbegin() 和 rend()。

QList::const_reverse_iterator QList::rbegin() const

这是个重载函数。

该方法在 Qt 5.6 中引入。

int QList::removeAll(const T &value)

移除列表中所有值为 value 的元素,然后返回移除的数量。

示例:

QList<QString> list;
list << "sun" << "cloud" << "sun" << "rain";
list.removeAll("sun");
// list: ["cloud", "rain"]

该方法要求值类型实现了 operator==()

另请参阅 removeOne(), removeAt(), takeAt() 和 replace()。

void QList::removeAt(int i)

移除位于索引位置 i 的元素。i 必须是列表中的合法索引位置 (即 0 <= i < size())。

另请参阅 takeAt(), removeFirst(), removeLast() 和 removeOne()。

void QList::removeFirst()

移除列表中的第一个元素,等同于调用 removeAt(0)。该方法要求列表不能为空,如果列表可能为空,应先调用 isEmpty() 进行检查。

另请参阅 removeAt() 和 takeFirst()。

void QList::removeLast()

移除列表中最后一个元素,等同于调用 removeAt(size() - 1)。该方法要求列表不能为空,如果列表可能为空,应先调用 isEmpty() 进行检查。

另请参阅 removeAt() 和 takeLast()。

bool QList::removeOne(const T &value)

移除列表中第一个值为 value 的元素,若找到并移除成功则返回 true,否则返回 false

示例:

QList<QString> list;
list << "sun" << "cloud" << "sun" << "rain";
list.removeOne("sun");
// list: ["cloud", "sun", "rain"]

该方法要求值类型实现了 operator==()

该方法在 Qt 4.4 中引入。

另请参阅 removeAll(), removeAt(), takeAt() 和 replace()。

QList::reverse_iterator QList::rend()

返回一个指向列表在逆序遍历下最后一个元素的后一个元素的 STL 风格的反向迭代器。

该方法在 Qt 5.6 中引入。

另请参阅 end(), crend() 和 rbegin()。

QList::const_reverse_iterator QList::rend() const

这是个重载函数。

该方法在 Qt 5.6 中引入。

void QList::replace(int i, const T &value)

将位于索引位置 i 的元素替换为 valuei 必须是列表中合法的索引位置 (即 0 <= i < size())。

另请参阅 operator[]() 和 removeAt()。

void QList::reserve(int alloc)

预留 alloc 个元素的空位。

如果 alloc 比当前列表的长度要小,则无事发生。

如果你可以提前预知接下来将会有多少元素追加到列表中,可以使用该方法避免 QList 内部数组重复分配内存。需要注意的是如果内部数组保存的是元素指针,则仅会仅预分配保存指针的数组的内存。

该方法在 Qt 4.7 中引入。

int QList::size() const

返回列表中元素的个数。

另请参阅 isEmpty() 和 count()。

bool QList::startsWith(const T &value) const

如果列表非空且第一个元素等于 value 则返回 true ,否则返回 false

该方法在 Qt 4.5 中引入。

另请参阅 isEmpty() 和 contains()。

void QList::swap(QList &other)

交换列表 other 和当前列表。该操作非常快且绝对不会失败。

该方法在 Qt 4.8 中引入。

void QList::swapItemsAt(int i, int j)

交换位于索引位置 ij 的元素。该方法会假定 fromto 都不小于 0 且小于 size()。为了避免调用出错,请提前检查 fromto 是否不小于 0 且小于 size()。

示例:

QList<QString> list;
list << "A" << "B" << "C" << "D" << "E" << "F";
list.swapItemsAt(1, 4);
// list: ["A", "E", "C", "D", "B", "F"]

该方法在 Qt 5.13 中引入。

另请参阅 move()。

T QList::takeAt(int i)

移除位于索引位置 i 的元素并返回该元素。 i 必须是列表中合法的索引位置 (即 0 <= i < size())。

如果并不需要返回值,使用 removeAt() 会更高效。

另请参阅 removeAt(), takeFirst() 和 takeLast()。

T QList::takeFirst()

移除列表中第一个元素并返回该元素,等同于 takeAt(0)。该方法会假定列表非空,为了避免调用失败,应提前调用 isEmpty() 进行检查。

如果该列表是非共享的,那么此操作将花费常量时间

如果不需要返回值,使用 removeFirst() 会更高效。

另请参阅 takeLast(), takeAt() 和 removeFirst()。

T QList::takeLast()

移除列表中最后一个元素并返回该元素。等同于 takeAt(size() - 1)。该方法会假定列表非空,为了避免调用失败,应提前调用 isEmpty() 进行检查。

如果该列表是非共享的,那么此操作将花费常量时间

如果不需要返回值,使用 removeLast() 会更高效。

另请参阅 takeFirst(), takeAt() 和 removeLast()。

QSet QList::toSet() const

返回一个包含且仅包含该 QList 中所有数据的 QSet 。由于 QSet 不允许有重复的数据,因此得到的 QSet 中的元素可能会少于原列表。

示例:

QStringList list;
list << "Julia" << "Mike" << "Mike" << "Julia" << "Julia";

QSet<QString> set = list.toSet();
set.contains("Julia");  // 返回 true
set.contains("Mike");   // 返回 true
set.size();             // 返回 2

注意: 从 Qt 5.14 开始,Qt 泛型容器类支持范围构造函数,建议用来取代这个方法。

另请参阅 toVector(), fromSet() 和 QSet::fromList()。

std::list QList::toStdList() const

返回一个包含且仅包含该 QList 中所有数据的 std::list。

示例:

QList<double> list;
list << 1.2 << 0.5 << 3.14;

std::list<double> stdlist = list.toStdList();

注意: 从 Qt 5.14 开始,Qt 泛型容器类支持范围构造函数,建议用来取代这个方法。

另请参阅 fromStdList() 和 QVector::toStdVector()。

QVector QList::toVector() const

返回一个包含且仅包含该 QList 中所有数据的 QVector 对象。

示例:

QStringList list;
list << "Sven" << "Kim" << "Ola";

QVector<QString> vect = list.toVector();
// vect: ["Sven", "Kim", "Ola"]

注意: 从 Qt 5.14 开始,Qt 泛型容器类支持范围构造函数,建议用来取代这个方法。

另请参阅 toSet(), fromVector() 和 QVector::fromList()。

T QList::value(int i) const

返回位于索引位置 i 的元素的值。

如果索引 i 越界了,该方法会返回一个默认值。如果想确保索引位于边界内,你可以使用 at() 来代替,同时也会略快一些。

另请参阅 at() 和 operator[]()。

T QList::value(int i, const T &defaultValue) const

这是个重载函数。

如果索引 i 越界了,该方法会返回 defaultValue

bool QList::operator!=(const QList &other) const

如果 other 不等于该列表则返回 true,否则返回 false

当且仅当两个列表以相同的顺序包含相同的值时两个列表才被认为是相等的。

该方法要求值类型实现了 operator==()

另请参阅 operator==()。

QList QList::operator+(const QList &other) const

返回一个包含了 other 列表中所有元素附加在该列表之后得到的全部元素的列表。

另请参阅 operator+=()。

QList &QList::operator+=(const QList &other)

other 列表中所有元素追加到当前列表尾部并返回当前列表的引用。

另请参阅 operator+() 和 append()。

QList &QList::operator+=(const T &value)

这是个重载函数。

追加值 value 到列表中。

另请参阅 append() 和 operator<<()。

QList &QList::operator<<(const QList &other)

other 列表中所有元素追加到当前列表尾部并返回当前列表的引用。

另请参阅 operator+=() 和 append()。

QList &QList::operator<<(const T &value)

这是个重载函数。

追加值 value 到列表中。

bool QList::operator==(const QList &other) const

如果 other 等于该列表则返回 true,否则返回 false

当且仅当两个列表以相同的顺序包含相同的值时两个列表才被认为是相等的。

该方法要求值类型实现了 operator==()

另请参阅 operator!=()。

T &QList::operator[](int i)

以可修改的引用返回位于索引位置 i 的元素。i 必须是列表中一个合法的索引位置 (即 0 <= i < size())。

如果对一个处于共享状态的列表调用该方法,则会触发对所有元素的拷贝。否则该方法运行时间开销为常量时间复杂度。如果你不打算修改列表,你用当使用 QList::at()。

另请参阅 at() 和 value()。

const T &QList::operator[](int i) const

这是个重载函数。

等同于 at()。 该方法运行时间开销为 常量时间复杂度

相关非成员函数

template uint qHash(const QList &key, uint seed = 0)

返回一个 key 的哈希值,可通过 seed 设置计算的种子。

该方法要求值类型 T 提供 qHash() 的重载。

该方法在 Qt 5.6 中引入。

template bool operator<(const QList &lhs, const QList &rhs)

如果列表 lhs字典序小于 rhs 则返回 true; 否则返回 false

该方法要求值类型实现了 operator<()

该方法在 Qt 5.6 中引入。

template QDataStream &operator<<(QDataStream &out, const QList &list)

将列表 list 写入流 out 中。

该方法要求值类型实现了 operator<<()

另请参阅 QDataStream 操作符格式

template bool operator<=(const QList &lhs, const QList &rhs)

如果列表 lhs字典序小于或等于 rhs 则返回 true; 否则返回 false

该方法要求值类型实现了 operator<()

该方法在 Qt 5.6 中引入。

template bool operator>(const QList &lhs, const QList &rhs)

如果列表 lhs字典序大于 rhs 则返回 true; 否则返回 false

该方法要求值类型实现了 operator<()

该方法在 Qt 5.6 中引入。

template bool operator>=(const QList &lhs, const QList &rhs)

如果列表 lhs字典序大于或等于 rhs 则返回 true; 否则返回 false

该方法要求值类型实现了 operator<()

该方法在 Qt 5.6 中引入。

template QDataStream &operator>>(QDataStream &in, QList &list)

从流 in 中读取一个列表到 list

该方法要求值类型实现了 operator>>()

另请参阅 QDataStream 操作符格式

已废弃成员

下述的 QList 成员已被废弃。 仅为保证旧代码的正常运行而被保留。我们强烈建议在新代码中不要再使用这些成员。

公共成员函数

(obsolete) voidswap(int i, int j)

成员函数文档

void QList::swap(int i, int j)

此方法已废弃。仅为保证旧代码的正常运行而被保留。我们强烈建议在新代码中不要再使用这些成员。

使用 swapItemsAt() 代替。

另请参阅 move() 和 swapItemsAt().

模型/视图 编程

模型/视图编程简介

Qt 中包含了一系列的项目视图类,他们使用了模型/视图架构来管理数据和显示之间的关系。此架构的功能分离特征给开发人员在自定义项目的呈现形式时带来了很大的灵活性,并提供标准的模型接口,以允许将各种数据源与现有项目视图一起使用。在本文档中,我们对模型/视图进行了简要介绍,概述了所涉及的概念,并描述了项目视图系统的结构特征。介绍了体系结构中的每个组件,并给出了示例,这些示例告诉我们如何使用所提供的类。

模型/视图体系架构

模型-视图-控制器(MVC)是源自 Smalltalk 的设计模式,在构建用户界面时经常使用。在《设计模式》一书中,Gamma 等人写到:

MVC 由三种对象组成。模型是应用程序对象,视图是其在屏幕上的呈现,控制器定义了用户界面对用户输入的反应方式。 在MVC之前,用户界面设计往往会将这些对象整合在一起。 MVC 使它们解耦以增加灵活性和重用性。

如果将视图和控制器对象组合在一起,就是模型/视图架构。基于将数据的存储方式与向用户呈现的方式分开的原理,模型/视图架构提供了一个更简单的框架。这种分离使得可以在几个不同的视图中显示相同的数据,并实现新的视图类型,而无需更改基础数据结构。为了灵活处理用户输入,我们引入了委托的概念。在此框架中使用委托的好处在于,它允许自定义呈现和编辑数据项的方式。

模型/视图架构

模型与数据源通信,为架构中的其他组件提供接口。通信的性质取决于数据源的类型以及模型的实现方式。

视图从模型中获取模型索引; 这些索引是对数据项的引用。 通过向模型提供模型索引,视图可以从数据源检索出数据项。

在标准视图中,委托负责渲染显示数据项。 当编辑项目后,委托将直接通过模型索引与模型进行通信。

通常,模型/视图类可以分为上述三个组:模型,视图和委托。这些组件中的每个组件都由抽象类定义,这些抽象类提供了公共接口,并在某些情况下提供了一些功能的默认实现。抽象类应被子类化,以提供其他组件期望的全部功能;当然也可以编写专用组件。

模型、视图和委托之间通过信号槽通信。

  • 数据源的数据发生改变时模型将发射信号通知视图。
  • 用户交互发生改变时,视图将发射相应的信号。
  • 在编辑期间,委托将发射信号来告知模型和视图有关编辑器的状态。

模型

所有项目模型均基于 QAbstractItemModel 类。此类定义一个接口,视图和委托使用该接口访问数据。数据本身不必存储在模型中。它可以保存在由单独的类,文件,数据库或某些其他应用程序组件提供的数据结构或存储库中。

有关模型的基本概念在 “模型类”部分中介绍。

QAbstractItemModel 提供了一个数据接口,该接口足够灵活,可以处理以表,列表和树的形式表示数据的视图。但是,当为列表和类似表的数据结构实现新模型时,QAbstractListModelQAbstractTableModel 类是更好的选择,因为它们提供了常用功能的默认实现。这些类中的每一个都可以被子类化以提供支持特殊类型的列表和表的模型。

创建新模型 部分中讨论了模型子类化的过程。

Qt提供了一些现成的模型,可用于处理数据项:

如果这些标准模型不满足您的要求,则可以将 QAbstractItemModelQAbstractListModelQAbstractTableModel 子类化以创建您自己的自定义模型。

视图

Qt 提供了针对各种视图的完整实现:QListView 用于显示项目列表,QTableView 用于显示表结构模型的数据,QTreeView 在一个分层次的结构列表中显示数据项。这些类均基于 QAbstractItemView 抽象基类。尽管这些类都是已经实现好的,但也可以将它们子类化以提供满足我们需求的自定义视图。

所有可用的视图可在 视图类 部分中进行查阅。

委托

QAbstractItemDelegate 是模型/视图框架中委托的抽象基类。默认委托实现由 QStyledItemDelegate 提供, Qt 中的标准视图都将其作为默认委托。但是,QStyledItemDelegateQItemDelegate 是绘制和为视图中的项目提供编辑器的独立替代方法。它们之间的区别在于 QStyledItemDelegate 使用当前样式来绘制其项目。因此,在实现自定义委托或使用 Qt 样式表时,建议将 QStyledItemDelegate 用作基类。

委托在 “委托类” 部分中进行了描述。

排序

在模型/视图架构中,有两种方法可以进行排序;选择哪种方法取决于底层使用的模型。

如果您的模型是可排序的,即重新实现 QAbstractItemModel::sort() 函数,则 QTableViewQTreeView 都提供了API,可让您以编程方式对模型数据进行排序。 另外,通过将 QHeaderView::sortIndicatorChanged() 信号连接到 QTableView::sortByColumn() 槽函数或 QTreeView::sortByColumn() 槽函数,可以启用交互式排序(即允许用户通过单击视图的标题对数据进行排序))。

如果您的模型没有所需的接口,或者如果您想使用列表视图来呈现数据,则另一种方法是在视图呈现数据之前,使用委托模型来转换模型的结构。 有关 代理模型 的部分将对此进行详细介绍。

便捷类

从标准视图类派生出了许多便捷类,用于 Qt 中基于项目的项目视图类和表类,它们不打算被子类化。

比如 QListWidgetQTreeWidgetQTableWidget

这些类的灵活性不如视图类,并且不能与任意模型一起使用。我们建议您使用模型/视图方法来处理项目视图中的数据,除非您强烈需要一组基于项目的类。

如果希望在使用基于项目的接口的同时利用模型/视图方法提供的功能,请考虑将视图类(例如 QListViewQTableViewQTreeView )与 QStandardItemModel 一起使用。

使用模型和视图

以下各节说明如何在 Qt 中使用模型/视图模型。每个部分都包含一个示例,紧接着是展示如何创建新组件。

Qt 中包含的两个模型

Qt 提供的两个标准模型是 QStandardItemModelQFileSystemModelQStandardItemModel 是一个多功能模型,可用于表示列表,表和树视图所需的各种不同的数据结构。该模型还保存数据项。QFileSystemModel 是用于维护有关目录内容的信息的模型,它本身不保存任何数据项,而仅表示本地文件系统上的文件和目录。

QFileSystemModel 提供了一个现成的模型用来测试使用,并且可以通过简单的配置就可以使用现有的数据。使用该模型,我们可以展示如何创建一个可用于现成视图的模型,并探索如何使用模型索引来操控数据。

通过现有模型使用视图

QListViewQTreeView 类是最适合与 QFileSystemModel一起使用的视图。下面提供的示例是分别在树形视图和列表视图中显示相同的目录内容。这些视图共享用户的选择,以便在两个视图中突出显示所选的项目。

shared dir model

我们使用了现成的模型 QFileSystemModel,并创建了一些视图来显示目录的内容。展示了使用模型的最简单方法。 该模型的构建和使用是在单个 main() 函数中执行的:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QSplitter *splitter = new QSplitter;

    QFileSystemModel *model = new QFileSystemModel;
    model->setRootPath(QDir::currentPath());

该模型被设置为使用来自特定文件系统的数据。调用 setRootPath() 将告诉模型文件系统上的哪个驱动器会暴露给视图。

我们使用同一个模型创建两个不同的视图,来查看模型中的项目呈现出来的效果:

QTreeView *tree = new QTreeView(splitter);
tree->setModel(model);
tree->setRootIndex(model->index(QDir::currentPath()));

QListView *list = new QListView(splitter);
list->setModel(model);
list->setRootIndex(model->index(QDir::currentPath()));

视图的构造方法与其他部件一样。设置视图来显示模型中的项目仅需使用目录模型作为参数来调用视图的 setModel() 函数即可。我们通过在每个视图上调用 setRootIndex() 函数来过滤模型提供的数据,并从文件系统模型中为当前目录传递合适的模型索引。

这种情况下对于 QFileSystemModel 来说只能通过传递一个目录参数来使用 index() 函数获取索引,模型索引在 模型类 部分中讨论。

该函数的其余部分是在分裂器部件中显示视图,并运行应用程序的事件循环:

    splitter->setWindowTitle("Two views onto the same file system model");
    splitter->show();
    return app.exec();
}

在上面的示例中,我们忽略了如何处理项目选择。 在 处理项目视图中的选择项 部分中将更详细地介绍。

模型类

在学习如何处理选择项之前,理解模型/视图框架中使用的概念是很有用的。

基本概念

在模型/视图体系结构中,模型提供了视图和委托用来访问数据的标准接口。在 Qt 中,标准接口由 QAbstractItemModel 类定义。无论数据项如何存储在任何基础数据结构中,QAbstractItemModel 的所有子类都将数据表示为一个有层次的包含项表的结构。视图使用此约定来访问模型中的数据项,但是它们向用户呈现此信息的方式不受限制。

modelview-models

模型通过信号槽机制通知所有附加视图有关数据更改的信息。

本节描述一些基本概念,这些概念对于其他组件通过模型类访问数据项的方式至关重要。 稍后的部分将讨论更高级的概念。

模型索引

为了确保数据的表示方式与访问方式分开,引入了模型索引的概念。通过模型获得的每条信息都由模型索引表示。视图和委托使用这些索引来请求要显示的数据项。

所以,只有模型需要知道如何获取数据和定义数据类型。模型索引包含一个指向创建它们的模型的指针,以免在使用多个模型时造成混淆。

QAbstractItemModel *model = index.model();

模型索引提供了一个对信息的临时的引用,可用于通过模型检索或修改数据。由于模型可能会不时重组其内部结构,因此模型索引可能会变得无效,不应进行存储。如果需要长期使用一条信息,则必须创建一个持久的模型索引。这为模型保持最新状态提供了参考。临时模型索引由 QModelIndex 类提供,而持久模型索引由 QPersistentModelIndex 类提供。

要获得与数据项相对应的模型索引,必须为模型指定三个属性:行号,列号和父项的模型索引。以下各节将详细描述和解释这些属性。

行和列

在基本的形式中,可以将模型作为一个简单的表进行访问,项目按行和列的编号位于其中。这并不意味着底层数据存储在数组结构中;行号和列号的使用仅仅是组件之间相互通信的约定。我们可以通过在模型中指定给定项目的行号和列号来检索有关任何给定项目的信息,我们接收到的是代表该项目的索引:

QModelIndex index = model->index(row, column, ...);

为简单的单层数据结构(例如列表和表)提供接口的模型不需要提供任何其他信息,但是,如上述代码所示,在获取模型索引时,我们需要提供更多信息。

行和列

该图显示了基本表模型的表示形式,其中每个项目都由一对行号和列号定位。通过将相关的行号和列号传递给模型,我们获得了一个引用数据项的模型索引。

QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexB = model->index(1, 1, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

始终通过用 QModelIndex() 指定为其父项来引用模型中的顶级项目。下一节将对此进行讨论。

项的父项

当在表视图或列表视图中使用数据时,模型提供的类似于表的接口就非常合适。行号和列号系统准确地映射到视图显示项目的方式上。但是,诸如树状视图的结构对于模型项目的操作要求模型提供更灵活的接口。每个项目也可以是另一个项目表的父项,就像树状视图中的顶级项目可以包含另一个项目列表一样。

当请求模型项的索引时,我们必须提供有关该项父项的一些信息。在模型之外,引用项目的唯一方法是通过模型索引,因此还必须提供父模型索引:

QModelIndex index = model->index(row, column, parent);
父项、行和列

该图展示了树模型的表示形式,其中每一项均由父项、行号和列号检索出来。

“A”和“C”项在模型中为同级顶级:

QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

“A”项有多个子项。使用以下代码获得“B”项的模型索引:

QModelIndex indexB = model->index(1, 0, indexA);

项角色

模型中的项可以为其他组件扮演各种不同的角色,从而可以为不同的数据提供不同的方案。 例如,Qt::DisplayRole 用于访问可以在视图中显示为文本的字符串。通常,项包含许多不同角色的数据,标准角色由 Qt::ItemDataRole 定义。

我们可以通过向模型传递与该项相对应的模型索引,并指定一个角色来获取所需的数据类型,从而获得我们想要的数据:

QVariant value = model->data(index, role);
项角色

项角色向模型指示要引用的数据类型。视图可以以不同的方式显示角色,因此为每个角色提供适当的信息很重要。

创建新模型 部分将更详细地介绍角色的某些特定用法。

项标准角色 Qt::ItemDataRole 涵盖了大部分常用的项角色。通过为每个角色提供适当的项目数据,模型可以向视图和委托提示如何将项呈现给用户。不同类型的视图可以根据自身需要解释或忽略此信息。也可以为特定目的定义其他角色。

摘要

  • 模型索引以独立于任何底层数据结构的方式提供给视图和委托有关模型所提供项的位置的信息。
  • 项通过其行号和列号以及其父项的模型索引检索出来。
  • 模型索引是由模型根据其他组件(例如视图和委托)的请求构造出来的。
  • 如果使用 index() 请求索引时为父项指定了有效的模型索引,则返回的索引将引用模型中该父项之下的项。获得的索引指向该项目的子项。
  • 如果使用 index() 请求索引时为父项指定了无效的模型索引,则返回的索引引用模型中的顶级项。
  • 项角色 用来区分与项关联的不同类型的数据。

使用模型索引

为了演示如何使用模型索引从模型中检索数据,我们设置了一个没有视图的 QFileSystemModel,并在部件中显示文件和目录的名称。尽管这没有展示使用模型的正常方法,但是它说明了在处理模型索引时模型使用的规则。

QFileSystemModel 加载是异步的,以最大程度地减少系统资源的使用。在处理此模型时,我们必须考虑到这一点。

我们通过以下方式构造文件系统模型:

QFileSystemModel *model = new QFileSystemModel;
connect(model, &QFileSystemModel::directoryLoaded, [model](const QString &directory) {
    QModelIndex parentIndex = model->index(directory);
    int numRows = model->rowCount(parentIndex);
});
model->setRootPath(QDir::currentPath);

在这种情况下,我们首先设置默认的 QFileSystemModel 。我们将其连接到 lambda,在其中我们将使用该模型提供的 index() 的特定实现来获取父索引。 在 lambda 中,我们使用 rowCount() 函数计算模型中的行数。最后,我们设 QFileSystemModel 的根路径,以便它开始加载数据并触发 lambda

为简单起见,我们只处理模型第一栏中的项目。我们依次检查每一行,获取每一行中第一项的模型索引,然后读取模型中为该项存储的数据。

for (int row = 0; row < numRows; ++row) {
    QModelIndex index = model->index(row, 0, parentIndex);

为了获得模型索引,我们指定行号,列号(第一列为零),并为所需的所有项的父项指定适当的模型索引。使用模型的 data() 函数检索存储在每个项目中的文本。我们指定模型索引和 DisplayRole 获取字符串形式的项数据。

QString text = model->data(index, Qt::DisplayRole).toString();
    // Display the text in a widget.

}

以上示例演示了从模型中检索数据的基本用法:

  • 模型的大小范围可以使用 rowCount()columnCount 获取到。这些函数通常需要指定一个父项模型索引。
  • 模型索引用于访问模型中的数据项。指定一个项需要行、列和父项的模型索引。
  • 通过 QModelIndex() 指定一个空的父项的索引来访问模型中的顶层项。
  • 项包含不同角色的数据。要获得特定角色的数据,必须同时向模型提供模型索引和角色。

拓展阅读

可以通过提供了标准接口的 QAbstractItemModel 类来创建新模型。在 创建新模型 部分,我们将通过创建一个方便可用的保存字符串列表的模型来展示这一点。

视图类

概念

在模型/视图架构中,视图从模型中获取数据项并将其呈现给用户。呈现数据的方式不必类似于模型提供数据的方式,而可以与用于存储数据项的底层数据结构完全不同

通过使用 QAbstractItemModel 提供的标准模型接口,QAbstractItemView 提供的标准视图接口以及使用表示数据项的模型索引来实现内容和表示的分离。视图通常管理从模型获得的数据的整体布局。它们可以自己呈现单个数据项,或使用委托来处理渲染和编辑功能。

除了显示数据外,视图还处理项目之间的导航以及项目选择。这些视图还实现了基本的用户界面功能,例如上下文菜单和拖放功能。视图可以提供项目的默认编辑功能,也可以与 委托 一起使用以提供自定义编辑器。

可以在没有模型的情况下构造视图,但是必须提供模型才能显示有用的信息。视图通过使用 选择项 来跟踪用户选择的项目,这些选择项可以被每个视图单独维护,或者在多个视图之间共享。

一些视图,例如 QTableViewQTreeView,需要显示表头和项目。这些由视图类 QHeaderView 实现。表头通常与包含它们的视图访问的是同一个模型。他们使用 QAbstractItemModel::headerData() 函数从模型中检索数据,并且通常以标签形式显示表头信息。可以继承 QHeaderView 类实现新的标头,以为视图提供更专业的标签。

使用现有视图

Qt提供了三种现成的视图类,它们以大多数用户熟悉的方式显示来自模型的数据。 QListView 可以将模型中的项目显示为简单列表或经典图标视图的形式。QTreeView 将模型中的项目显示为具有层次结构的列表,从而以紧凑的方式表示深层嵌套的结构。QTableView 以表格的形式显示来自模型的项目,非常类似于电子表格应用程序的布局。

standard-views

上面显示的标准视图的默认行为对于大多数应用程序来说应该足够了。它们提供了基本的编辑功能,并且可以进行定制以满足更专业的用户界面的需求。

使用模型

我们以创建的字符串列表模型为例,设置一些数据,并构造一个视图以显示模型的内容。所有这些都可以在一个函数中执行:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // Unindented for quoting purposes:
    QStringList numbers;
    numbers << "One" << "Two" << "Three" << "Four" << "Five";

    QAbstractItemModel *model = new StringListModel(numbers);

请注意,StringListModel 被声明为 QAbstractItemModel。这使我们能够使用模型的抽象接口,并确保即使我们将字符串列表模型替换为其他模型,代码仍然可以正常工作。

QListView 提供的列表视图足以在字符串列表模型中显示项目。我们构造视图,并使用以下代码行建立模型:

    QListView *view = new QListView;
    view->setModel(model);

该视图以正常方式显示:

    view->show();
    return app.exec();
}

视图渲染显示模型中的内容,并通过模型的接口访问数据。当用户尝试编辑项目时,视图使用默认委托提供一个默认的编辑器部件。

stringlistmodel

上图显示了 QListView 如何显示字符串列表模型中的数据。由于模型是可编辑的,因此视图允许使用默认委托来编辑列表中的每个项目。

多个视图共用一个模型

为同一模型提供多个视图仅是为每个视图设置相同模型的问题。在下面的代码中,我们创建了两个表视图,每个表视图都使用了创建的简单的表模型:

    QTableView *firstTableView = new QTableView;
    QTableView *secondTableView = new QTableView;

    firstTableView->setModel(model);
    secondTableView->setModel(model);

在模型/视图架构中使用信号槽可以将对模型的更改传递给到所有设置该模型的视图,从而确保我们始终可以访问相同的数据,而不管所使用的视图如何。

sharedmodel-tableviews

上面的两张图片显示了两种不同的视图使用同一个模型的情况,每一个视图都包含一些被选中的项。尽管在视图中始终显示来自模型的数据,但每个视图都维护一个自己内部的选择模型。在某些情况下这可能很有用,但是对于许多应用程序来说,需要一个共享的选择模型。

处理项目选择

QItemSelectionModel 类提供了用于处理视图中项目选择的机制。默认情况下,所有标准视图都构建自己的选择模型,并以常规方式与其交互。可以通过 selectionModel() 函数获得视图使用的选择模型,并可以使用 setSelectionModel 指定替换选择模型。当我们想在同一模型数据上提供多个一致的视图时,控制视图使用的选择模型的功能非常有用。

通常,除非要对模型或视图进行子类化,否则无需直接操作选择的内容。但是,如果需要,可以访问选择模型的接口,这在 处理项目视图中的选择项 部分中进行了探讨。

在视图之间共享选择

尽管默认情况下视图类提供它们自己的选择模型很方便,但是当我们在同一个模型上使用多个视图时,通常希望在所有视图中都同时显示模型的数据和用户的选择。由于视图类允许替换其内部选择模型,因此我们可以使用以下代码在视图之间实现统一选择:

    secondTableView->setSelectionModel(firstTableView->selectionModel());

为第二个视图提供了第一个视图的选择模型。现在,两个视图都在相同的选择模型上运行,从而使数据和所选项目保持同步。

sharedselection-tableviews

在上面显示的示例中,使用了两个相同类型的视图来显示相同模型的数据。但是,如果使用两种不同类型的视图,则在每个视图中所选项目的表现方式可能会非常不同。例如,表视图中的连续选择在树状图视图中就表现为一系列分散的高亮项的片段。

委托类

概念

与“模型-视图-控制器”模式不同,模型/视图设计不包括用于管理与用户交互的完全独立的组件。通常,视图负责将模型数据呈现给用户,并负责处理用户输入。为了使获取此输入的方式具有一定的灵活性,交互由委托执行。这些组件提供输入功能,还负责在某些视图中渲染单个项目。在 QAbstractItemDelegate 类中定义了用于控制委托的标准接口。

委托应该能够通过实现 paint()sizeHint() 函数来呈现它们自己的内容。但是,基于简单窗口部件的委托可以继承 QStyledItemDelegate 而不是 QAbstractItemDelegate 类,并使用这些函数的默认实现。

委托编辑器可以通过使用部件来管理编辑过程或直接处理事件来实现。本节稍后将介绍第一种方法,在 SpinBox委托 示例中也有相应的实现。

Pixelator 示例显示了如何创建自定义委托来专门渲染一个表视图。

使用现有的委托

Qt 提供的标准视图使用 QStyledItemDelegate 实例提供编辑功能。委托接口的默认实现以通用的样式为每个标准视图 QListViewQTableViewQTreeView 呈现项目。

所有标准角色均由标准视图使用的默认委托处理。QStyledItemDelegate 文档中描述了这些解释的方式。

视图使用的委托由 itemDelegate() 函数返回。setItemDelegate() 函数允许为标准视图设置自定义委托,并且在为自定义视图设置委托时必须使用此函数。

一个简单的委托

此处实现的委托使用 QSpinBox 提供编辑功能,并且主要用于显示整数的模型。因此我们建立了一个基于整数的自定义的表模型,但是由于自定义委托控制数据输入,因此我们可以轻松地使用 QStandardItemModel。 我们构造一个表视图来显示模型的内容,使用自定义委托进行编辑。

spinboxdelegate-example

我们从 QStyledItemDelegate 继承实现自定义委托,因为我们不想编写自定义显示函数。但是,我们仍然必须提供函数来管理编辑器部件:

class SpinBoxDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    SpinBoxDelegate(QObject *parent = nullptr);

    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const override;

    void setEditorData(QWidget *editor, const QModelIndex &index) const override;
    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const override;

    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
                              const QModelIndex &index) const override;
};

注意,在构造委托时,不会设置任何编辑器部件。我们仅在需要时构造一个编辑器部件。

提供一个编辑器

在此示例中,当表视图需要提供编辑器时,要求委托提供适合于项目修改的编辑器部件。createEditor() 函数可以让委托能够设置合适的部件作为编辑器:

QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
                                       const QStyleOptionViewItem &/* option */,
                                       const QModelIndex &/* index */) const
{
    QSpinBox *editor = new QSpinBox(parent);
    editor->setFrame(false);
    editor->setMinimum(0);
    editor->setMaximum(100);

    return editor;
}

注意,我们不需要保留指向编辑器部件的指针,因为当视图不再需要该编辑器时,负责将其销毁。

我们在编辑器上安装了委托的默认事件过滤器,以确保它提供用户期望的标准编辑快捷方式。可以将其他快捷方式添加到编辑器中,以允许更复杂的行为。这些内容将在 编辑提示 部分中进行讨论。

视图可通过调用稍后我们定义的函数来确保正确设置编辑器的数据和几何形状。我们可以根据视图提供的模型索引创建不同的编辑器。例如,如果我们有一列整数和一列字符串,则可以返回 QSpinBoxQLineEdit,具体取决于正在编辑的列。

委托必须提供将模型数据复制到编辑器中的功能。在此示例中,我们读取了 显示角色 中存储的数据,并在 spin box 中相应地设置了值。

void SpinBoxDelegate::setEditorData(QWidget *editor,
                                    const QModelIndex &index) const
{
    int value = index.model()->data(index, Qt::EditRole).toInt();

    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
    spinBox->setValue(value);
}

在此示例中,我们知道编辑器部件是一个 spin box,但是我们可以为模型中的不同类型的数据提供不同的编辑器,在这种情况下,我们需要在访问其成员函数之前将部件转换为适当的类型。

提交数据到模型

当用户完成在 spin box 中的值编辑后,视图通过调用 setModelData() 函数要求委托将已编辑的值存储在模型中。

void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                   const QModelIndex &index) const
{
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
    spinBox->interpretText();
    int value = spinBox->value();

    model->setData(index, value, Qt::EditRole);
}

由于视图管理委托的编辑器部件,因此我们只需要使用提供的编辑器的内容来更新模型。在这种情况下,我们确保 spin box 是最新的,并使用指定的索引更新模型数据。

标准 QStyledItemDelegate 类在完成编辑后通过发出 closeEditor() 信号通知视图。该视图确保编辑器部件已关闭并销毁。在此示例中,我们仅提供简单的编辑工具,因此我们永远不需要发出此信号。

数据的所有操作都是通过 QAbstractItemModel 提供的接口执行的。这使委托在很大程度上不受其处理的数据类型的影响,但是必须做出一些假设才能使用某些类型的编辑器部件。在此示例中,我们假定模型始终包含整数值,但是我们仍然可以将此委托与其他类型的模型一起使用,因为 QVariant 为意外的数据提供了合理的默认值。

更新编辑器的几何图形

委托负责管理编辑器的几何图形。创建编辑器时以及更改项目的大小或在视图中的位置时,必须设置几何形状。幸运的是,视图在 视图选项 对象内提供了所有必要的几何信息。

void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,
                                           const QStyleOptionViewItem &option,
                                           const QModelIndex &/* index */) const
{
    editor->setGeometry(option.rect);
}

在这种情况下,我们仅使用了视图选项提供的项的矩形几何信息。具有多个元素的项目的委托将不会直接使用项目矩形几何信息。它将编辑器放置在相对于其它项的位置。

编辑提示

编辑后,委托应向其他组件提供有关编辑过程结果的提示,并提供有助于后续任何编辑操作的提示。这是通过发出一个带有适当提示的 closeEditor() 信号来实现的。 默认情况下,这是由我们在构造 spin box 时安装的 QStyledItemDelegate 事件过滤器来处理的。

spin box 的行为可以进行调整,以使其更加用户友好。在 QStyledItemDelegate 提供的默认事件过滤器中,如果用户单击 Return 确认在 spin box 中的选择,则委托将值提交给模型并关闭 spin box 。我们可以通过在 spin box 上安装我们自己的事件过滤器来更改此行为,并提供我们需要的编辑提示。例如,我们可以发出带有 EditNextItem 提示的 closeEditor() 信号来自动开始编辑视图中的下一个项目。

另一种不需要使用事件过滤器的方法是提供我们自己的编辑器部件,为方便起见,可能将 QSpinBox 子类化。这种替代方法可以使我们更好地控制编辑器部件的行为,而无需编写其他代码。如果需要自定义标准 Qt 编辑器窗口部件的行为,简单常用的方法就是在委托中安装事件过滤器。

委托不必发出这些提示,但是为了应用程序的更加可用,我们需要发出提示以支持常见编辑操作。

处理项目视图中的选择项

概念

项目视图类中使用的选择模型对基于模型/视图架构的选择提供了的一般描述。尽管用于提供选择的标准视图足以处理提供的项目视图,但是选择模型允许创建专门的选择模型,以适合项目模型和视图的要求。

视图中被选择的项目的信息存储在一个 QItemSelectionModel 类的实例中。它负责维护单个模型中项目的模型索引,并且独立于任何视图。由于模型上可以有很多视图,因此可以在视图之间共享选择,从而允许应用程序以一致的方式显示多个视图。

选择由选择范围指定。只需记录每一个选择范围开始和结束的模型索引即可,这些有效地维护了有关大量项目选择的信息。非连续的选择可以使用多个选择范围来描述。

选择可以看作是在选择模型中保存的一个模型索引集合,最近的项目选择被称为当前选择。即使在应用了某些类型的选择命令后,也可以修改此选择的效果。这些将在本节后面讨论。

当前项和选择项

视图中总有一个当前项目和一个被选择的项目,两者时两个独立的状态。在同一时间,一个项目既可以是当前项目也同时可以是被选择的项目。视图图则确保总是有一个项目作为当前项目来实现键盘导航。

下表显示了当前项目和被选择的项目的区别。

当前项选择项
只能有一个当前项目可以有多个被选择的项目
使用键盘导航键或者鼠标按键可以改变当前项目项目是否处于被选择状态,取决于用户交互时当前几个预定义好的模式,比如单项选择、多重选择等
如果按下 F2 键或者双击都可以编辑当前项目(前提是允许编辑)当前项目可以指定一个范围来一起选择或者取消选择(或者两种混用)
当前项目会显示焦点矩形被选择的项目会使用选择矩形来表示

当操作选择时,可以将 QItemSelectionModel 看作是一个项目模型中所有项目的选择状态的一个记录。一旦设置了一个选择模型,所有的集合项目都可以被选择、取消选择或者切换选择状态,而不需要知道哪一个项目已经被选择了。所有被选择的项目的索引都可以被随时进行检索,其他的组件也可以通过信号槽机制来获取选择模型的改变信息。

使用选择模型

标准的视图类中提供了默认的选择模型,可以在大多数的应用中直接使用。属于一个视图的选择模型可以使用这个视图的 setSelectionModel() 获得,而且还可以在多个视图之间使用 setSelectionModel() 函数来共享该选择模型,所以一般是不需要重新构建一个选择模型的。

通过指定一个模型和一对指向 QItemSelection 的模型索引来创建选择。使用索引来引用给定模型中的项目,并将它们解释为所选项目块中的左上和右下项目。要将选择应用于模型中的项目,需要将选择提交给选择模型。这可以通过多种方式来实现,每种方式都对选择模型中已经存在的选择产生不同的影响。

选择项目

为了演示选择的一些主要功能,我们构造了一个总共有32个项目的自定义表格模型实例,并将其设置给表格视图:

   TableModel *model = new TableModel(8, 4, &app);

   QTableView *table = new QTableView(0);
   table->setModel(model);

   QItemSelectionModel *selectionModel = table->selectionModel();

检索表视图的默认选择模型以供以后使用。我们不修改模型中的任何项目,而是选择一些显示在表左上方的一些项目。为此,我们需要检索与要选择的区域中的左上和右下项目相对应的模型索引:

   QModelIndex topLeft;
   QModelIndex bottomRight;

   topLeft = model->index(0, 0, QModelIndex());
   bottomRight = model->index(5, 2, QModelIndex());

要在模型中选择这些项目,并在表视图中查看相应的更改,我们需要构造一个选择对象,然后将其应用于选择模型:

   QItemSelection selection(topLeft, bottomRight);
   selectionModel->select(selection, QItemSelectionModel::Select);

使用 选择标志 的组合定义的命令将选择应用于选择模型。在这种情况下,所使用的标志使得记录在选择对象中的项目被包括在选择模型中,而与它们先前的状态无关。视图显示结果选择。

selected-items1

在示例模型上执行的命令都涉及到累积模型中各项的选择。也可以清除选择,或用新选择替换当前选择。

要将当前选择替换为新选择,请将其他选择标志与 当前 标志组合。使用此标志的命令指示选择模型将其当前模型索引集合替换为对 select() 的调用中指定的模型索引集合。要在开始添加新选择之前清除所有选择,将其他选择标记与 清除 标记结合使用。它的作用是重置选择模型的模型索引集合。

选择模型中的所有项目

要选择模型中的所有项目,必须为模型的每个级别创建一个选择,以覆盖该级别中的所有项目。为此,我们使用给定的父索引检索与左上和右下项目相对应的索引:

    QModelIndex topLeft = model->index(0, 0, parent);
    QModelIndex bottomRight = model->index(model->rowCount(parent)-1,
       model->columnCount(parent)-1, parent);

使用这些索引和模型构建选择。然后在选择模型中选择相应的项目:

    QItemSelection selection(topLeft, bottomRight);
    selectionModel->select(selection, QItemSelectionModel::Select);

需要对模型中的所有级别执行此操作。对于顶级项目,通常将以定义父索引的方式:

    QModelIndex parent = QModelIndex();

对于分层模型,hasChildren() 函数用于确定任何给定项目是否是另一级别项目的父项。

创建新模型

使用了模型/视图架构的组件之间功能分离特征允许创建的模型使用已有的视图。这种方法可以用标准图形界面组件呈现各种各样的数据源,比如 QListViewQTableViewQTreeView

QAbstractItemModel 类提供了足够灵活的接口,以支持按层次结构排列信息的数据源,从而允许以某种方式插入,删除,修改或分类数据。它还支持拖放操作。

QAbstractListModelQAbstractTableModel 类为更简单的非分层数据结构提供接口支持,并且更易于用作简单列表和表模型。

在本节中,我们将创建一个简单的只读模型,以探索模型/视图架构的基本原理。在本节的稍后部分,我们将修改此简单模型,以便用户可以修改项目。

有关更复杂的模型的示例,请参见 简单树模型 示例。

有关 QAbstractItemModel 子类的要求在 模型子类参考 文档中有更详细的描述。

设计模型

当为已有的数据结构创建新的模型时,考虑需要创建哪种模型很重要的。如果数据结构可以用列表或者表呈现出来,可以子类化 QAbstractListModel 或者 QAbstractTableModel,因为这些类已经为很多功能提供了默认的实现。

如果底层数据结构只能被具体有层次的树形结构表现出来,那么子类化 QAbstractItemModel 就很有必要。在 简单树模型 一例中采用了这种方法。

本节我们实现一个基于字符串列表的 QAbstractListModel 作为基类的简单模型。

无论底层数据结构采用什么形式,通常最好在专用模型中扩展补充标准 QAbstractItemModel API,可以更自然地访问底层数据结构。这样可以更轻松地用数据填充模型,也仍然允许其他常规模型/视图组件使用标准 API 与之交互。下述模型为此提供了一个自定义构造函数。

只读的示例模型

这里我们基于标准 QStringListModel 类实现了一个简单的,非层次结构的只读的数据模型。使用 QStringList 作为其内部数据源,并且仅实现构建功能模型所需的内容。为了简化实现,我们将 QAbstractListModel 子类化,因为它为列表模型定义了明智的默认行为,并且它公开了比 QAbstractItemModel 类更简单的接口。

在实现模型时,重要的是要记住 QAbstractItemModel 本身并不存储任何数据,它只是提供了一个视图用来访问数据的接口。对于最小的只读模型,仅需要实现一些功能,因为大多数接口都有默认实现。 类声明如下:

class StringListModel : public QAbstractListModel
{
    Q_OBJECT

public:
    StringListModel(const QStringList &strings, QObject *parent = nullptr)
        : QAbstractListModel(parent), stringList(strings) {}

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role) const override;
    QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const override;

private:
    QStringList stringList;
};

除了模型的构造函数之外,我们只需要实现两个函数:rowCount() 返回模型中的行数,data() 返回与指定模型索引相对应的数据项。

比较好的模型还实现了 [headerData()],用来显示树形视图和表视图的标题。

请注意,这是一个非分层模型,因此我们不必担心父子关系。如果我们的模型是分层的,那么我们还必须实现 index()parent() 函数。

字符串列表被存储在 stringList 内部的私有成员变量中。

模型尺寸

我们希望模型中的行数与字符串列表中的字符串数相同。考虑到这一点,我们实现 rowCount() 函数:

int StringListModel::rowCount(const QModelIndex &parent) const
{
    return stringList.count();
}

由于模型是非分层的,因此我们可以安全地忽略与父项相对应的模型索引。默认情况下,从 QAbstractListModel 派生的模型仅包含一列,因此我们无需重新实现 columnCount() 函数。

模型标题和数据

对于视图中的项目,我们想返回字符串列表中的字符串。data() 函数负责返回与 index 参数对应的数据项:

QVariant StringListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= stringList.size())
        return QVariant();

    if (role == Qt::DisplayRole)
        return stringList.at(index.row());
    else
        return QVariant();
}

仅当提供的模型索引有效,行号在字符串列表中的项目范围内并且请求的角色是我们支持的角色时,我们才返回有效的 QVariant

一些视图(例如 QTreeViewQTableView )能够通过项目数据显示标题。如果我们的模型显示在带有标题的视图中,我们希望标题显示行号和列号。我们可以通过将 headerData() 函数子类化来提供有关标题的信息:

QVariant StringListModel::headerData(int section, Qt::Orientation orientation,
                                     int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

    if (orientation == Qt::Horizontal)
        return QStringLiteral("Column %1").arg(section);
    else
        return QStringLiteral("Row %1").arg(section);
}

同样,仅当角色是我们支持的角色时,我们才返回有效的 QVariant。标题的方向也会影响返回的数据。

并非所有视图都显示带有项目数据的标题,可以通过配置隐藏它们。尽管如此,还是建议实现 headerData() 函数以提供有关模型据的相关信息。

一个项目可以具有多个角色,根据指定的角色给出不同的数据。我们模型中的项目只有一个角色 DisplayRole,因此无论指定的角色如何,我们都将返回项目的数据。但是,我们可以在其他角色中重用为 DisplayRole 提供的数据,例如 ToolTipRole,视图可用来在工具提示中显示有关项目的信息。

可编辑的模型

只读模型显示了如何将简单的选择呈现给用户,但是对于许多应用程序而言,可编辑列表模型更加有用。我们可以通过更改为只读实现的 data() 函数以及实现两个额外的函数 flags()setData() 来修改只读模型,以使项目可编辑。将以下函数声明添加到类定义中:

    Qt::ItemFlags flags(const QModelIndex &index) const override;
    bool setData(const QModelIndex &index, const QVariant &value,
                 int role = Qt::EditRole) override;

让模型可编辑

创建编辑器之前委托将检擦项目是否可编辑。模型必须让委托知道它的一些项目是可编辑的。具体做法是为模型中的每个项返回正确的标志;这种情况下,我们可以使能所有的项目并让这些项目可选择和可编辑:

Qt::ItemFlags StringListModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return Qt::ItemIsEnabled;

    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}

请注意,我们不必知道委托是如何执行实际的编辑过程的。我们只需要为委托提供一种在模型中设置数据的方法。这是通过 setData() 函数实现的:

bool StringListModel::setData(const QModelIndex &index,
                              const QVariant &value, int role)
{
    if (index.isValid() && role == Qt::EditRole) {

        stringList.replace(index.row(), value.toString());
        emit dataChanged(index, index, {role});
        return true;
    }
    return false;
}

在此模型中,字符串列表中与模型索引对应的项目将替换为所提供的值。但是,在修改字符串列表之前,我们必须确保索引有效,项目类型正确并且支持该角色。按照惯例,我们使用 EditRole 角色,因为这是标准项目委托使用的角色。但是,对于布尔值,可以使用 Qt::CheckStateRole 并设置 Qt::ItemIsUserCheckable 标志;然后将使用一个复选框来编辑值。模型中的底层数据对于所有角色都是相同的,这使得该模型与标准组件的集成更加容易。

设置数据后,模型必须让视图知道某些数据已更改。这是通过发出 dataChanged() 信号完成的。由于仅一项数据已更改,因此信号中指定的项范围仅限于一个模型索引。

同样,测试Qt::EditRole 需要更改 data() 函数:

QVariant StringListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= stringList.size())
        return QVariant();

    if (role == Qt::DisplayRole || role == Qt::EditRole)
        return stringList.at(index.row());
    else
        return QVariant();
}

插入和删除行

模型中的行数和列数是可以改变的。在字符串列表模型中,仅更改行数才有意义,因此我们仅重新实现用于插入和删除行的函数。这些在类定义中声明:

    bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;

由于此模型中的行与列表中的字符串相对应,所以 insertRows() 函数将多个空字符串插入到字符串列表中的指定位置之前。插入的字符串数等于指定的行数。

父索引通常用于确定插入到模型中的的位置。我们只有一个顶级字符串列表,因此我们只需在该列表中插入空字符串即可。

bool StringListModel::insertRows(int position, int rows, const QModelIndex &parent)
{
    beginInsertRows(QModelIndex(), position, position+rows-1);

    for (int row = 0; row < rows; ++row) {
        stringList.insert(position, "");
    }

    endInsertRows();
    return true;
}

该模型首先调用 beginInsertRows() 函数以通知其他组件行数即将更改。该函数指定要插入的第一行和最后一行新行的行号,以及其父项的模型索引。更改字符串列表后,它将调用 endInsertRows() 以完成操作并通知其他组件该模型的尺寸已更改,并返回 true 表示成功。

从模型中删除行的函数也很简单。要从模型中删除的行由位置和给定的行数指定。我们忽略父索引以简化实现,只是从字符串列表中删除相应的项。

bool StringListModel::removeRows(int position, int rows, const QModelIndex &parent)
{
    beginRemoveRows(QModelIndex(), position, position+rows-1);

    for (int row = 0; row < rows; ++row) {
        stringList.removeAt(position);
    }

    endRemoveRows();
    return true;
}

在删除任何底层数据之前都要调用 beginRemoveRows() 函数,并指定要删除的第一行和最后一行。这允许其他组件在数据不可用之前访问数据。删除行之后,模型将发出 endRemoveRows() 信号以完成操作,并让其他组件知道模型的尺寸已更改。

下一步

我们可以使用 QListView 类显示此模型或任何其他模型提供的数据,以垂直列表的形式显示模型的项目。对于字符串列表模型,此视图还提供了默认编辑器,以便可以操作项目。在 视图类 部分我们研究了标准视图的各种各样的用法。

模型子类参考 文档更详细地讨论了 QAbstractItemModel 子类的要求,并说明了如何在不同类型的模型中实现虚函数来实现各种不同的功能。

项目视图中的便捷类

基于项目的窗口部件具有反映其用途的名称:QListWidget 提供项目列表,QTreeWidget 显示多级树结构,QTableWidget 提供以项目为单位的表格。每个类都继承了 QAbstractItemView 类的行为,该类实现了项目选择和标头管理的常见行为。

列表部件

通常使用 QListWidget 和许多 QListWidgetItems 显示单级列表的项目。列表部件的构造方式与其他部件相同:

    QListWidget *listWidget = new QListWidget(this);

构造列表项时,可以将它们直接添加到列表部件中:

    new QListWidgetItem(tr("Sycamore"), listWidget);
    new QListWidgetItem(tr("Chestnut"), listWidget);
    new QListWidgetItem(tr("Mahogany"), listWidget);

也可以在没有父列表部件的情况下构造它们,并在以后将其添加到列表中:

    QListWidgetItem *newItem = new QListWidgetItem;
    newItem->setText(itemText);
    listWidget->insertItem(row, newItem);

列表中的每个项目都可以显示一个文本标签和一个图标,文本的字体和颜色都可以自定义。工具提示、状态提示和 What's This? 提示也都可以设置。

    newItem->setToolTip(toolTipText);
    newItem->setStatusTip(toolTipText);
    newItem->setWhatsThis(whatsThisText);

默认情况下,列表中的项目会根据添加的顺序显示,也可以使用 Qt::SortOrder 对项目列表进行正向或反向字母顺序排序:

    listWidget->sortItems(Qt::AscendingOrder);
    listWidget->sortItems(Qt::DescendingOrder);

树形部件

QTreeWidgetQTreeWidgetItem 类提供树或项目的层次结构列表。树部件中的每个项目都可以拥有自己的子项目,并且可以显示多列信息。树部件的创建与其他任何部件一样:

    QTreeWidget *treeWidget = new QTreeWidget(this);

在将项目添加到树部件之前,必须设置列数。例如,我们可以定义两列,并创建一个标题以在每列的顶部提供标签:

    treeWidget->setColumnCount(2);
    QStringList headers;
    headers << tr("Subject") << tr("Default");
    treeWidget->setHeaderLabels(headers);

为每个部分设置标签的最简单方法是提供一个字符串列表。对于更复杂的标题,可以构造一个树项目,根据需要对其进行装饰,并将其用作树部件的标题。

树部件中的顶级项目是使用树部件作为其父小部件构造的。可以按任意顺序插入它们,也可以通过在构造每个项目时指定上一个项目来确保以特定顺序列出它们:

    QTreeWidgetItem *cities = new QTreeWidgetItem(treeWidget);
    cities->setText(0, tr("Cities"));
    QTreeWidgetItem *osloItem = new QTreeWidgetItem(cities);
    osloItem->setText(0, tr("Oslo"));
    osloItem->setText(1, tr("Yes"));

    QTreeWidgetItem *planets = new QTreeWidgetItem(treeWidget, cities);

树部件在处理顶级项目时,与树中较深处的其他项目略有不同。可以通过调用树部件的 takeTopLevelItem() 函数从树的顶层删除项目,但可以通过调用其父项的 takeChild() 函数从较低级别的项目中删除项目。使用 insertTopLevelItem() 函数将项目插入树的顶层。在树的较低级别位置,使用父项的 insertChild() 函数。

在树的最高层和最低层之间移动项目很容易。我们只需要检查项目是否为顶级项目,该信息由每个项目的 parent() 函数提供。例如,我们可以删除树部件中的当前项目,而不管其位置如何:

    QTreeWidgetItem *parent = currentItem->parent();
    int index;

    if (parent) {
        index = parent->indexOfChild(treeWidget->currentItem());
        delete parent->takeChild(index);
    } else {
        index = treeWidget->indexOfTopLevelItem(treeWidget->currentItem());
        delete treeWidget->takeTopLevelItem(index);
    }

将项目插入到树部件中的其他位置也遵循相同的模式:

    QTreeWidgetItem *parent = currentItem->parent();
    QTreeWidgetItem *newItem;
    if (parent)
        newItem = new QTreeWidgetItem(parent, treeWidget->currentItem());
    else
        newItem = new QTreeWidgetItem(treeWidget, treeWidget->currentItem());

表部件

使用 QTableWidgetQTableWidgetItem 构造类似于电子表格的项目的表。这些提供了带有标题和要可以滚动的表的窗口部件。

可以创建具有一定数量的行和列的表,也可以根据需要将它们添加到未调整大小的表中。

    QTableWidget *tableWidget;
    tableWidget = new QTableWidget(12, 3, this);

在将表项添加到表中所需位置之前,先在表外部构造它们:

    QTableWidgetItem *newItem = new QTableWidgetItem(tr("%1").arg(
        pow(row, column+1)));
    tableWidget->setItem(row, column, newItem);

通过在表外部构造项目并将其用作标题,可以将水平和垂直标题添加到表中:

    QTableWidgetItem *valuesHeaderItem = new QTableWidgetItem(tr("Values"));
    tableWidget->setHorizontalHeaderItem(0, valuesHeaderItem);

注意,表中的行和列从零开始。

共同特征

这些便捷类都使用了相同的接口提供了一些基于项的特色功能。在以下各节中,我们将针对不同的部件提供一些示例。查看 模型/视图类 列表,可以获取有关所使用的每个函数的用法的更多详细信息。

隐藏项目

有时需要在项目视图部件中隐藏项目,而不是删除它们。上面所有的部件的项目都可以隐藏,以后再显示。可以通过调用 isItemHidden() 函数确定某个项目是否隐藏,并且可以使用 setItemHidden() 隐藏该项目。

由于此操作是基于项目的,因此对于所有三个便捷类都可以使用相同的功能。

选择项

选择项目的方式由部件的选择模式( QAbstractItemView::SelectionMode )控制。此属性控制用户是否可以选择一个或多个项目,并且在多个项目选择中控制选择是否必须是连续范围的项目。对于所有上述部件,选择模式都以相同的方式工作。

单项选择: 当用户需要从部件中选择单个项目时,默认的 SingleSelection 模式最合适。在此模式下,当前项目和所选项目相同。
多项目选择: 在这种模式下,用户可以在不更改现有选择的情况下切换部件中任何项目的选择状态,就像非独占复选框可以独立切换的方式一样。
扩展选择: 通常部件需要许多相邻的项目被选择,例如在电子表格中的部件,需要 ExtendedSelection 模式。在这种模式下,可以用鼠标和键盘选择窗口部件中连续范围内的项目。如果使用修饰键,则还可以对控件中与其他被选择的项不相邻的项进行选择。 如果用户在不使用修饰键的情况下选择了项目,则会清除现有选择。

使用 selectedItems() 函数读取窗口部件中的所选项目,从而提供可以迭代的相关项目的列表。例如,我们可以使用以下代码找到所选项目列表中所有数值的总和:

    const QList<QTableWidgetItem *> selected = tableWidget->selectedItems();
    int number = 0;
    double total = 0;

    for (QTableWidgetItem *item : selected) {
        bool ok;
        double value = item->text().toDouble(&ok);

        if (ok && !item->text().isEmpty()) {
            total += value;
            number++;
        }
    }

请注意,对于单选模式,当前项目就是被选择的项目。在多选和扩展选择模式下,取决于用户形成选择的方式,当前项目可能不在选择之内。

搜索

在项目视图部件中查找项目很实用。这三个项目视图便捷类都提供了一个通用的 findItems() 函数,以使其尽可能一致和简单。

根据从 Qt::MatchFlags 中选择的值指定的标准使用它们所包含的文本来搜索项。我们可以使用 findItems() 函数获得匹配项的列表:

    const QList<QTreeWidgetItem *> found = treeWidget->findItems(
        itemText, Qt::MatchWildcard);

    for (QTreeWidgetItem *item : found) {
        item->setSelected(true);
        // Show the item->text(0) for each item.
    }

如果上面的代码搜索包含指定文本的项目,列表和表部件同样适用。

在项目视图中使用拖放

模型/视图框架完全支持 Qt 的拖放功能。列表,表和树中的项目可以在视图中拖动,并且数据可以作为 MIME 编码的数据导入和导出。

标准视图自动支持内部拖放,可以移动项目来更改显示顺序。默认情况下,这些视图没有配置拖放功能,因为它们被配置为最简单,最常见的用途。要允许拖动项目,需要启用视图的某些属性,并且项目本身还必须允许进行拖动。

仅允许从视图中导出项目且不允许将数据放入其中的模型的要求比完全启用的拖放模型的要求少。

另请参阅 模型子类参考,以获取有关在新模型中启用拖放支持的更多信息。

使用便捷视图类

默认情况下,与 QListWidgetQTableWidgetQTreeWidget 一起使用的每种项目类型都配置为使用不同的标志集。 例如,每个 QListWidgetItemQTreeWidgetItem 最初都是启用的,可检查的,可选的,并且可用作拖放操作的源。每个 QTableWidgetItem 也可以进行编辑,并用作拖放操作的目标。

尽管所有标准项都为拖放设置了一个或两个标志,但是通常需要在视图本身中设置各种属性来使它启用对拖放操作的内建支持:

  • 启用项目拖拽,要将视图的 dragEnabled 属性设置为 true
  • 要允许用户将内部或者外部的项目放入视图中,需要设置视图的 viewport() acceptDrops 属性为 true
  • 要显示现在用户拖拽的项目将要放置的位置,需要设置 showDropIndicator 属性。这为用户提供了有关视图内项目放置的连续更新信息。

例如,我们可以使用以下代码在列表部件中拖放:

QListWidget *listWidget = new QListWidget(this);
listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
listWidget->setDragEnabled(true);
listWidget->viewport()->setAcceptDrops(true);
listWidget->setDropIndicatorShown(true);

该列表部件允许在视图内到处复制项目,甚至允许用户在包含相同数据类型的视图之间拖动项目。在两种情况下,都是复制而不是移动项目。

为了使用户能够在视图内移动项目,我们必须设置列表部件的 dragDropMode

listWidget->setDragDropMode(QAbstractItemView::InternalMove);

在模型/视图类中使用拖放

设置拖放视图的方式与便捷视图类相同。例如,可以使用与 QListWidget 相同的方式来设置 QListView

QListView *listView = new QListView(this);
listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
listView->setDragEnabled(true);
listView->setAcceptDrops(true);
listView->setDropIndicatorShown(true);

由于对视图显示的数据的访问由模型控制,因此所使用的模型还必须提供对拖放操作的支持。可以通过重新实现 QAbstractItemModel::supportedDropActions() 函数来指定模型支持的动作。例如,使用以下代码启用复制和移动操作:

Qt::DropActions DragDropListModel::supportedDropActions() const
{
    return Qt::CopyAction | Qt::MoveAction;
}

尽管可以给出 Qt::DropActions 中值的任意组合,但是需要编写模型来支持它们 例如,要允许 Qt::MoveAction 与列表模型一起正确使用,该模型必须直接或通过从其基类继承实现来提供 QAbstractItemModel::removeRows() 的实现。

启用项目拖放

通过重新实现 QAbstractItemModel::flags() 函数以提供适当的标志,模型向视图指示可以拖动哪些项目,哪些将接受放置。

例如,一个提供基于 QAbstractListModel 的简单列表的模型可以通过确保返回的标志包含 Qt::ItemIsDragEnabledQt::ItemIsDropEnabled 值来启用每个项目的拖放操作:

Qt::ItemFlags DragDropListModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags defaultFlags = QStringListModel::flags(index);

    if (index.isValid())
        return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
    else
        return Qt::ItemIsDropEnabled | defaultFlags;
}

注意,可以将项目拖放到模型的顶层,但是仅对有效项目有效。

在上面的代码中,由于模型是从 QStringListModel 派生的,我们通过调用重新实现的 flags() 函数来获取默认的标志集。

编码导出的数据

当通过拖放操作从模型中导出数据项时,会将它们编码为与一种或多种 MIME 类型相对应的格式。通过重新实现 QAbstractItemModel::mimeTypes() 函数并返回标准 MIME类型的列表,声明了可用于项目的MIME类型。

例如,仅提供纯文本的模型将提供以下实现:

QStringList DragDropListModel::mimeTypes() const
{
    QStringList types;
    types << "application/vnd.text.list";
    return types;
}

该模型还必须提供代码以被告知的格式编码数据。可以通过重新实现 QAbstractItemModel::mimeData() 函数来提供 QMimeData 对象来实现该功能,就像其他任何拖放操作一样。

以下代码显示了与给定索引列表相对应的每一项数据如何被编码为纯文本并存储在 QMimeData 对象中。

QMimeData *DragDropListModel::mimeData(const QModelIndexList &indexes) const
{
    QMimeData *mimeData = new QMimeData;
    QByteArray encodedData;

    QDataStream stream(&encodedData, QIODevice::WriteOnly);

    for (const QModelIndex &index : indexes) {
        if (index.isValid()) {
            QString text = data(index, Qt::DisplayRole).toString();
            stream << text;
        }
    }

    mimeData->setData("application/vnd.text.list", encodedData);
    return mimeData;
}

由于向函数提供了模型索引列表,该方法很通用,可以在分层模型和非分层模型中使用。

注意,必须将自定义数据类型声明为 元对象,并且必须为其实现流运算符。有关详细信息,请参见 QMetaObject 类描述。

将放置的数据插入到模型中

任何给定模型处理放置数据的方式取决于其类型(列表,表或树)以及其内容可能呈现给用户的方式。通常,保存放置数据的方法就是最适合模型底层数据存储的方法。

不同类型的模型倾向于以不同方式处理放置的数据。列表和表模型仅提供了用于存储数据项的平面结构。因此,当将数据放置到视图中的现有项目上时,他们可能会插入新的行(和列),或者可能会使用提供的某些数据覆盖模型中项目的内容。树模型通常能够将包含新数据的子项添加到其底层数据存储中,因此就用户而言,其行为更具可预测性。

底层的数据由模型的 QAbstractItemModel::dropMimeData() 重新实现来处理。例如,处理字符串的简单列表的模型可以提供一种实现,该实现将放置到现有项目上的数据与放置到模型顶层(即到无效项目)的数据分开处理。

通过重新实现 QAbstractItemModel::canDropMimeData() ,模型可以禁止放置到某些项目,或者取决于放置的数据。

该模型首先必须确保执行操作,所提供的数据采用可以使用的格式,并且其在模型中的目的地是有效的:

bool DragDropListModel::canDropMimeData(const QMimeData *data,
    Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    Q_UNUSED(action);
    Q_UNUSED(row);
    Q_UNUSED(parent);

    if (!data->hasFormat("application/vnd.text.list"))
        return false;

    if (column > 0)
        return false;

    return true;
}
bool DragDropListModel::dropMimeData(const QMimeData *data,
    Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    if (!canDropMimeData(data, action, row, column, parent))
        return false;

    if (action == Qt::IgnoreAction)
        return true;

如果提供的数据不是纯文本,或者为放置指定的列号无效,则简单的一列字符串列表模型可以指示失败。

根据要插入模型的数据是否被放到现有项目上,其处理方式有所不同。在这个简单的示例中,我们希望允许在现有项目之间,列表中的第一个项目之前以及最后一个项目之后进行放置。

发生放置时,对应于父项的模型索引将是有效的(指示该放置发生在某个项目上),或者将是无效的(指示该放置发生在视图中与模型顶层相对应的某处)。

    int beginRow;

    if (row != -1)
        beginRow = row;

最初,我们检查提供的行号,以查看是否可以使用它向模型中插入项目,而不管父索引是否有效。

    else if (parent.isValid())
        beginRow = parent.row();

如果父模型索引有效,则该放置到项目上。在这个简单的列表模型中,我们找出项目的行号,并使用该值将放置的项目插入模型的顶层。

    else
        beginRow = rowCount(QModelIndex());

当视图中的其他位置发生拖放并且行号不可用时,我们会将项目追加到模型的顶层。

在分层模型中,当某项发生放置时,最好将新项作为该项的子项插入模型中。在此处显示的简单示例中,模型只有一个级别,因此此方法不合适。

解码导入的数据

dropMimeData() 的每个实现还必须解码数据并将其插入到模型的底层数据结构中。

对于简单的字符串列表模型,可以对编码的项目进行解码并将其流式传输到 QStringList 中:

    QByteArray encodedData = data->data("application/vnd.text.list");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);
    QStringList newItems;
    int rows = 0;

    while (!stream.atEnd()) {
        QString text;
        stream >> text;
        newItems << text;
        ++rows;
    }

然后可以将字符串插入底层数据存储中。为了保持一致,可以通过模型自己的接口来完成此操作:

    insertRows(beginRow, rows, QModelIndex());
    for (const QString &text : qAsConst(newItems)) {
        QModelIndex idx = index(beginRow, 0, QModelIndex());
        setData(idx, text);
        beginRow++;
    }

    return true;
}

注意,该模型通常需要重新实现 QAbstractItemModel::insertRows()QAbstractItemModel::setData() 函数。

代理模型

在模型/视图框架中,单个模型提供的数据项可以被任意数量的视图共享,并且每个视图可能以完全不同的方式表示相同的信息。自定义视图和委托对于完全相同的数据进行不同的显示提供了有效方法。但是,应用程序通常需要提供对相同数据的不同的处理版本的常规视图,例如对项列表的不同排序视图。

尽管将排序和筛选操作作为视图的内部功能执行似乎是适当的,但是这种方法不允许多个视图共享这种可能代价高昂的操作的结果。另一种方法,涉及到模型本身的排序,会导致类似的问题,即每个视图都必须显示根据最近的处理操作组织的数据项。

为了解决此问题,模型/视图框架使用代理模型来管理各个模型和视图之间提供的信息。从视图的角度来看,代理模型与普通的模型类似,视图从源模型访问数据。模型/视图框架使用的信号槽机制确保无论在其自身与源模型之间放置多少代理模型,每个视图都将在合适的时机进行更新。

使用代理模型

可以在现有模型和任意数量的视图之间插入代理模型。Qt 提供了一个标准的代理模型 QSortFilterProxyModel,该模型通常被实例化并直接使用,但是也可以被子类化以提供自定义的过滤和排序功能。 QSortFilterProxyModel 类可以按以下方式使用:

    QSortFilterProxyModel *filterModel = new QSortFilterProxyModel(parent);
    filterModel->setSourceModel(stringListModel);

    QListView *filteredView = new QListView;
    filteredView->setModel(filterModel);

由于代理模型是从 QAbstractItemModel 继承的,因此它们可以连接到任何类型的视图,并且可以在视图之间共享。它们还可以用于处理从其他代理模型获得的信息。

QSortFilterProxyModel 在应用程序中可被实例化后直接使用。通过子类化此类并实现所需的比较操作,可以创建更多专业的代理模型。

自定义代理模型

通常,代理模型中使用的处理类型涉及到将每个数据项从源模型中的原始位置映射到代理模型中的不同位置。在某些模型中,某些项在代理模型中可能没有相应的位置;这些模型正在过滤代理模型。视图使用代理模型提供的模型索引访问数据项,这些索引不包含有关源模型或原始项在该模型中的位置的信息。

QSortFilterProxyModel 允许在将源模型中的数据提供给视图之前对其进行筛选,还允许将源模型的内容作为预先排序的数据提供给视图。

自定义过滤模型

QSortFilterProxyModel 类提供了一个相当通用的过滤模型,并且可以在多种常见情况下使用。对于高级用户,可以将 QSortFilterProxyModel 子类化,从而提供一种使自定义过滤器得以实现的机制。

QSortFilterProxyModel 的子类可以重新实现两个虚函数,只要请求或使用来自代理模型的模型索引,就会调用这两个虚函数:

QSortFilterProxyModel 中上述函数的默认实现返回 true,以确保将所有项目都传递给视图。重新实现这些函数应返回 false,以过滤出单独的行和列。

自定义排序模型

QSortFilterProxyModel 实例使用 std::stable_sort() 函数在源模型中的项目和代理模型中的项目之间设置映射,从而将已排序的分层次的项目公开给视图,而无需修改源模型的结构。要提供这种自定义排序的功能,需要重新实现 lessThan() 函数来执行自定义比较。

模型子类参考

模型子类需要实现 QAbstractItemModel 基类中定义的许多虚函数。需要实现的这些函数的数量取决于模型的类型-它是否为视图提供简单的列表,表还是复杂的项目层次结构。从 QAbstractListModelQAbstractTableModel 继承的模型可以利用这些类提供的函数的默认实现。以树状结构显示数据项的模型实现 QAbstractItemModel 中的许多虚函数提。

在模型子类中需要实现的函数可以分为三类:

  • 项目数据处理: 所有模型都需要实现一些函数,以使视图和委托能够查询模型的尺寸,检查项目并检索数据。
  • 导航和索引创建: 层次模型需要实现视图可以调用的函数,以导航它们公开的树状结构,并获取项目的模型索引。
  • 拖放支持和 MIME 类型处理: 模型继承了控制内部和外部拖放操作执行方式的函数。这些函数允许使用其他组件和应用程序可以理解的 MIME 类型来描述数据项。

项目数据处理

模型可以提供对数据的不同级别的访问:它们可以是简单的只读组件,某些模型可以支持调整大小的操作,而其他模型则可以编辑项目。

只读访问

要提供对模型数据的只读访问,必须在模型的子类中实现以下功能:

flags()用于其他组件来获取有关模型提供的每个项目的信息。在许多模型中,标志的组合应包括 Qt::ItemIsEnabledQt::ItemIsSelectable
data()用于向视图和委托提供项目数据。通常,模型只需要为 Qt::DisplayRole 和任何特定应用程序的用户角色提供数据,但是为 Qt::DisplayRoleQt::ToolTipRoleQt::AccessibleTextRoleQt::AccessibleDescriptionRole 提供数据也是一种好习惯。 有关与每个角色关联的类型的信息,请参见 Qt::ItemDataRole 枚举文档。
headerData()提供有关标题的数据信息。该信息仅由可显示标题信息的视图检索。
rowCount()提供模型公开的数据行数。

这四个函数必须在所有类型的模型中实现,包括列表模型( QAbstractListModel 子类)和表模型( QAbstractTableModel 子类)。

此外,必须在 QAbstractTableModelQAbstractItemModel 的直接子类中实现以下功能:

columnCount() 提供模型公开的数据列数。列表模型不提供此功能,因为它已在QAbstractListModel中实现。

可编辑的项目

可编辑模型允许修改数据项,还可以提供允许插入和删除行和列的函数。要启用编辑,必须正确实现以下功能:

flags()必须为每个项目返回适当的标志组合。特别是,此函数返回的值除了应用于只读模型中的项的值外,还必须包括 Qt::ItemIsEditable
setData()用于修改与指定模型索引关联的数据项。 为了能够接受用户界面元素提供的用户输入,此功能必须处理与 Qt::EditRole 相关的数据。 该实现还可以接受与 Qt::ItemDataRole 指定的许多不同种类的角色相关联的数据。更改数据项后,模型必须发出 dataChanged() 信号以将更改通知其他组件。
setHeaderData()用于修改水平和垂直标题信息。更改数据项后,模型必须发出 headerDataChanged() 信号以将更改通知其他组件。

可调整大小的模型

所有类型的模型都可以支持行的插入和删除。表模型和层次结构模型也可以支持列的插入和删除。在模型尺寸发生变化 之前之后 ,都必须通知其他组件有关模型尺寸的变化。可以实现以下函数以允许调整模型的大小,但是实现必须确保调用合适的函数以通知附加的视图和委托:

insertRows() 用于向所有类型的模型添加新行和数据项。函数的实现必须在将新行插入任何底层数据结构 之前 调用 beginInsertRows()然后立即 调用 endInsertRows()
removeRows()用于从所有类型的模型中删除行及其包含的数据项。在从任何底层数据结构中删除行 之前,必须调用 beginRemoveRows()然后立即 调用 beginRemoveRows()
insertColumns()用于向表模型和层次结构模型添加新的列和数据项。函数的实现必须在将新列插入任何底层数据结构 之前 调用 beginInsertColumns()然后立即 调用 endInsertColumns()
removeColumns()用于从表模型和层次模型中删除列及其包含的数据项。函数的实现必须在从任何基础数据结构中删除列 之前 调用 beginRemoveColumns()然后立即 调用 endRemoveColumns()

通常,如果操作成功,这些函数应返回true。但是,在某些情况下,操作可能仅部分成功;例如,如果可以插入少于指定数量的行。在这种情况下,模型应返回 false 来提示失败,让连接到该模型的组件处理这种情况。

在调整大小的 API 函数中发出信号使附加的组件有机会在任何数据变得不可用之前采取措施。使用 beginend 函数对插入和删除操作进行的封装也使模型能够正确管理持久模型索引。

通常,beginend 函数能够通知其他组件有关模型底层结构的更改。对于模型结构的更复杂的更改,可能涉及内部重组,数据排序或任何其他结构更改,有必要按照以下顺序:

该顺序可用于任何结构更新,以代替更高级,更方便的受保护方法。例如,如果一个200万行的模型需要删除所有奇数行,那就是100万个不连续的范围,每行1个元素。可以使用 beginRemoveRows 和 `endRemoveRows 一百万次,但这显然效率不高。相反,这可以作为一个单独的布局更改来表示,它一次更新所有必需的持久性索引。

懒惰的模型数据

懒散填充模型数据可以有效地延迟对模型信息的请求,直到视图实际需要它为止。

一些模型需要从远程源获取数据,或者必须执行耗时的操作才能获取有关数据组织方式的信息。由于视图通常要求尽可能多的信息以准确显示模型数据,因此限制返回给它们的信息量以减少不必要的后续数据请求可能很有用。

在分层模型中查找给定项目的子项数是一项昂贵的操作,确保仅在必要时才调用模型的 rowCount() 实现是有用的。在这种情况下,可以重新实现 hasChildren() 函数,以提供一种廉价的方法来查看视图是否存在子项,对于 QTreeView,则为其父项绘制适当的装饰。

无论 hasChildren() 的重新实现返回 true 还是 false,该视图都不必调用 rowCount() 来找出存在多少个子项。例如,如果 QTreeView 未扩展父项则就不需要知道有多少个子级。

如果知道许多项目将有子项,那么重新实现 hasChildren() 以无条件返回 true 有时是一种有用的方法。这样可以确保以后可以检查每个项目的子项,同时尽可能快地对模型数据进行初始填充。唯一的缺点是,在用户试图查看不存在的子项之前,没有子项的项目可能会在某些视图中显示不正确。

创建导航和模型索引

分层结构的模型需要提供一些函数,视图可以调用这些函数来导航它们公开的树状结构,并获取项目的模型索引。

父项和子项

由于公开给视图的结构是由底层数据结构决定的,因此由每个模型子类通过提供以下函数的实现来创建自己的模型索引:

index()给定父项的模型索引,此函数允许视图和委托访问该项的子项。如果找不到有效的子项-对应于指定的行,列和父模型索引,则该函数必须返回 QModelIndex() ,这是无效的模型索引。
parent()提供与任何给定子项的父项相对应的模型索引。如果指定的模型索引对应于模型中的顶级项,或者模型中没有有效的父项,则该函数必须返回使用空 QModelIndex() 构造函数创建的无效模型索引。

以上两个函数都使用 createIndex() 工厂函数来生成索引以供其他组件使用。通常,模型会为此函数提供一些唯一的标识符,以确保以后可以将模型索引与其对应的项目重新关联。

拖放支持和 MIME 类型处理

模型/视图类支持拖放操作,提供足以用于许多应用程序的默认行为。但是,也可以自定义拖放操作期间项目的编码方式,无论是复制项目还是移动项目以及如何将其插入现有模型。

另外,便捷视图类实现了专门的行为,该行为应与现有开发人员所期望的行为紧密相随。便捷视图 部分提供了此行为的概述。

MIME 数据

默认情况下,内置模型和视图使用内部 MIME 类型( application / x-qabstractitemmodeldatalist )来传递有关模型索引的信息。这将指定项目列表的数据,其中包含每个项目的行号和列号,以及有关每个项目支持的角色的信息。

MIME 类型编码的数据可以通过调用带有 QModelIndexList 参数的 QAbstractItemModel::mimeData() 函数获取。

在自定义模型中实现拖放支持时,可以通过重新实现以下函数以专用格式导出数据项:

mimeData()可以重新实现此函数,以返回默认 `application / x-qabstractitemmodeldatalist` 内部 `MIME` 类型以外的格式的数据。子类可以从基类获取默认的 QMimeData 对象,并以其他格式向其添加数据。

对于许多模型,以 MIME 类型表示的通用格式(例如 text / plain和image / png )提供项目的内容很有用。请注意,可以使用 QMimeData::setImageData()QMimeData::setColorDataQMimeData::setHtml() 函数轻松地将图像,颜色和 HTML 文档添加到 QMimeData 对象。

接受放置的数据

在视图上执行拖放操作时,将查询底层模型,以确定其支持的操作类型以及可以接受的 MIME 类型。此信息由 QAbstractItemModel::supportedDropActionsQAbstractItemModel::mimeTypes() 函数提供。不覆盖 QAbstractItemModel 提供的实现的模型支持复制操作和项的默认内部 MIME 类型。

当序列化的项目数据放到视图上时,使用其 QAbstractItemModel::dropMimeData() 实现将数据插入当前模型。该函数的默认实现将永远不会覆盖模型中的任何数据。而是尝试将数据项作为项的兄弟或项的子项插入。

要利用 QAbstractItemModel 的默认实现来实现内置的 MIME 类型,新模型必须提供以下函数的重新实现:

insertRows()这些函数使模型可以使用 QAbstractItemModel::dropMimeData() 提供的现有实现自动插入新数据。
insertColumns()
setData()允许用项目填充新的行和列。
setItemData()此函数为填充新项目提供了更有效的支持。

要接受其他形式的数据,必须重新实现以下函数:

supportedDropActions()用于返回 放置动作 的组合,指示模型接受的拖放操作的类型。
mimeTypes()用于返回可由模型解码和处理的 MIME 类型的列表。通常,支持输入模型的 MIME 类型与对数据进行编码以供外部组件使用时可以使用的 MIME 类型相同。
dropMimeData()对通过拖放操作传输的数据执行实际的解码,确定将在模型中设置的位置,并在必要时插入新的行和列。在子类中如何实现此函数取决于每个模型公开的数据的要求。

如果 dropMimeData() 函数的实现通过插入或删除行或列来更改模型的尺寸,或者如果修改了数据项,则必须注意确保发出所有相关信号。简单地调用子类中其他函数的重新实现(例如 setData()insertRows()insertColumns() )可能很有用,以确保模型表现一致。

为了确保拖动操作正常工作,重要的是重新实现以下从模型中删除数据的函数:

有关在项目视图中进行拖放的更多信息,请参阅 在项目视图中使用拖放

便捷视图

便捷视图(QListWidgetQTableWidgetQTreeWidget)将覆盖默认的拖放功能,以提供较不灵活但更自然的行为,适用于许多应用程序。例如,由于更常见的是将数据放入 QTableWidget 中的单元格中,用传输的数据替换现有内容,因此底层模型将设置目标项目的数据,而不是在模型中插入新的行和列。有关在方便视图中拖放的更多信息,请参阅 在项目视图中使用拖放

针对大量数据的性能优化

canFetchMore() 函数检查父级是否有更多可用数据,并相应地返回 truefalsefetchMore() 函数根据指定的父级获取数据。这两个函数可以组合在一起,例如,在涉及增量数据的数据库查询中组合以填充 QAbstractItemModel。我们重新实现 canFetchMore() 来指示是否有更多数据要提取,并根据需要使用 fetchMore() 来填充模型。

另一个示例是动态填充的树模型,当树模型中的分支扩展时,我们重新实现 fetchMore()

如果重新实现 fetchMore() 将行添加到模型中,则需要调用 beginInsertRows()endInsertRows()。另外,必须重新实现 canFetchMore()fetchMore(),因为它们的默认实现返回 false 并且不执行任何操作。

模型/视图类

这些类使用模型/视图设计模式,其中(模型中的)底层数据与用户(视图中)呈现和操作数据的方式保持分离。

说明
QAbstractItemDelegate用于显示和编辑模型中的数据项
QAbstractItemModel项目模型类的抽象接口
QAbstractItemView项目视图类的基本功能
QAbstractListModel可以被子类化创建一维列表模型的抽象模型
QAbstractProxyModel代理项目模型的基类,可以执行排序,过滤或其他数据处理任务
QAbstractTableModel可以被子类化创建表模型的抽象模型
QColumnView列视图的模型/视图实现
QConcatenateTablesProxyModel代理多个源模型,并连接它们的行
QDataWidgetMapper数据模型的一部分到部件之间的映射
QFileSystemModel本地文件系统的数据模型
QHeaderView项目视图的标题行或标题列
QIdentityProxyModel代理未经修改的源模型
QItemDelegate显示和编辑来自模型的数据项的工具
QItemEditorCreator无需子类 QItemEditorCreatorBase 即可创建项目编辑器创建者库
QItemEditorCreatorBase实现新项目编辑器创建者时必须子类化的抽象基类
QItemEditorFactory用于在视图和委托中编辑项目数据的部件
QItemSelection管理有关模型中选定项目的信息
QItemSelectionModel跟踪视图的选定项目
QItemSelectionRange管理有关模型中选定项目范围的信息
QListView模型上的列表或图标视图
QListWidget基于项目的列表部件
QListWidgetItemQListWidget 项目视图类一起使用的项目
QModelIndex用于在数据模型中定位数据
QPersistentModelIndex用于在数据模型中定位数据
QSortFilterProxyModel支持对在另一个模型和视图之间传递的数据进行排序和过滤
QStandardItemQStandardItemModel 类一起使用的项目
QStandardItemEditorCreator无需子类化 QItemEditorCreatorBase 即可注册部件的可能新
QStandardItemModel用于存储自定义数据的通用模型
QStringListModel向视图提供字符串的模型
QStyledItemDelegate显示和编辑来自模型的数据项的工具
QTableView表格视图的默认模型/视图实现
QTableWidget具有默认模型的基于项目的表视图
QTableWidgetItemQTableWidget 类一起使用的项目
QTableWidgetSelectionRange在不使用模型索引和选择模型的情况下与模型中的选择进行交互的方法
QTreeView树视图的默认模型/视图实现
QTreeWidget使用预定义树模型的树视图
QTreeWidgetItemQTreeWidget 便捷类一起使用的项目
QTreeWidgetItemIterator遍历 QTreeWidget 实例中的项目的方法

相关示例

另请参阅 项目视图拼图示例

模型视图教程

每个 UI 开发人员都应该了解模型视图编程,本教程的目的是让您轻松理解有关模型视图的相关知识。

表格,列表和树部件是 GUI 中经常使用的组件。这些部件可以通过两种不同的方式访问其数据。传统方式是,这些部件包含用于存储数据的内部容器。这种方法非常直观,但是,在许多应用程序中,它会导致数据同步问题。第二种方法是模型/视图编程,其中部件不维护内部数据容器。他们通过标准化接口访问外部数据,因此避免了数据重复。乍一看,这似乎很复杂,但是如果您仔细看一看,不仅容易掌握,而且模型/视图编程的许多好处也变得更加明显。

treeview

在此过程中,我们将了解 Qt 提供的一些基本技术,例如:

  • 标准部件和模型视图部件的区别
  • 窗体和模型之间的适配器
  • 开发一个简单的模型视图的应用程序
  • 预定义模型
  • 中间主题,比如:
    • 树形视图
    • 选项
    • 委托
    • 模型调试测试

您还将了解到新的应用程序是否可以通过模型/视图编程更容易地编写,或者经典的标准部件是否也可以工作。

本教程中的代码您可以编辑修改或者集成到您的项目中去。 源码在 Qtexamples/widgets/tutorials/modelview 目录。

详见 参考文档

1. 介绍

模型/视图是一种将数据从视图中分离出来,来处理数据集的一种技术。标准部件并不是为将数据从视图中分离出来而设计的,这就是为什么 Qt 会有两种不同类型的部件。这两种部件看起来都一样,但是它们与数据之间的交互方式是不同的。

标准部件使用的数据最为部件的一部分。

视图类操作的是外部的数据(模型)。

1.1 标准部件

让我们仔细看一下标准表部件。表格部件是用户可以更改的数据元素的 2D 数组。通过读取和写入表部件提供的数据元素,可以将表部件集成到程序流中。该方法非常直观,在许多应用程序中很有用,但是使用标准表窗口部件显示和编辑数据库表可能会出现问题。数据的两个副本必须协调一致:一个在部件外部;另一个在部件外部。开发人员负责同步两个版本。除此之外,表示和数据的紧密耦合使编写单元测试更加困难。

1.2 使用模型/视图解决

模型/视图提供了一个使用更通用体系结构的解决方案。模型/视图消除了标准控件可能出现的数据一致性问题。模型/视图还可以更容易地使用同一数据的多个视图,因为一个模型可以传递给多个视图。最重要的区别是模型/视图部件不在表单元格后面存储数据。实际上,它们直接根据您的数据进行操作。因为视图类不知道数据的结构,所以需要提供一个包装器,使数据符合 QAbstractItemModel 接口。视图使用此接口读取和写入数据。实现 QAbstractItemModel 类的任何实例都称为模型。一旦视图接收到指向模型的指针,它将读取和显示其内容,并成为其编辑器。

1.3 模型/视图部件概览

下面是模型/视图部件及其对应的标准部件。

部件标准部件
(基于项的便捷类)
模型/视图 视图类
(使用外部数据)
QListWidgetQListView
QTableWidgetQTableView
QTreeWidgetQTreeView
QColumnView s显示列表层次结构的树视图
QComboBox 既可以用作视图类,也可以用作传统部件

1.4 在表单和模型之间使用适配器

在表单和模型之间使用适配器非常方便。我们可以直接从表内部编辑存储在表中的数据,但是在文本字段中编辑数据更为方便。在对部件的一个数据而不是数据集操作时,模型/视图并没有提供对应的方法将数据和视图分离开。比如 QLineEditQCheckBox...因此我们需要一个适配器来将表单连接到数据源。

QDataWidgetMapper 是一个很好的解决方案,因为它可以将表单部件映射到表行,并且为数据库表构建表单非常容易。

widgetmapper

适配器的另一个例子是 QCompleterQt 使用 QCompleterQt 部件(如 QComboBox 和 如下图所示的 QLineEdit)中提供自动补全功能。QCompleter使用模型作为其数据源。

qcompleter

2. 一个简单的模型/视图应用程序

如果要开发模型/视图应用程序,应该从哪里开始?我们建议从一个简单的示例开始,并逐步扩展它。这样更容易理解模型/视图架构。事实证明,在调用 IDE 之前尝试详细了解模型/视图体系结构对于许多开发人员来说并不方便。从具有演示数据的简单模型/视图应用程序开始要容易得多。试试看!只需将以下示例中的数据替换为您自己的数据即可。

以下是7个非常简单和独立的应用程序,它们展示了模型/视图编程的不同方面。可以在 examples/widgets/tutorials/modelview 目录中找到源代码。

2.1 只读的表

我们从使用 QTableView 来显示数据的应用程序开始。稍后我们将添加编辑功能。

(文件: examples/widgets/tutorials/modelview/1_readonly/main.cpp)

// main.cpp
#include <QApplication>
#include <QTableView>
#include "mymodel.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTableView tableView;
    MyModel myModel;
    tableView.setModel(&myModel);
    tableView.show();
    return a.exec();
}

main() 函数:

这是有趣的部分:我们创建 MyModel 的实例并使用 tableView.setModel(&myModel);。 将其指针传递给 tableViewtableView 将调用它收到的指针的方法来找出两件事:

  • 需要显示多少行和列。
  • 每个单元格中应该显示什么内容。

模型需要一些代码来响应以上这些。

我们有一个表数据集,因此让我们从 QAbstractTableModel 开始,因为它比更通用的 QAbstractItemModel 更易于使用。

(文件:examples/widgets/tutorials/modelview/1_readonly/mymodel.h)

// mymodel.h
#include <QAbstractTableModel>

class MyModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    MyModel(QObject *parent = nullptr);
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
};

QAbstractTableModel 需要实现三个抽象方法。

(文件:examples/widgets/tutorials/modelview/1_readonly/mymodel.cpp)

// mymodel.cpp
#include "mymodel.h"

MyModel::MyModel(QObject *parent)
    : QAbstractTableModel(parent)
{
}

int MyModel::rowCount(const QModelIndex & /*parent*/) const
{
   return 2;
}

int MyModel::columnCount(const QModelIndex & /*parent*/) const
{
    return 3;
}

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    if (role == Qt::DisplayRole)
       return QString("Row%1, Column%2")
                   .arg(index.row() + 1)
                   .arg(index.column() +1);

    return QVariant();
}

行数和列数由 MyModel::rowCount()MyModel::columnCount() 提供。当视图必须知道单元格的文本是什么时,它将调用方法 MyModel::data()。行和列信息由参数 index 指定,并且角色设置为 Qt::DisplayRole。下一节将介绍其他角色。在我们的示例中,生成了应显示的数据。在实际的应用程序中,MyModel 会有一个名为 MyData 的成员,该成员充当所有读取和写入操作的目标。

这个小例子说明了模型的被动性质。该模型不知道何时使用它或需要哪些数据。每次视图请求时,它仅提供数据。

当需要更改模型数据时会发生什么?视图如何认识到数据已更改并且需要再次读取?该模型必须发射一个信号,该信号指示已更改了哪些单元格范围。这将在第2.3节中演示。

2.2 使用角色扩展只读表格示例

除了控制视图显示的文本之外,模型还控制文本的外观。当我们稍微改变模型时,我们得到以下结果:

readonlytable_role

实际上,除了 data() 方法外,无需更改其他任何内容即可设置字体,背景色,对齐方式和复选框。下面是产生上面所示结果的 data() 方法。不同之处在于,这次我们使用参数 int 角色根据其值返回不同的信息。

(文件: examples/widgets/tutorials/modelview/2_formatting/mymodel.cpp)

// mymodel.cpp
QVariant MyModel::data(const QModelIndex &index, int role) const
{
    int row = index.row();
    int col = index.column();
    // generate a log message when this method gets called
    qDebug() << QString("row %1, col%2, role %3")
            .arg(row).arg(col).arg(role);

    switch (role) {
    case Qt::DisplayRole:
        if (row == 0 && col == 1) return QString("<--left");
        if (row == 1 && col == 1) return QString("right-->");

        return QString("Row%1, Column%2")
                .arg(row + 1)
                .arg(col +1);
    case Qt::FontRole:
        if (row == 0 && col == 0) { //change font only for cell(0,0)
            QFont boldFont;
            boldFont.setBold(true);
            return boldFont;
        }
        break;
    case Qt::BackgroundRole:
        if (row == 1 && col == 2)  //change background only for cell(1,2)
            return QBrush(Qt::red);
        break;
    case Qt::TextAlignmentRole:
        if (row == 1 && col == 1) //change text alignment only for cell(1,1)
            return Qt::AlignRight + Qt::AlignVCenter;
        break;
    case Qt::CheckStateRole:
        if (row == 1 && col == 0) //add a checkbox to cell(1,0)
            return Qt::Checked;
        break;
    }
    return QVariant();
}

每个格式化属性将通过对 data() 方法的单独调用从模型中请求。角色参数用于让模型知道请求哪个属性:

枚举 Qt::ItemDataRole意义类型
Qt::DisplayRole文本QString
Qt::FontRole字体 QFont
BackgroundRole单元格背景的画笔QBrush
Qt::TextAlignmentRole文本对齐方式enum Qt::AlignmentFlag
Qt::CheckStateRole使用 QVariant() 取消复选框,使用 Qt::CheckedQt::UnChecked 设置复选框enum Qt::ItemDataRole

请参阅 Qt 明明控件文档了解有关 Qt::ItemDataRole 枚举功能的更多信息。

现在我们需要确定使用分离的模型如何影响应用程序的性能,因此让我们跟踪视图调用 data() 方法的频率。为了跟踪视图调用模型的频率,我们在 data() 方法中放置了一条调试语句,该语句记录到错误输出流中。在我们的小示例中,data() 将被调用 42 次。每次将光标悬停在该字段上时,都会再次调用 data(),每个单元格7次。这就是为什么在调用 data() 和缓存昂贵的查找操作时确保数据可用的重要原因。

2.3 表内的时钟

clock

我们仍然使用一个只读表,但是这次内容每秒更改一次,因为我们正在显示当前时间。

(文件:examples/widgets/tutorials/modelview/3_changingmodel/mymodel.cpp

QVariant MyModel::data(const QModelIndex &index, int role) const
{
    int row = index.row();
    int col = index.column();

    if (role == Qt::DisplayRole && row == 0 && col == 0)
        return QTime::currentTime().toString();

    return QVariant();
}

缺少一些东西来使时钟滴答作响。我们需要每秒告诉视图时间已更改,并且需要再次读取。我们用一个计时器来做到这一点。在构造函数中,我们将其间隔设置为1秒,然后连接其超时信号。

(文件:examples/widgets/tutorials/modelview/3_changingmodel/mymodel.cpp)

MyModel::MyModel(QObject *parent)
    : QAbstractTableModel(parent)
    , timer(new QTimer(this))
{
    timer->setInterval(1000);
    connect(timer, &QTimer::timeout , this, &MyModel::timerHit);
    timer->start();
}

(文件:examples/widgets/tutorials/modelview/3_changingmodel/mymodel.cpp

槽函数:

void MyModel::timerHit()
{
    //we identify the top left cell
    QModelIndex topLeft = createIndex(0,0);
    //emit a signal to make the view reread identified data
    emit dataChanged(topLeft, topLeft, {Qt::DisplayRole});
}

我们通过发射 dataChanged() 信号要求视图再次读取左上角单元格中的数据。请注意,我们没有将 dataChanged 信号显式连接到视图。这在我们调用 setModel 时自动发生。

2.4 设置行和列的标题

标题可以通过调用视图的一个方法被隐藏: tableView->verticalHeader()->hide();

modelview-header

但是,标题内容是通过模型设置的,因此我们重新实现 headerData() 方法:

(文件:examples/widgets/tutorials/modelview/4_headers/mymodel.cpp

QVariant MyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
        switch (section) {
        case 0:
            return QString("first");
        case 1:
            return QString("second");
        case 2:
            return QString("third");
        }
    }
    return QVariant();
}

请注意,方法 headerData() 也具有角色参数,该角色与 MyModel::data() 中的含义相同。

最小编辑示例

在此示例中,我们将构建一个应用程序,该应用程序通过重复输入表单元格中的值来自动用内容填充窗口标题。为了能够轻松访问窗口标题,我们将 QTableView 放在 QMainWindow 中。

该模型决定编辑功能是否可用。我们仅需修改模型即可启用可用的编辑功能。这可以通过重新实现以下虚函数来完成:setData()flags()

(文件:examples/widgets/tutorials/modelview/5_edit/mymodel.h

// mymodel.h
#include <QAbstractTableModel>
#include <QString>

const int COLS= 3;
const int ROWS= 2;

class MyModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    MyModel(QObject *parent = nullptr);
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
private:
    QString m_gridData[ROWS][COLS];  //holds text entered into QTableView
signals:
    void editCompleted(const QString &);
};

我们使用二维数组 QString m_gridData 来存储我们的数据。这使 m_gridData 成为 MyModel 的核心。MyModel 的其余部分就像包装器一样,将 m_gridData 调整为 QAbstractItemModel 接口。我们还引入了 editCompleted() 信号,这使得将修改后的文本传输到窗口标题成为可能。

(文件:examples/widgets/tutorials/modelview/5_edit/mymodel.cpp)

bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (role == Qt::EditRole) {
        if (!checkIndex(index))
            return false;
        //save value from editor to member m_gridData
        m_gridData[index.row()][index.column()] = value.toString();
        //for presentation purposes only: build and emit a joined string
        QString result;
        for (int row = 0; row < ROWS; row++) {
            for (int col= 0; col < COLS; col++)
                result += m_gridData[row][col] + ' ';
        }
        emit editCompleted(result);
        return true;
    }
    return false;
}

每次用户编辑单元格时都会调用 setData()index 参数告诉我们哪个字段已被编辑,value 提供了编辑过程的结果。该角色将始终设置为 Qt::EditRole,因为我们的单元格仅包含文本。如果存在一个复选框,并且将用户权限设置为允许选中该复选框,则还将以角色设置为 Qt::CheckStateRole 进行调用。

(文件:examples/widgets/tutorials/modelview/5_edit/mymodel.cpp)

Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
{
    return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
}

单元格的各种属性可以使用 flags() 进行调整。

返回 Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled 足以显示一个可被单元格选中的编辑器。

如果编辑一个单元格所修改的数据多于该特定单元格中的数据,则该模型必须发射 dataChanged() 信号,以便读取已更改的数据。

中间主题

3.1 树视图

您可以将上面的示例转换为具有树视图的应用程序。只需将 QTableView 替换为 QTreeView,这将生成一个读/写树。不必对模型进行任何更改。树不会有任何层次结构,因为模型本身没有任何层次结构。

dummy_tree

QListViewQTableViewQTreeView 均使用了合并了列表、表和树的模型抽象。这样就可以让不同类型的视图类使用同一模型。

list_table_tree

到目前为止,我们在示例中使用的模型都是这样子的:

example_model

我们想呈现一棵真正的树。在上面的示例中包装了数据,以建立模型。这次我们使用 QStandardItemModel ,这是用于具有层次结构的数据的容器,也实现了 QAbstractItemModel。要显示树,必须用 QStandardItem 填充 QStandardItemModelQStandardItem 可以保存项目的所有标准属性,例如文本,字体,复选框或笔刷。

tree_2_with_algorithm

(文件:examples/widgets/tutorials/modelview/6_treeview/mainwindow.cpp)

// modelview.cpp
#include "mainwindow.h"

#include <QTreeView>
#include <QStandardItemModel>
#include <QStandardItem>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , treeView(new QTreeView(this))
    , standardModel(new QStandardItemModel(this))
{
    setCentralWidget(treeView);

    QList<QStandardItem *> preparedRow = prepareRow("first", "second", "third");
    QStandardItem *item = standardModel->invisibleRootItem();
    // adding a row to the invisible root item produces a root element
    item->appendRow(preparedRow);

    QList<QStandardItem *> secondRow = prepareRow("111", "222", "333");
    // adding a row to an item starts a subtree
    preparedRow.first()->appendRow(secondRow);

    treeView->setModel(standardModel);
    treeView->expandAll();
}

QList<QStandardItem *> MainWindow::prepareRow(const QString &first,
                                              const QString &second,
                                              const QString &third) const
{
    return {new QStandardItem(first),
            new QStandardItem(second),
            new QStandardItem(third)};
}

我们只需实例化一个 QStandardItemModel 并向构造函数添加几个 QStandardItem。然后,我们可以建立分层数据结构,因为 QStandardItem 可以容纳其他 QStandardItem。节点在视图内折叠并展开。

3.2 处理选择

我们希望访问所选项目的内容,以便将其与层次结构级别一起输出到窗口标题中。

selection2

因此,让我们来创建几个项:

(文件: examples/widgets/tutorials/modelview/7_selections/mainwindow.cpp

#include "mainwindow.h"

#include <QTreeView>
#include <QStandardItemModel>
#include <QItemSelectionModel>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , treeView(new QTreeView(this))
    , standardModel(new QStandardItemModel(this))
{
    setCentralWidget(treeView);
    QStandardItem *rootNode = standardModel->invisibleRootItem();

    //defining a couple of items
    QStandardItem *americaItem = new QStandardItem("America");
    QStandardItem *mexicoItem =  new QStandardItem("Canada");
    QStandardItem *usaItem =     new QStandardItem("USA");
    QStandardItem *bostonItem =  new QStandardItem("Boston");
    QStandardItem *europeItem =  new QStandardItem("Europe");
    QStandardItem *italyItem =   new QStandardItem("Italy");
    QStandardItem *romeItem =    new QStandardItem("Rome");
    QStandardItem *veronaItem =  new QStandardItem("Verona");

    //building up the hierarchy
    rootNode->    appendRow(americaItem);
    rootNode->    appendRow(europeItem);
    americaItem-> appendRow(mexicoItem);
    americaItem-> appendRow(usaItem);
    usaItem->     appendRow(bostonItem);
    europeItem->  appendRow(italyItem);
    italyItem->   appendRow(romeItem);
    italyItem->   appendRow(veronaItem);

    //register the model
    treeView->setModel(standardModel);
    treeView->expandAll();

    //selection changes shall trigger a slot
    QItemSelectionModel *selectionModel = treeView->selectionModel();
    connect(selectionModel, &QItemSelectionModel::selectionChanged,
            this, &MainWindow::selectionChangedSlot);
}

视图在单独的选择模型中管理选择,可以使用 selectionModel() 方法进行检索。我们检索选择模型,以便将槽函数连接到其 selectionChanged() 信号。

(文件: examples/widgets/tutorials/modelview/7_selections/mainwindow.cpp)

void MainWindow::selectionChangedSlot(const QItemSelection & /*newSelection*/, const QItemSelection & /*oldSelection*/)
{
    //get the text of the selected item
    const QModelIndex index = treeView->selectionModel()->currentIndex();
    QString selectedText = index.data(Qt::DisplayRole).toString();
    //find out the hierarchy level of the selected item
    int hierarchyLevel = 1;
    QModelIndex seekRoot = index;
    while (seekRoot.parent() != QModelIndex()) {
        seekRoot = seekRoot.parent();
        hierarchyLevel++;
    }
    QString showString = QString("%1, Level %2").arg(selectedText)
                         .arg(hierarchyLevel);
    setWindowTitle(showString);
}

我们通过调用 treeView->selectionModel()->currentIndex() 来获得与选择相对应的模型索引,并通过使用模型索引来获取字段的字符串。然后我们只计算项目的hierarchyLevel。顶级项没有父级,并且 parent() 方法将返回默认构造的 QModelIndex()。这就是为什么我们在迭代计算期间使用 parent() 方法迭代到最高级别的原因。

可以检索选择模型(如上所示),但也可以使用 QAbstractItemView::setSelectionModel 进行设置。这样就可以拥有3个具有同步选择的视图类,因为仅使用了一个选择模型实例。要在3个视图之间共享选择模型,请使用 selectionModel() 并通过 setSelectionModel 将结果分配给第二个和第三个视图类。

3.3 预定义模型

使用模型/视图的典型方法是包装特定数据以使其可用于视图类。但是,Qt 还为常见的底层数据结构提供了预定义的模型。如果可用的数据结构之一适合您的应用程序,那么预定义的模型可能是一个不错的选择。

QStringListModel存储字符串列表
QStandardItemModel存储任意分层结构的项目
QFileSystemModel
QDirModel
封装本地文件系统
QSqlQueryModel封装 SQL 结果集
QSqlTableModel封装 SQL 表
QSqlRelationalTableModel封装带外键的 SQL 表
QSortFilterProxyModel排序或者过滤其他模型

3.4 委托

到目前为止,在所有示例中,数据均以文本或复选框形式显示在单元格中,并以文本或复选框的形式进行编辑。提供这些表示和编辑服务的组件称为委托。我们只是刚刚开始使用委托,因为视图使用了默认委托。但是,假设我们想要一个不同的编辑器(例如,一个滑块或一个下拉列表),或者想象我们想要将数据显示为图形。让我们看一个名为 Star Delegate 的示例,其中的星星用于显示等级:

stardelegate

该视图具有 setItemDelegate() 方法,该方法将替换默认委托并安装自定义委托。可以通过创建从 QStyledItemDelegate 继承的类来编写新的委托。为了编写一个显示星星但没有输入功能的委托,我们只需要重写2个方法即可。

class StarDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    StarDelegate(QWidget *parent = 0);
    void paint(QPainter *painter, const QStyleOptionViewItem &option,
               const QModelIndex &index) const;
    QSize sizeHint(const QStyleOptionViewItem &option,
                   const QModelIndex &index) const;
};

paint() 根据底层数据的内容绘制星形。可以通过调用 index.data() 查找数据。委托的 sizeHint() 方法用于获取每个星星的尺寸,因此该单元格将提供足够的高度和宽度来容纳星星。

如果要在视图类的网格内以自定义图形显示数据,则编写自定义委托是正确的选择。如果要离开网格,则可以不使用自定义委托,而使用自定义视图类。

Qt 文档中关于委托的其他参考:

3.5 使用 ModelTest 调试

模型的被动性质为程序员带来了新的挑战。模型中的不一致会导致应用程序崩溃。由于该模型会被视图中调用无数次,因此很难找出哪个调用使应用程序崩溃了,以及哪个操作导致了该问题。

Qt Labs 提供了一个名为 ModelTest 的软件,该软件可在您的程序运行时检查模型。每次更改模型时,ModelTest 都会扫描模型并使用断言报告错误。这对于树模型尤其重要,因为它们的层次结构性质即使是细微的不一致也会留下许多可能性。

与视图类不同,ModelTest 使用超出范围的索引来测试模型。这意味着您的应用程序可能会因 ModelTest 而崩溃,即使没有它也可以完美运行。因此,在使用 ModelTest 时,您还需要处理所有超出范围的索引。

4 附录

4.1 书籍

模型/视图编程在 Qt 文档中有相当广泛的介绍,但也有好几本好书。

  1. C++ GUI Programming with Qt 4 / Jasmin Blanchette, Mark Summerfield, Prentice Hall, 2nd edition, ISBN 0-13-235416-0. Also available in German: C++ GUI Programmierung mit Qt 4: Die offizielle Einführung, Addison-Wesley, ISBN 3-827327-29-6
  2. The Book of Qt4, The Art of Building Qt Applications / Daniel Molkentin, Open Source Press, ISBN 1-59327-147-6. Translated from Qt 4, Einführung in die Applikationsentwicklung, Open Source Press, ISBN 3-937514-12-0.
  3. Foundations of Qt Development / Johan Thelin, Apress, ISBN 1-59059-831-8.
  4. Advanced Qt Programming / Mark Summerfield, Prentice Hall, ISBN 0-321-63590-6. This book covers Model/View programming on more than 150 pages.

以下列表概述了上面列出的前三本书中包含的示例程序。其中一些为开发类似应用程序提供了非常好的模板。

示例名称使用的视图类使用的模型涵盖的方面 
Team LeadersQListviewQStringListModelBook 1, Chapter 10, Figure 10.6
Directory ViewerQTreeViewQDirModelBook 1, Chapter 10, Figure 10.7
Color NamesQListView应用于 QStringListModelQSortFilterProxyModelBook 1, Chapter 10, Figure 10.8
CurrenciesQTableView基于 QAbstractTableModel 的自定义模型 只读 Book 1, Chapter 10, Figure 10.10 
Cities QTableView基于 QAbstractTableModel 的自定义模型 读/写Book 1, Chapter 10, Figure 10.12
Boolean ParserQTreeView基于 QAbstractTableModel 的自定义模型只读 Book 1, Chapter 10, Figure 10.14
Track EditorQTableWidget自定义的委托并提供了自定义的编辑器 Book 1, Chapter 10, Figure 10.15
Four directory viewsQListView QTableView QTreeViewQDirModel演示使用多个视图Book2, Chapter 8.2
Address BookQListView QTableView QTreeView基于 QAbstractTableModel 的自定义模型读/写 Book2, Chapter 8.4
Address Book with sorting QSortfilterProxyModel引入排序和过滤功能Book2, Chapter 8.5
Address Book with checkboxes在模型/视图中引入复选框Book2, Chapter 8.6
Address Book with transposed grid基于 QAbstractProxyModel 的自定义代理模型引入自定义模型Book2, Chapter 8.7
Address Book with drag and drop引入托拖放支持Book2, Chapter 8.8
Address Book with custom editor引入自定义委托Book2, Chapter 8.9
ViewsQListView QTableView QTreeViewQStandardItemModel只读Book 3, Chapter 5, figure 5-3
BardelegateQTableView演示基于 QAbstractItemDelegate 的自定义委托Book 3, Chapter 5, figure 5-5
EditdelegateQTableView基于 QAbstractItemDelegate 的用于编辑的自定义委托Book 3, Chapter 5, figure 5-6
Singleitemview基于 QAbstractItemView 的自定义视图自定义视图Book 3, Chapter 5, figure 5-7
listmodelQTableView基于 QAbstractTableModel 的自定义模型只读Book 3, Chapter 5, Figure 5-8
treemodelQTreeView基于 QAbstractTableModel 的自定义模型只读Book 3, Chapter 5, Figure 5-10
edit integers[QListView]基于 QAbstractTableModel 的自定义模型读/写Book 3, Chapter 5, Listing 5-37, Figure 5-11
sortingQTableView应用于 QStringListModelQSortFilterProxyModel演示排序Book 3, Chapter 5, Figure 5-12

4.2 Qt 文档

Qt5.0提供了19个模型/视图示例。这些示例可以在 项目视图示例 页面上找到。

示例名称使用的视图类使用的模型涵盖的方面 
Address BookQTableViewQAbstractTableModel QSortFilterProxyModel使用QSortFilterProxyModel从一个数据池生成不同的子集
Basic Sort/Filter ModelQTreeViewQStandardItemModel QSortFilterProxyModel
Chart自定义视图QStandardItemModel设计与选择模型协作的自定义视图
Color Editor FactoryQTableWidget扩展一个带有新的自定义的编辑器的委托来选择颜色
Combo Widget Mapper使用 [QDataWidgetMapper] () 映射 QLineEditQTextEditQComboBoxQStandardItemModel演示了QComboBox如何作为视图类
Custom Sort/Filter ModelQTreeViewQStandardItemModel QSortFilterProxyModel子类化 QSortFilterProxyModel 用于高级排序和过滤
Dir ViewQTreeViewQFileSystemModel演示如何将模型指定给视图的非常小的示例
Editable Tree ModelQTreeView自定义树模型使用树的综合示例,演示了如何使用底层自定义模型编辑单元格和树结构
Fetch MoreQListView自定义列表模型动态变化的模型
Frozen ColumnQTableViewQStandardItemModel
InterviewMultiple 自定义项目模型多个视图
PixelatorQTableView自定义表模型实现一个自定义委托
PuzzleQListView自定义列表模型支持拖放的模型/视图
Simple DOM ModelQTreeView 自定义树模型自定义的只读的树模型的示例
Simple Tree ModelQTreeView自定义树模型自定义的只读的树模型的示例
Simple Widget Mapper使用 QDataWidgetMapper 映射 QLineEditQTextEditQSpinBoxQStandardItemModelQDataWidgetMapper 的基本用法
Spin Box DelegateQTableViewQStandardItemModel使用旋转框作为单元格编辑器的自定义委托
SpreadsheetQTableView自定义委托 
Star DelegateQTableWidget功能齐全的自定义委托示例

还提供了模型/视图技术的参考文档

QMainWindow 类

QMainWindow 类用于创建主程序窗口。 更多内容...

属性方法
头文件:#include <QMainWindow>
qmake:QT += widgets
继承:QWidget

公共成员类型

类型方法
enumDockOption { AnimatedDocks, AllowNestedDocks, AllowTabbedDocks, ForceTabbedDocks, VerticalTabs, GroupedDragging }
flagsDockOptions

属性

属性名类型
animatedbool
dockNestingEnabledbool
dockOptionsDockOptions
documentModebool
iconSizeQSize
tabShapeQTabWidget::TabShape
toolButtonStyleQt::ToolButtonStyle
unifiedTitleAndToolBarOnMacbool

公共成员函数

返回类型函数名
QMainWindow (QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags())
virtual~QMainWindow ()
voidaddDockWidget (Qt::DockWidgetArea area, QDockWidget *dockwidget)
voidaddDockWidget (Qt::DockWidgetArea area, QDockWidget *dockwidget, Qt::Orientation orientation)
voidaddToolBar (Qt::ToolBarArea area, QToolBar *toolbar)
voidaddToolBar (QToolBar *toolbar)
QToolBar *addToolBar (const QString &title)
voidaddToolBarBreak (Qt::ToolBarArea area = Qt::TopToolBarArea)
QWidget *centralWidget () const
Qt::DockWidgetAreacorner (Qt::Corner corner) const
virtual QMenu *createPopupMenu ()
QMainWindow::DockOptionsdockOptions () const
Qt::DockWidgetAreadockWidgetArea (QDockWidget *dockwidget) const
booldocumentMode () const
QSizeiconSize () const
voidinsertToolBar (QToolBar *before, QToolBar *toolbar)
voidinsertToolBarBreak (QToolBar *before)
boolisAnimated () const
boolisDockNestingEnabled () const
QMenuBar *menuBar () const
QWidget *menuWidget () const
voidremoveDockWidget (QDockWidget *dockwidget)
voidremoveToolBar (QToolBar *toolbar)
voidremoveToolBarBreak (QToolBar *before)
voidresizeDocks (const QList<QDockWidget *> &docks, const QList<int> &sizes, Qt::Orientation orientation)
boolrestoreDockWidget (QDockWidget *dockwidget)
boolrestoreState (const QByteArray &state, int version = 0)
QByteArraysaveState (int version = 0) const
voidsetCentralWidget (QWidget *widget)
voidsetCorner (Qt::Corner corner, Qt::DockWidgetArea area)
voidsetDockOptions (QMainWindow::DockOptions options)
voidsetDocumentMode (bool enabled)
voidsetIconSize (const QSize &iconSize)
voidsetMenuBar (QMenuBar *menuBar)
voidsetMenuWidget (QWidget *menuBar)
voidsetStatusBar (QStatusBar *statusbar)
voidsetTabPosition (Qt::DockWidgetAreas areas, QTabWidget::TabPosition tabPosition)
voidsetTabShape (QTabWidget::TabShape tabShape)
voidsetToolButtonStyle (Qt::ToolButtonStyle toolButtonStyle)
voidsplitDockWidget (QDockWidget *first, QDockWidget *second, Qt::Orientation orientation)
QStatusBar *statusBar () const
QTabWidget::TabPositiontabPosition (Qt::DockWidgetArea area) const
QTabWidget::TabShapetabShape () const
QList<QDockWidget *>tabifiedDockWidgets (QDockWidget *dockwidget) const
voidtabifyDockWidget (QDockWidget *first, QDockWidget *second)
QWidget *takeCentralWidget ()
Qt::ToolBarAreatoolBarArea (QToolBar *toolbar) const
booltoolBarBreak (QToolBar *toolbar) const
Qt::ToolButtonStyletoolButtonStyle () const
boolunifiedTitleAndToolBarOnMac () const

公共槽

返回类型函数名
voidsetAnimated (bool enabled)
voidsetDockNestingEnabled (bool enabled)
voidsetUnifiedTitleAndToolBarOnMac (bool set)

信号

返回类型函数名
voidiconSizeChanged (const QSize &iconSize)
voidtabifiedDockWidgetActivated (QDockWidget *dockWidget)
voidtoolButtonStyleChanged (Qt::ToolButtonStyle toolButtonStyle)

保护成员函数

返回类型函数名
virtual voidcontextMenuEvent (QContextMenuEvent *event) override
virtual boolevent (QEvent *event) override

详细描述

Qt 主窗口框架

主窗口提供了一套创建应用程序用户界面的框架。 Qt 用QMainWindow相关类 来管理主窗口。QMainWindow已经定义了一个布局,可以往里添加一些 QToolBarQDockWidget,也可以添加一个 QMenuBar 和一个 QStatusBar。这个布局有一个中央区域,可以放任意部件。如下图所示:

main window layout

注意 主窗口必须设置中央部件。

创建主窗口组件

中央部件通常是标准 Qt 部件,如 QTextEditQGraphicsView,也可自定义部件。用setCentralWidget()来设置中央部件。

主窗口可以是单文档界面或多文档界面。 Qt 中设置 QMdiArea 为中央部件即创建了多文档界面。

下面举例说明主窗口可以添加的部件。

创建菜单

Qt 用 QMenu 类实现菜单,主窗口将其放在 QMenuBar。可以添加 QActionQMenu,一个QAction代表菜单中的一个条目。

menuBar()可以得到主窗口的菜单栏,用 QMenuBar::addMenu 添加菜单。

QMainWindow 默认有一个菜单栏,可以用setMenuBar()自定义一个新的菜单栏。如果不想用 QMenuBar ,也可以用setMenuWidget()来定制菜单栏。

创建菜单代码示例:

    void MainWindow::createMenus()
    {
        fileMenu = menuBar()->addMenu(tr("&File"));
        fileMenu->addAction(newAct);
        fileMenu->addAction(openAct);
        fileMenu->addAction(saveAct);
    }

createPopupMenu()可以创建弹出式菜单,它会在主窗口收到 context menu 事件时弹出。 停靠部件和菜单栏默认实现了右键菜单,可以重写createPopupMenu()创建自定义菜单。

创建工具栏

Qt 用 QToolBar 类实现工具栏,可以用addToolBar()添加工具栏到主窗口。

可以设置 Qt::ToolBarArea 来控制工具栏的初始位置。可以用addToolBarBreak()insertToolBarBreak()分割工具栏所在的区域,前者可使接下来添加的工具栏换至新的一行,后者添加了一个工具栏分隔符。用 QToolBar::setAllowedAreasQToolBar::setMovable 可以限制用户放工具栏的位置。

工具栏图标的尺寸可以用iconSize()获取,它是平台相关的。可以用setIconSize()设置固定尺寸。用setToolButtonStyle()可以修改工具栏图标外观。

创建工具栏代码示例:

    void MainWindow::createToolBars()
    {
        fileToolBar = addToolBar(tr("File"));
        fileToolBar->addAction(newAct);
    }

创建停靠部件

Qt 用 QDockWidget 类实现停靠部件。停靠部件即可以停靠在主窗口的窗口,可以用addDockWidget()添加停靠部件到主窗口。

可以设置 Qt::DockWidgetArea 来控制停靠部件的位置,有上、下、左、右四种。setCorner()可以让一个角落属于某个相邻的区域。默认情况下,一个区域只有一个停靠部件,用setDockNestingEnabled()可以使其能上下或者左右排列多个停靠部件。

两个停靠部件也可以堆叠在一起,然后使用 QTabBar 来选择应显示哪个部件。

创建并添加停靠部件到主窗口的代码示例:

    QDockWidget *dockWidget = new QDockWidget(tr("Dock Widget"), this);
    dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
    dockWidget->setWidget(dockWidgetContents);
    addDockWidget(Qt::LeftDockWidgetArea, dockWidget);

状态栏

setStatusBar()可以设置状态栏,调用statusBar()会返回主窗口的状态栏。查看 QStatusBar 获取更多内容。

存储状态

QMainWindow 用saveState()保存布局,用restoreState()恢复布局。包括工具栏和停靠部件的位置和相对主窗口的尺寸。

另请参阅 QMenuBarQToolBarQStatusBar, QDockWidgetApplication ExampleDock Widgets ExampleMDI ExampleSDI ExampleMenus Example

成员变量文档

enum QMainWindow::DockOption flags QMainWindow::DockOptions

该枚举包含指定 QMainWindow 停靠行为的标志。

函数描述
QMainWindow::AnimatedDocks0x01animated 属性
QMainWindow::AllowNestedDocks0x02dockNestingEnabled 属性
QMainWindow::AllowTabbedDocks0x04允许形成下方有 tabBar 的重合部件
QMainWindow::ForceTabbedDocks0x08每个停靠区域都只包含一个选项卡式停靠部件。换句话说,停靠区域里不能上下或左右排列停靠部件。如果设置了此选项,则 AllowNestedDocks 不起作用。
QMainWindow::VerticalTabs0x10设置 tabBar 在竖直左方位置(默认在下方),包含了 AllowTabbedDocks。另请参阅 setTabPosition ()
QMainWindow::GroupedDragging0x20拖动停靠部件的标题栏时,将拖动所有和它在一起的停靠部件。包含了 AllowTabbedDocks 。在某些停靠部件有区域限制时会有问题。(该枚举值在 Qt 5.6 添加)

这些选项只是控制停靠部件将如何放入 QMainWindow,不会重新排列停靠部件。所以要先给停靠部件设置这些选项,再添加到主窗口。AnimatedDocks 和 VerticalTabs 选项例外,它们可以随后设置。

此枚举值在 Qt 4.3 引入和修改

DockOptions 是 QFlags<DockOption> 的 typedef。它存储 DockOption 值的 OR 组合。

属性文档

animated : bool

此属性表示是否对操作停靠部件和工具栏进行动画处理

当停靠部件或工具栏在主窗口拖动时,主窗口会显示它们将停靠在什么位置。设置此属性使 QMainWindow 在平滑动画中移动其内容。清除此属性会使它们啪地进入新位置。

默认情况,此属性是生效的。如果主窗口调整尺寸或重绘太慢,可能会无效。

设置此属性与使用 setDockOptions () 设置 AnimatedDocks 选项相同。

该属性在 Qt 4.2 引入

存取函数:

返回类型函数名
boolisAnimated() const
voidsetAnimated(bool enabled)

dockNestingEnabled : bool

此属性表示停靠部件是否可以嵌套。

如果此属性为false,停靠区域只能是水平的或垂直的一行。如果此属性为true,停靠区域所占的区域可以沿任意方向拆分以包含更多的停靠部件。

译者注:如果设为 true,两个 dock 可以上面一块,下面一块,显示在一个区域里,如果设为 false,则两个 dock 只能变成选项卡式占用一个区域。

只有在包含大量停靠部件的应用程序中,才需要停靠嵌套。它给用户更大的自由来组织主窗口。但是,当停靠部件拖过主窗口时,停靠嵌套会导致更加复杂和不太直观的行为。

设置此属性与使用 setDockOptions () 设置 AllowNestedDocks 选项相同。

该属性在 Qt 4.2 引入

存取函数:

返回类型函数名
boolisDockNestingEnabled() const
voidsetDockNestingEnabled(bool enabled)

dockOptions : DockOptions

此属性表示 QMainWindow 的停靠行为

默认值是 AnimatedDocks | AllowTabbedDocks

该属性在 Qt 4.3 引入

存取函数:

返回类型函数名
QMainWindow::DockOptionsdockOptions() const
voidsetDockOptions(QMainWindow::DockOptions options)

documentMode : bool

此属性决定选项卡式停靠部件的选项卡栏是否为文档模式。

默认为 false

该属性在 Qt 4.5 引入

存取函数:

返回类型函数名
booldocumentMode() const
voidsetDocumentMode(bool enabled)

另请参阅 QTabBar::documentMode

iconSize : QSize

主窗口工具栏图标的尺寸。

默认是 GUI 样式的默认工具栏图标大小。请注意,使用的图标必须至少具有此大小,因为图标只会缩小。

存取函数:

返回类型函数名
QSizeiconSize() const
voidsetIconSize(const QSize &iconSize)

tabShape : QTabWidget::TabShape

此属性表示选项卡式停靠部件的选项卡形状。

默认是 QTabWidget::Rounded

该属性在 Qt 4.5 引入

存取函数:

返回类型函数名
QTabWidget::TabShapetabShape() const
voidsetTabShape(QTabWidget::TabShape tabShape)

另请参阅 setTabPosition ()

toolButtonStyle : Qt::ToolButtonStyle

主窗口的工具栏按钮样式。

若要使工具按钮的样式遵循系统设置,请将此属性设置为 Qt::ToolButtonFollowStyle。在 Unix 上,将使用桌面环境变量中的用户设置。在其他平台上,Qt::ToolButtonFollowStyle 意思是仅显示图标。

默认是 Qt::ToolButtonIconOnly

存取函数:

返回类型函数名
Qt::ToolButtonStyletoolButtonStyle() const
voidsetToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle)

unifiedTitleAndToolBarOnMac : bool

此属性表示窗口是否使用 macOS 上的统一标题和工具栏外观

请注意,与 Qt 4 相比,Qt 5 实现有几个限制:

  • 不支持在包含 OpenGL 内容的窗口中使用。这包括 QGLWidget 和 QOpenGLWidget
  • 使用可停靠或可移动工具栏可能会导致绘制错误,不建议使用。

该属性在 Qt 5.2 引入

存取函数:

返回类型函数名
boolunifiedTitleAndToolBarOnMac() const
voidsetUnifiedTitleAndToolBarOnMac(bool set)

成员函数文档

QMainWindow::QMainWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags())

主窗口构造函数,指定 parentflags

主窗口设置了 Qt::Window 标志,因此它总作为顶层窗口被创建。

[signal]void QMainWindow::iconSizeChanged(const QSize &iconSize)

当窗口图标被改变时,将发出此信号。新图标的尺寸为 iconSize

您可以将此信号连接到其他组件,以帮助保持应用程序的一致外观。

另请参阅 setIconSize ().

[signal]void QMainWindow::tabifiedDockWidgetActivated(QDockWidget *dockWidget)

当通过选择选项卡激活停靠部件时,将发出此信号。 dockWidget 表示激活的停靠部件。

该函数在 Qt 5.8 引入

另请参阅 tabifyDockWidget() and tabifiedDockWidgets()

[signal]void QMainWindow::toolButtonStyleChanged(Qt::ToolButtonStyle toolButtonStyle)

当窗口的工具栏按钮样式更改时,将发出此信号。toolButtonStyle 表示新样式。

您可以将此信号连接到其他组件,以帮助保持应用程序的一致外观。

另请参阅 setToolButtonStyle ()

[virtual]QMainWindow::~QMainWindow()

销毁主窗口

void QMainWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget)

添加指定 dockwidget 到指定 area.

void QMainWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget, Qt::Orientation orientation)

dockwidget 添加到指定的 area,方向由 orientation 指定。

void QMainWindow::addToolBar(Qt::ToolBarArea area, QToolBar *toolbar)

在主窗口中将 toolbar 添加到指定的 areatoolbar 放置在当前工具栏块(比如分隔符)的后面。如果主窗口已管理 toolbar,则只会将工具栏移动到 area

另请参阅 insertToolBar (),addToolBarBreak () 和 insertToolBarBreak ()

void QMainWindow::addToolBar(QToolBar *toolbar)

这是一个重载函数。

相当于调用 addToolBar(Qt::TopToolBarArea, toolbar)

QToolBar *QMainWindow::addToolBar(const QString &title)

这是一个重载函数。

创建 QToolBar 的一个对象,设置它的窗口标题为 title 并将它添加到上方的工具栏区域。

另请参阅 setWindowTitle ()

void QMainWindow::addToolBarBreak(Qt::ToolBarArea area = Qt::TopToolBarArea)

添加一个工具栏 break。这时,新添加的工具条将不再紧跟前一个工具条,而是另起一行。

QWidget *QMainWindow::centralWidget() const

返回主窗口的中央部件。如果尚未设置中央部件,则此函数返回零。

另请参阅 setCentralWidget ()

[override virtual protected]void QMainWindow::contextMenuEvent(QContextMenuEvent *event)

重写:QWidget::contextMenuEvent (QContextMenuEvent *event)

Qt::DockWidgetArea QMainWindow::corner(Qt::Corner corner) const

返回占用指定 corner 的停靠部件区域。

另请参阅 setCorner ()

[virtual]QMenu *QMainWindow::createPopupMenu()

返回一个弹出式菜单,其中包含主窗口中存在的工具栏和停靠部件的可选中条目。如果没有工具栏和停靠小部件,则此函数将返回nullptr

默认情况下,当用户激活上下文菜单时,主窗口会调用此函数,通常通过右键单击工具栏或停靠部件。

如果要创建自定义弹出式菜单,请重新实现此功能并返回新创建的弹出式菜单。弹出式菜单的所有权将传输到调用方。

另请参阅 addDockWidget (),addToolBar () 和 menuBar ()

Qt::DockWidgetArea QMainWindow::dockWidgetArea(QDockWidget *dockwidget) const

返回 dockwidgetQt::DockWidgetArea。如果 dockwidget 没有被加入主窗口,此函数返回Qt::NoDockWidgetArea

另请参阅 addDockWidget (),splitDockWidget () 和 Qt::DockWidgetArea

[override virtual protected]bool QMainWindow::event(QEvent *event)

重写:QWidget::event (QEvent *event)

void QMainWindow::insertToolBar(QToolBar *before, QToolBar *toolbar)

toolbar 插入到 before 工具栏占用的区域之前。例如,在正常的从左到右布局操作中,toolbar 将显示在水平工具栏区域中 before 工具栏的左侧。

另请参阅 insertToolBarBreak (),addToolBar () 和 addToolBarBreak ()

void QMainWindow::insertToolBarBreak(QToolBar *before)

before 工具栏左侧插入一个工具栏 break。

QMenuBar *QMainWindow::menuBar() const

返回主窗口的菜单栏。如果菜单栏不存在,则此函数将创建并返回一个空菜单栏。

如果希望 Mac 应用程序中的所有窗口共享一个菜单栏,请不要使用此函数来创建它,因为此处创建的菜单栏将把 QMainWindow 作为它的父对象。必须创建一个没有父对象的菜单栏,然后可以在所有 Mac 窗口之间共享该菜单栏。这样创建:

QMenuBar *menuBar = new QMenuBar(nullptr);

另请参阅 setMenuBar ()

QWidget *QMainWindow::menuWidget() const

返回主窗口的菜单栏。如果尚未构造菜单栏,返回 null。

该函数在 Qt 4.2 引入

另请参阅 setMenuWidget ()

void QMainWindow::removeDockWidget(QDockWidget *dockwidget)

从主窗口布局中移除 dockwidget 并将其隐藏。注意,dockwidget 没有被 delete

void QMainWindow::removeToolBar(QToolBar *toolbar)

从主窗口布局中移除 toolbar 并将其隐藏。注意,toolbar 没有被 delete

void QMainWindow::removeToolBarBreak(QToolBar *before)

删除 before 工具栏之前的一个工具栏 break。

void QMainWindow::resizeDocks(const QList<QDockWidget *> &docks, const QList<int> &sizes, Qt::Orientation orientation)

docks 列表中的停靠部件调整为 sizes 列表中的相应尺寸(以像素为单位)。如果 orientationQt::Horizontal,则调整宽度,否则调整高度。尺寸会调整,以遵守设置的最大尺寸和最小尺寸,并且 QMainWindow 本身不会调整大小。任何多余或缺少的空间将根据尺寸的相对权重分布在部件之间。

示例:

    resizeDocks({blueWidget, yellowWidget}, {20 , 40}, Qt::Horizontal);

如果蓝色和黄色部件嵌套在同一级别上,它们将被调整大小,使黄色部件是蓝色部件的两倍大

如果某些部件在选项卡中分组,则每个组只应指定一个部件。不在列表中的部件可能会改变以遵守约束。

该函数在 Qt 5.6 引入

bool QMainWindow::restoreDockWidget(QDockWidget *dockwidget)

如果在调用 restoreState () 后创建 dockwidget 的状态,则恢复状态。如果状态已恢复,则返回true;否则返回false

另请参阅 restoreState () 和 saveState ()

bool QMainWindow::restoreState(const QByteArray &state, int version = 0)

还原主窗口工具栏和停靠部件的 state。也恢复 corner 的设置。version 编号与存储在 state 中的号码进行比较。如果它们不匹配,主窗口的状态保持不变,并且函数将返回false;否则,状态将恢复,函数返回 true

若要恢复用 QSettings 保存的窗口 geometry,可以这么写:

void MainWindow::readSettings()
{
    QSettings settings("MyCompany", "MyApp");
    restoreGeometry(settings.value("myWidget/geometry").toByteArray());
    restoreState(settings.value("myWidget/windowState").toByteArray());
}

另请参阅 saveState (),QWidget::saveGeometry (),QWidget::restoreGeometry () 和 restoreDockWidget ()

QByteArray QMainWindow::saveState(int version = 0) const

保存主窗口工具栏和停靠部件的当前状态。这包括用 setCorner () 设置的角落区域。version 作为数据的一部分存储。

objectName 属性用于标识每个 QToolBarQDockWidget。您应该确保此属性对于添加到 QMainWindow 的每个 QToolBarQDockWidget 是唯一的。

要还原保存的状态,请传递返回值和 versionrestoreState ()。

若要在窗口关闭时保存 geometry,可以这样实现关闭事件:

void MyMainWindow::closeEvent(QCloseEvent *event)
{
    QSettings settings("MyCompany", "MyApp");
    settings.setValue("geometry", saveGeometry());
    settings.setValue("windowState", saveState());
    QMainWindow::closeEvent(event);
}

另请参阅 restoreState (), QWidget::saveGeometry () 和 QWidget::restoreGeometry ()

void QMainWindow::setCentralWidget(QWidget *widget)

将主窗口的中央部件设置为 widget

注意: QMainWindow 拥有 widget 指针的所有权,会在适当的时间将其删除。

另请参阅 centralWidget ()

void QMainWindow::setCorner(Qt::Corner corner, Qt::DockWidgetArea area)

用停靠部件 area 占据指定的 corner

另请参阅 corner ()

void QMainWindow::setMenuBar(QMenuBar *menuBar)

将主窗口的菜单栏设置为 menuBar

注意: QMainWindow 拥有 menuBar 指针的所有权,会在适当的时间将其删除。

另请参阅 menuBar ()

void QMainWindow::setMenuWidget(QWidget *menuBar)

将主窗口的菜单栏设置为 menuBar

QMainWindow 拥有 menuBar 指针的所有权,会在适当的时间将其删除。

该函数在 Qt 4.2 引入

另请参阅 menuWidget ()

void QMainWindow::setStatusBar(QStatusBar *statusbar)

将主窗口的状态栏设置为 statusbar

将状态栏设置为nullptr会将其从主窗口中删除。请注意,QMainWindow 拥有 statusbar 指针的所有权,会在适当的时间将其删除。

另请参阅 statusBar ()

void QMainWindow::setTabPosition(Qt::DockWidgetAreas areas, QTabWidget::TabPosition tabPosition)

将停靠部件 areas 的选项卡位置设置为指定的 tabPosition。默认情况下,所有停靠区域在底部显示其选项卡。

注意 VerticalTabs 会覆盖此方法设置的选项卡位置

该函数在 Qt 4.5 引入

另请参阅 tabPosition () 和 setTabShape ()

void QMainWindow::splitDockWidget(QDockWidget *first, QDockWidget *second, Qt::Orientation orientation)

分割停靠部件 first 为两个停靠部件,第一个部件指针传给 first,第二个部件指针传给 second

orientation 指定了空间的划分。Qt::Horizontal 左右分割;Qt::Vertical 上下分割。

注意:如果 first 位于选项卡式停靠区域中,则 second 将添加为新选项卡,这是因为单个选项卡只能包含一个停靠部件。

注意Qt::LayoutDirection 会影响两个分割区域的停靠部件顺序。启用从右到左布局方向时,将反转停靠部件的位置。

另请参阅 tabifyDockWidget(), addDockWidget () 和 removeDockWidget ()

QStatusBar *QMainWindow::statusBar() const

返回主窗口的状态栏。如果状态栏不存在,则此函数将创建并返回一个空状态栏。

另请参阅 setStatusBar ()

QTabWidget::TabPosition QMainWindow::tabPosition(Qt::DockWidgetArea area) const

返回停靠区域 area 的位置。

注意 如果设置了 VerticalTabs ,会覆盖此函数的返回值。

该函数在 Qt 4.5 引入

另请参阅 setTabPosition () 和 tabShape ()

QList<QDockWidget *> QMainWindow::tabifiedDockWidgets(QDockWidget *dockwidget) const

返回 dockwidget 内堆叠着的停靠部件。

该函数在 Qt 4.5 引入

另请参阅 tabifyDockWidget ()

void QMainWindow::tabifyDockWidget(QDockWidget *first, QDockWidget *second)

second 停靠部件移动到 first 停靠部件旁形成一个选项卡式停靠区域。

另请参阅 tabifiedDockWidgets ()

QWidget *QMainWindow::takeCentralWidget()

从主窗口中移除中央部件。

返回中央部件的指针。

该函数在 Qt 5.2 引入

Qt::ToolBarArea QMainWindow::toolBarArea(QToolBar *toolbar) const

返回 toolbarQt::ToolBarArea。 如果主窗口没有添加 toolbar ,返回 Qt::NoToolBarArea

另请参阅 addToolBar (), addToolBarBreak () 和 Qt::ToolBarArea

bool QMainWindow::toolBarBreak(QToolBar *toolbar) const

返回 toolbar 之前是否有工具栏 break。

另请参阅 addToolBarBreak () 和 insertToolBarBreak ()

QMetaMethod 类

QMetaMethod 类提供了对应一个成员函数的元数据。更多内容...

属性内容
头文件#include <QMetaMethod>
qmakeQT += core

公共类型

类型名称
enumAccess { Private, Protected, Public }
enumMethodType { Method, Signal, Slot, Constructor }

公共成员函数

返回类型函数
QMetaMethod::Accessaccess() const
boolinvoke(QObject *object, Qt::ConnectionType connectionType, QGenericReturnArgument returnValue, QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const
boolinvoke(QObject *object, QGenericReturnArgument returnValue, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const
boolinvoke(QObject *object, Qt::ConnectionType connectionType, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const
boolinvoke(QObject *object, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const
boolinvokeOnGadget(void *gadget, QGenericReturnArgument returnValue, QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const
boolinvokeOnGadget(void *gadget, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const
boolisValid() const
intmethodIndex() const
QByteArraymethodSignature() const
QMetaMethod::MethodTypemethodType() const
QByteArrayname() const
intparameterCount() const
QList<QByteArray>parameterNames() const
intparameterType(int index) const
QList<QByteArray>parameterTypes() const
intreturnType() const
intrevision() const
const char *tag() const
const char *typeName() const

静态公共成员

返回类型函数
QMetaMethodfromSignal(PointerToMemberFunction signal)

相关非成员函数

返回类型函数
booloperator!=(const QMetaMethod &m1, const QMetaMethod &m2)
booloperator==(const QMetaMethod &m1, const QMetaMethod &m2)

宏定义

宏定义
Q_METAMETHOD_INVOKE_MAX_ARGS

详细描述

QMetaMethod 类具有一个 methodType()、一个 methodSignature()、一组 parameterTypes() 和 parameterNames()、返回值的 typeName()、一个 tag()、一个 access() 描述符。可以通过 invoke() 来执行任意 QObject 的方法。

另请参阅:QMetaObjectQMetaEnumQMetaPropertyQt 属性系统

成员类型文档

enum QMetaMethod::Access

此枚举描述某方法的访问权限,遵循 C++ 相关公约。

常量数值
QMetaMethod::Private0
QMetaMethod::Protected1
QMetaMethod::Public2

enum QMetaMethod::MethodType

常量数值描述
QMetaMethod::Method0该函数是普通的成员函数。
QMetaMethod::Signal1该函数是信号函数。
QMetaMethod::Slot2该函数是槽函数。
QMetaMethod::Constructor3该函数是构造函数。

成员函数文档

QMetaMethod::Access QMetaMethod::access() const

返回该方法的访问权限(privateprotected 或 `public)。

注意: 信号永远是公共的,但应将此认为是实现细节。在类外发射该类的信号通常是个坏主意。

另请参阅:methodType()。


[static] template <typename PointerToMemberFunction> QMetaMethod QMetaMethod::fromSignal(PointerToMemberFunction signal)

返回对应给定 signal 的元方法,若 signal 并非信号,则返回无效的 QMetaMethod 对象。

范例:

QMetaMethod destroyedSignal = QMetaMethod::fromSignal(&QObject::destroyed);

本函数在 Qt 5.0 中被引入。


bool QMetaMethod::invoke(QObject *object, Qt::ConnectionType connectionType, QGenericReturnArgument returnValue, QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const

通过 object 对象动态调用本方法。若可被动态调用则返回 tue,若该对象无此方法或参数不匹配则返回 false

该动态调用可以是同步或异步的,由 connectionType 决定:

  • typeQt::DirectConnection,则该方法会被立即执行。
  • typeQt::QueuedConnection,则会发送一个 QEvent ,该方法会在应用进入该对象所属线程的主事件循环后执行。
  • typeQt::AutoConnection,当 object 与调用者处于相同线程中时,该方法会被同步执行,否则会被异步执行。

返回值会被存放在 returnvalue 中。若调用方式是异步,则返回值无法被获取。最多可以传递十个参数 (val0, val1, val2, val3, val4, val5, val6, val7, val8val9) 至本方法。

QGenericArgumentQGenericReturnArgument 是内部的辅助类。为了动态调用信号槽,您需要将参数通过 Q_ARG() 和 Q_RETURN_ARG() 宏进行封装。Q_ARG() 接受一个类型名称和一个该类型的不可变引用;Q_RETURN_ARG() 接受一个类型名称和一个该类型的可变引用。

通过异步方式动态调用 QPushButtonanimateClick() 槽:

int methodIndex = pushButton->metaObject()->indexOfMethod("animateClick()");
QMetaMethod method = metaObject->method(methodIndex);
method.invoke(pushButton, Qt::QueuedConnection);

当异步调用方法时,传递的参数必须被 Qt 的元对象系统所知悉,因为 Qt 需要在后台事件中拷贝并保存它们。如果您使用队列连接时遇到下述错误信息:

QMetaMethod::invoke: Unable to handle unregistered datatype 'MyType'

则在调用 invokeMethod() 之前通过 qRegisterMetaType() 来注册该数据类型。

若想通过 obj 对象同步调用 compute(QString, int, double) 槽,则代码如下:

QString retVal;
QByteArray normalizedSignature = QMetaObject::normalizedSignature("compute(QString, int, double)");
int methodIndex = obj->metaObject()->indexOfMethod(normalizedSignature);
QMetaMethod method = obj->metaObject()->method(methodIndex);
method.invoke(obj,
              Qt::DirectConnection,
              Q_RETURN_ARG(QString, retVal),
              Q_ARG(QString, "sqrt"),
              Q_ARG(int, 42),
              Q_ARG(double, 9.7));

此处使用了 QMetaObject::normalizedSignature() 来确保函数签名符合 invoke() 期望的格式,即将多余的空白移除。

compute 槽通过特定顺序没有完整获取到一个 QString、一个 int 和一个 double,则此调用会失败。

警告: 此方法不会验证参数的有效性,object 必须是创建 QMetaMethodQMetaObject 的类型实例,参数列表必须与该方法的保持一直,否则会导致未定义行为。

另请参阅:Q_ARG()、Q_RETURN_ARG()、qRegisterMetaType() 和 QMetaObject::invokeMethod()。


bool QMetaMethod::invoke(QObject *object, QGenericReturnArgument returnValue, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const

此函数是 invoke() 的重载。

此重载始终通过 Qt::AutoConnection 调用本方法。


bool QMetaMethod::invoke(QObject *object, Qt::ConnectionType connectionType, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const

此函数是 invoke() 的重载。

此重载用于不关心对返回值的场合。


bool QMetaMethod::invoke(QObject *object, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const

此函数是 invoke() 的重载。

此重载通过 Qt::AutoConnection 调用本方法,并忽略返回值。


bool QMetaMethod::invokeOnGadget(void *gadget, QGenericReturnArgument returnValue, QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const

通过 Q_GADGET 对象动态调用本方法。若可被动态调用则返回 tue,若该对象无此方法或参数不匹配则返回 false

gadget 指针必须是 Q_GADGET 类的实例。

该调用始终是同步的。

返回值会被存放在 returnvalue 中。若调用方式是异步,则返回值无法被获取。最多可以传递十个参数 (val0, val1, val2, val3, val4, val5, val6, val7, val8val9) 至本方法。

警告: 此方法不会验证参数的有效性,gadget 必须是创建 QMetaMethodQMetaObject 的类型实例,参数列表必须与该方法的保持一直,否则会导致未定义行为。

本函数在 Qt 5.5 中被引入。

另请参阅:Q_ARG()、Q_RETURN_ARG()、qRegisterMetaType() 和 QMetaObject::invokeMethod()。


bool QMetaMethod::invokeOnGadget(void \*gadget, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const

这是一个重载函数。

此重载函数通过 gadget 对象动态调用本方法,并忽略返回值。

本函数在 Qt 5.5 中被引入。


bool QMetaMethod::isValid() const

若此方法有效则返回 true(即可被自省并动态调用),否则返回 false

本函数在 Qt 5.0 中被引入。


int QMetaMethod::methodIndex() const

返回本方法的索引编号。

本函数在 Qt 4.6 中被引入。


QByteArray QMetaMethod::methodSignature() const

返回本方法的签名(例如 setValue(double))。

本函数在 Qt 5.0 中被引入。

另请参阅:parameterTypes() 和 parameterNames()。


QMetaMethod::MethodType QMetaMethod::methodType() const

返回本方法的类型(signalslotmethodconstructor)。

另请参阅:access()。


QByteArray QMetaMethod::name() const

返回本方法的名字。

本函数在 Qt 5.0 中被引入。

另请参阅:methodSignature() 和 parameterCount()。


int QMetaMethod::parameterCount() const

返回本方法的参数个数。

本函数在 Qt 5.0 中被引入。

另请参阅:parameterType() 和 parameterNames()。


QList<QByteArray> QMetaMethod::parameterNames() const

返回参数名称列表。

另请参阅:parameterTypes() 和 methodSignature()。


int QMetaMethod::parameterType(int index) const

返回对应 index 的参数类型。

返回值是 QMetaType 中注册的类型之一,若该类型未被注册则返回 QMetaType::UnknownType

本函数在 Qt 5.0 中被引入。

另请参阅:parameterCount()、returnType() 和 QMetaType

QList<QByteArray> QMetaMethod::parameterTypes() const

返回参数类型的列表。

另请参阅:parameterNames() 和 methodSignature()。


int QMetaMethod::returnType() const

返回本方法返回值的类型。

返回

返回值是 QMetaType 中注册的类型之一,若该类型未被注册则返回 QMetaType::UnknownType

本函数在 Qt 5.0 中被引入。

另请参阅:parameterType()、QMetaTypetypeName()。


int QMetaMethod::revision() const

返回通过 Q_REVISION 注明的版本,若未注明则返回 0

本函数在 Qt 5.1 中被引入。


const char *QMetaMethod::tag() const

返回与本方法关联的标签。

标签在此处指的是可以被 moc 识别的特定宏,用于为方法添加附加信息。

标签信息可以用如下方式添加到函数声明中:

    // 在 MainWindow 类声明中
    #ifndef Q_MOC_RUN
    // 将标签内容定义为空,以确保对编译器不可见
    #  define MY_CUSTOM_TAG
    #endif
    ...
    private slots:
        MY_CUSTOM_TAG void testFunc();

相关信息可通过如下方式获取:

    MainWindow win;
    win.show();

    int functionIndex = win.metaObject()->indexOfSlot("testFunc()");
    QMetaMethod mm = win.metaObject()->method(functionIndex);
    qDebug() << mm.tag(); // 将打印 MY_CUSTOM_TAG

此时,moc 会提取并记录所有标签,但不会对它们做任何特殊处理。您可以用标签来标注不同的方法,并在您的应用中通过特定的用途来使用它们。

注意: 自 Qt 5.0 开始,moc 会展开预编译宏,所以必须将标签定义包含在 #ifndef Q_MOC_RUN 中,正如上文范例。这在 Qt 4 中不是必要的,但上述代码在 Qt 4 中同样也能生效。


const char *QMetaMethod::typeName() const

返回本方法的返回类型名称。

另请参阅:returnType() 和 QMetaType::type()。

相关非成员函数

bool operator!=(const QMetaMethod &m1, const QMetaMethod &m2)

这是一个重载函数。

m1m2 不相等则返回 true,否则返回 false

本函数在 Qt 5.0 中被引入。


bool operator==(const QMetaMethod &m1, const QMetaMethod &m2)

这是一个重载函数。

m1m2 相等则返回 true,否则返回 false

本函数在 Qt 5.0 中被引入。

宏定义文档

Q_METAMETHOD_INVOKE_MAX_ARGS

此宏的数值等于通过 QMetaMethod::invoke() 执行方法时,可以传递的最大参数个数。

QMetaObject 结构体

QMetaObject 类包含了 Qt 对象的元信息。更多内容...

属性内容
头文件#include <QMetaObject>
qmakeQT += core

公共成员类型

类型名称
classConnection

公共成员函数

返回类型函数
QMetaClassInfoclassInfo(int index) const
intclassInfoCount() const
intclassInfoOffset() const
const char *className() const
QMetaMethodconstructor(int index) const
intconstructorCount() const
QMetaEnumenumerator(int index) const
intenumeratorCount() const
intenumeratorOffset() const
intindexOfClassInfo(const char *name) const
intindexOfConstructor(const char *constructor) const
intindexOfEnumerator(const char *name) const
intindexOfMethod(const char *method) const
intindexOfProperty(const char *name) const
intindexOfSignal(const char *signal) const
intindexOfSlot(const char *slot) const
boolinherits(const QMetaObject *metaObject) const
QMetaMethodmethod(int index) const
intmethodCount() const
intmethodOffset() const
QObject *newInstance(QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const
QMetaPropertyproperty(int index) const
intpropertyCount() const
intpropertyOffset() const
const QMetaObject *superClass() const
QMetaPropertyuserProperty() const

静态公共成员

返回类型函数
boolcheckConnectArgs(const char *signal, const char *method)
boolcheckConnectArgs(const QMetaMethod &signal, const QMetaMethod &method)
voidconnectSlotsByName(QObject *object)
boolinvokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument())
boolinvokeMethod(QObject *obj, const char *member, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument())
boolinvokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument())
boolinvokeMethod(QObject *obj, const char *member, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument())
boolinvokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr)
boolinvokeMethod(QObject *context, Functor function, FunctorReturnType *ret)
QByteArraynormalizedSignature(const char *method)
QByteArraynormalizedType(const char *type)

宏定义

返回类型宏定义
QGenericArgumentQ_ARG(Type, const Type &value)
QGenericReturnArgumentQ_RETURN_ARG(Type, Type &value)

详细描述

Qt 的元对象系统负责信号槽跨对象通信机制、运行时类型信息和 Qt 的属性系统。应用中的每个 QObject 子类都有一个唯一的 QMetaObject 实例(译者注:与类一一对应,即同一个 QObject 子类的任意对象,都使用同一个 QMetaObject),其中保存了这个 QObject 子类的所有元信息,可以通过 QObject::metaObject() 获取。

QMetaObject 在应用编写中通常不需要,但在进行元编程时会非常有用,例如脚本引擎或者用户界面生成器。

最常用的成员函数有:

索引函数 indexOfConstructor()、indexOfMethod()、indexOfEnumerator() 和 indexOfProperty() 将构造函数、成员函数、枚举类型和属性的名称映射为索引。例如,当连接信号槽时,Qt 内部使用 indexOfMethod() 进行检索。

类可以拥有一系列 名称—数值 格式的附加信息,保存在 QMetaClassInfo 对象中。信息条目数量可通过 classInfoCount()查询, classInfo()返回单条信息,也可通过 indexOfClassInfo() 检索信息条目。

注意:元对象系统的操作通常是线程安全的,比如元对象是在编译期生成的静态只读实例。然而,如果元对象在被程序动态修改了(如通过 QQmlPropertyMap),应用需要显示地同步对相关对象的访问。

另请参阅:QMetaClassInfoQMetaEnumQMetaMethodQMetaPropertyQMetaTypeMeta-Object System

成员函数文档

[static] bool QMetaObject::checkConnectArgs(const char *signal, const char *method)

如果 signalmethod 的参数能够匹配则返回 true,否则返回 false

signalmethod 都被假设是已经规范化的。

另请参阅:normalizedSignature()。


[static] bool QMetaObject::checkConnectArgs(const QMetaMethod &signal, const QMetaMethod &method)

这是一个重载函数。

如果 signalmethod 的参数能够匹配则返回 true,否则返回 false

本函数在 Qt 5.0 中被引入。


QMetaClassInfo QMetaObject::classInfo(int index) const

返回对应 index 的类型信息的元数据对象。

范例:

 class MyClass : public QObject
 {
     Q_OBJECT
     Q_CLASSINFO("author", "Sabrina Schweinsteiger")
     Q_CLASSINFO("url", "http://doc.moosesoft.co.uk/1.0/")

 public:
     ...
 };

另请参阅:classInfoCount()、classInfoOffset() 和 indexOfClassInfo()。


int QMetaObject::classInfoCount() const

返回该类信息条目数量。

另请参阅:classInfo()、classInfoOffset() 和 indexOfClassInfo()。


int QMetaObject::classInfoOffset() const

返回类信息在该类中的偏移量,即第一条类信息的编号。

若该类没有包含类信息的父类,则偏移量为 0,否则偏移量是所有父类的类信息数量的总和。

另请参阅:classInfo()、classInfoCount() 和 indexOfClassInfo()。


const char *QMetaObject::className() const

返回该类的名称。

另请参阅:superClass()。


[static] void QMetaObject::connectSlotsByName(QObject *object)

递归检索 object 和所有子对象,将它们的信号连接至 object 中匹配的槽,匹配格式如下:

 void on_<对象名>_<信号名>(<信号参数>);

假设有一个对象名button1QPushButton 类型的子对象,则捕获它的 clicked() 信号的槽应为:

 void on_button1_clicked();

object 对象自身的名字已设置,则它自己的信号也会被连接至对应的槽。

另请参阅:QObject::setObjectName().


QMetaMethod QMetaObject::constructor(int index) const

返回指定 index 的构造函数的元数据。

该函数在 Qt 4.5 中被引入。

另请参阅:constructorCount() 和 newInstance()。


int QMetaObject::constructorCount() const

返回此类的构造函数个数。

该函数在 Qt 4.5 中被引入。

另请参阅:constructor() 和 indexOfConstructor()。


QMetaEnum QMetaObject::enumerator(int index) const

返回指定 index 的枚举类型的元数据。

另请参阅:enumeratorCount()、enumeratorOffset() 和 indexOfEnumerator()。


int QMetaObject::enumeratorCount() const

返回该类的枚举类型的个数。

另请参阅:enumerator()、enumeratorOffset() 和 indexOfEnumerator()。


int QMetaObject::enumeratorOffset() const

返回该类的枚举类型偏移量,即首个枚举变量的编号。

若该类没有包含枚举类型的父类,则偏移量为 0,否则偏移量是所有父类的枚举类型数量的总和。

另请参阅:enumerator()、enumeratorCount() 和 indexOfEnumerator()。


int QMetaObject::indexOfClassInfo(const char *name) const

查找名为 name 的类型信息条目并返回其编号,未找到则返回-1

另请参阅:classInfo()、classInfoCount() 和 classInfoOffset()。


int QMetaObject::indexOfConstructor(const char *constructor) const

查找名为 constructor 的构造函数并返回其编号,未找到则返回-1

注意:constructor 需要为规范化的格式,如 normalizedSignature() 的返回值。

该函数在 Qt 4.5 中被引入。

另请参阅:constructor()、constructorCount() 和 normalizedSignature()。


int QMetaObject::indexOfEnumerator(const char *name) const

查找名为 name 的枚举类型并返回其编号,未找到则返回-1

另请参阅:enumerator(), enumeratorCount() 和 enumeratorOffset().


int QMetaObject::indexOfMethod(const char *method) const

查找名为 method 的方法并返回其编号,未找到则返回-1

注意:method 需要为规范化的格式,如 normalizedSignature() 的返回值。

另请参阅:method()、methodCount()、methodOffset() 和 normalizedSignature()。


int QMetaObject::indexOfProperty(const char *name) const

查找名为 name 的属性并返回其编号,未找到则返回-1

另请参阅:property()、propertyCount() 和 propertyOffset()。


int QMetaObject::indexOfSignal(const char *signal) const

查找名为 name 的信号并返回其编号,未找到则返回-1

此方法与 indexOfMethod() 相似,区别是若该方法存在但并非信号函数,则会返回 -1

注意:signal 需要为规范化的格式,如 normalizedSignature() 的返回值。

另请参阅:indexOfMethod()、normalizedSignature(), method()、methodCount() 和 methodOffset()。


int QMetaObject::indexOfSlot(const char *slot) const

查找名为 name 的槽并返回其编号,未找到则返回-1

此方法与 indexOfMethod() 相似,区别是若该方法存在但并非槽函数,则会返回 -1

另请参阅:indexOfMethod()、method()、methodCount() 和 methodOffset()。


bool QMetaObject::inherits(const QMetaObject *metaObject) const

若该 QMetaObject 继承自 metaObject 描述的类型,则返回 true,否则返回 false

一个类型被认为是继承自它自己的。

该函数在 Qt 5.7 中被引入。


[static] bool QMetaObject::invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument())

通过 obj 对象动态调用它的 member 方法(或者信号和槽),若调用成功则返回 true,若该对象没有此方法或参数不匹配则返回 false

该调用可以是同步或异步的,由 type 决定:

  • typeQt::DirectConnection,则该方法会被立即执行。

  • typeQt::QueuedConnection,则会发送一个 QEvent ,该方法会在应用进入该对象所属线程的主事件循环后执行。

  • typeQt::BlockingQueuedConnection,则该方法会通过与 Qt::QueuedConnection 相同的方式执行,此外当前线程会被阻塞,直到该事件被响应。使用此方法在相同线程的对象间通信会导致死锁。

  • typeQt::AutoConnection,当 obj 与调用者处于相同线程中时,该方法会被同步执行,否则会被异步执行。

member 函数的返回值会被存放在 ret 中。若调用方式是异步,则返回值无法被获取。最多可以传递十个参数 (val0, val1, val2, val3, val4, val5, val6, val7, val8val9) 至 member 函数。

QGenericArgumentQGenericReturnArgument 是内部的辅助类。为了动态调用信号槽,您需要将参数通过 Q_ARG() 和 Q_RETURN_ARG() 宏进行封装。Q_ARG() 接受一个类型名称和一个该类型的不可变引用;Q_RETURN_ARG() 接受一个类型名称和一个该类型的可变引用。

您只需要将信号槽的名称传递至本函数,无需传递完整的签名。例如,异步调用某个 QThread 对象的 quit() 槽需要的代码如下:

 QMetaObject::invokeMethod(thread, "quit",
                           Qt::QueuedConnection);

当异步调用方法时,传递的参数必须被 Qt 的元对象系统所知悉,因为 Qt 需要在后台事件中拷贝并保存它们。如果您使用队列连接时遇到下述错误信息:

 QMetaObject::invokeMethod: Unable to handle unregistered datatype 'MyType'

则在调用 invokeMethod() 之前通过 qRegisterMetaType() 来注册该数据类型。

若想通过 obj 对象同步调用 compute(QString, int, double) 槽,则代码如下:

 QString retVal;
 QMetaObject::invokeMethod(obj, "compute", Qt::DirectConnection,
                           Q_RETURN_ARG(QString, retVal),
                           Q_ARG(QString, "sqrt"),
                           Q_ARG(int, 42),
                           Q_ARG(double, 9.7));

compute 槽通过特定顺序没有完整获取到一个 QString、一个 int 和一个 double,则此调用会失败。

注意: 此方法是线程安全的。

另请参阅:Q_ARG()、Q_RETURN_ARG()、qRegisterMetaType() 和 QMetaMethod::invoke()。


[static] bool QMetaObject::invokeMethod(QObject *obj, const char *member, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument())

此函数是 invokeMethod() 的重载。

此重载始终通过 Qt::AutoConnection 调用对应方法。

注意: 此方法是线程安全的。


[static] bool QMetaObject::invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument())

此函数是 invokeMethod() 的重载。

此重载用于不关心对返回值的场合。

注意: 此方法是线程安全的。


[static] bool QMetaObject::invokeMethod(QObject *obj, const char *member, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument())

此函数是 invokeMethod() 的重载。

此重载通过 Qt::AutoConnection 调用对应方法,并忽略返回值。

注意: 此方法是线程安全的。


[static] template <typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr)

此函数是 invokeMethod() 的重载。

通过 type 方式在 context 所属的事件循环中动态调用 functionfunction 可以是一个仿函数或成员函数指针。若该函数可被动态调用则返回 true,当该函数不存在或参数不匹配时返回 false。函数的返回值将被保存至 ret 中。

注意: 此方法是线程安全的。

该函数在 Qt 5.10 中被引入。


[static] template <typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod(QObject *context, Functor function, FunctorReturnType *ret)

此函数是 invokeMethod() 的重载。

通过 Qt::AutoConnection 方式动态调用 functionfunction 可以是一个仿函数或成员函数指针。若该函数可被动态调用则返回 true,当该函数不存在或参数不匹配时返回 false。函数的返回值将被保存至 ret 中。

注意: 此方法是线程安全的。

该函数在 Qt 5.10 中被引入。


QMetaMethod QMetaObject::method(int index) const

返回指定 index 的方法的元数据。

另请参阅:methodCount(), methodOffset() 和 indexOfMethod().


int QMetaObject::methodCount() const

返回该类中方法的数量,包括所有基类的方法个数。除了常规成员函数外,也包含信号函数和槽函数。

Returns the number of methods in this class, including the number of methods provided by each base class. These include signals and slots as well as normal member functions.

使用下述代码来将所给类的所有方法签名存储至 QStringList

 const QMetaObject* metaObject = obj->metaObject();
 QStringList methods;
 for(int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i)
     methods << QString::fromLatin1(metaObject->method(i).methodSignature());

另请参阅:method()、methodOffset() 和 indexOfMethod()。


int QMetaObject::methodOffset() const

返回类方法在该类中的偏移量,即第一个类方法的编号。

该偏移量是所有父类的方法数总和(因此总为正数,因为 QObjectdeleteLater() 槽和 destroyed() 信号)。

另请参阅:method()、methodCount() 和 indexOfMethod()。


QObject *QMetaObject::newInstance(QGenericArgument val0 = QGenericArgument(nullptr), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const

构造一个此类的新实例。您可以传递最多十个参数 (val0, val1, val2, val3, val4, val5, val6, val7, val8val9) 至构造函数。返回构造的新对象,若没有合适的构造函数则返回 nullptr

注意: 只有通过 Q_INVOKABLE 修饰符声明的构造函数才能在元对象系统中使用。

该函数在 Qt 4.5 中被引入。

另请参阅:Q_ARG() 和 constructor()。


[static] QByteArray QMetaObject::normalizedSignature(const char *method)

将给予的 method 进行规范化。

Qt 使用规范化的签名来来判断两个给定的信号和槽是否匹配。规范化操作会将空格减到最少,将 const 适当前移,移除值类型的 const,并将不可变引用替换为值类型。

另请参阅:checkConnectArgs() 和 normalizedType()。


[static] QByteArray QMetaObject::normalizedType(const char *type)

type 规范化。

请参阅 QMetaObject::normalizedSignature() 中关于 Qt 如何进行规范化的描述。

范例:

 QByteArray normType = QMetaObject::normalizedType(" int    const  *");
 // 规范化的类型将为 "const int*"

该函数在 Qt 4.2 中被引入。

另请参阅:normalizedSignature().


QMetaProperty QMetaObject::property(int index) const

返回指定 index 的属性的元数据。若该属性不存在,则返回空的 QMetaProperty 对象。

另请参阅:propertyCount()、propertyOffset() 和 indexOfProperty()。


int QMetaObject::propertyCount() const

返回该类中属性的类型,包括所有基类的属性个数。

使用如下代码来将给定类的所有属性名称保存至 QStringList

 const QMetaObject* metaObject = obj->metaObject();
 QStringList properties;
 for(int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i)
     properties << QString::fromLatin1(metaObject->property(i).name());

另请参阅:property()、propertyOffset() 和 indexOfProperty()。


int QMetaObject::propertyOffset() const

返回类属性在该类中的偏移量,即第一条类属性的编号。

该偏移量包含所有父类的类属性数量总和(因此总为正数,因为 QObjectname() 属性)。

另请参阅:property()、propertyCount() 和 indexOfProperty()。


const QMetaObject *QMetaObject::superClass() const

返回父类的元对象,若不存在则返回 nullptr

另请参阅:className()。


QMetaProperty QMetaObject::userProperty() const

返回 USER 标志位为 true 的元属性。

该函数在 Qt 4.2 中被引入。

另请参阅:QMetaProperty::isUser()。

宏定义文档

QGenericArgument Q_ARG(Type, const Type &value)

该宏接受一个 type 和一个该类型的 value 参数,返回一个用于传递至 QMetaObject::invokeMethod() 的 QGenericArgument 对象。

另请参阅:Q_RETURN_ARG()。


QGenericReturnArgument Q_RETURN_ARG(Type, Type &value)

该宏接受一个 Type 和一个该类型的可变引用 value 参数,返回一个用于传递至 QMetaObject::invokeMethod() 的包含该类型的 QGenericReturnArgument 对象。

另请参阅:Q_ARG().。

QMetaType 类

QMetaType 类管理元对象系统中的注名类型。更多内容...

属性内容
头文件#include <QMetaType>
qmakeQT += core

注意: 此类中所有函数都是线程安全的。

公共成员类型

类型名称
enumType { Void, Bool, Int, UInt, Double, ..., UnknownType }
enumTypeFlag { NeedsConstruction, NeedsDestruction, MovableType, IsEnumeration, PointerToQObject }
flagsTypeFlags

公共成员函数

返回类型函数
QMetaType(const int typeId = QMetaType::UnknownType)
~QMetaType()
void *construct(void *where, const void *copy = 0) const
void *create(const void *copy = 0) const
voiddestroy(void *data) const
voiddestruct(void *data) const
QMetaType::TypeFlagsflags() const
intid() const
boolisRegistered() const
boolisValid() const
const QMetaObject *metaObject() const
::QByteArrayname() const
intsizeOf() const

静态公共成员

返回类型函数
boolcompare(const void *lhs, const void *rhs, int typeId, int *result)
void *construct(int type, void *where, const void *copy)
boolconvert(const void *from, int fromTypeId, void *to, int toTypeId)
void *create(int type, const void *copy = nullptr)
booldebugStream(QDebug &dbg, const void *rhs, int typeId)
voiddestroy(int type, void *data)
voiddestruct(int type, void *where)
boolequals(const void *lhs, const void *rhs, int typeId, int *result)
QMetaTypefromType()
boolhasRegisteredComparators()
boolhasRegisteredComparators(int typeId)
boolhasRegisteredConverterFunction(int fromTypeId, int toTypeId)
boolhasRegisteredConverterFunction()
boolhasRegisteredDebugStreamOperator()
boolhasRegisteredDebugStreamOperator(int typeId)
boolisRegistered(int type)
boolload(QDataStream &stream, int type, void *data)
const QMetaObject *metaObjectForType(int type)
boolregisterComparators()
boolregisterConverter()
boolregisterConverter(MemberFunction function)
boolregisterConverter(MemberFunctionOk function)
boolregisterConverter(UnaryFunction function)
boolregisterDebugStreamOperator()
boolregisterEqualsComparator()
boolsave(QDataStream &stream, int type, const void *data)
intsizeOf(int type)
inttype(const char *typeName)
inttype(const ::QByteArray &typeName)
QMetaType::TypeFlagstypeFlags(int type)
const char *typeName(int typeId)

相关非成员函数

返回类型函数
intqMetaTypeId()
intqRegisterMetaType(const char *typeName)
intqRegisterMetaType()
voidqRegisterMetaTypeStreamOperators(const char *typeName)
booloperator!=(const QMetaType &a, const QMetaType &b)
booloperator==(const QMetaType &a, const QMetaType &b)

宏定义

宏定义
Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(Container)
Q_DECLARE_METATYPE(Type)
Q_DECLARE_OPAQUE_POINTER(PointerType)
Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(Container)
Q_DECLARE_SMART_POINTER_METATYPE(SmartPointer)

详细描述

此类是一个辅助类,被用作序列化 QVariant 以及队列连接信号槽中的类型。它将类型名称关联到对应类型,以支持运行时动态创建和销毁此类型。通过 Q_DECLARE_METATYPE() 声明新类型,让它可以被 QVariant 和其它模板函数(qMetaTypeId() 等)使用。调用 qRegisterMetaType() 来让其可以被非模板型函数使用,如信号槽的队列连接。

任何包含一个公共默认构造函数、一个公共拷贝构造函数、一个默认析构函数的类或结构体都可以被注册为元类型。

下述代码展示了如何分配和销毁一个 MyClass 的实例:

int id = QMetaType::type("MyClass");
if (id != QMetaType::UnknownType) {
    void *myClassPtr = QMetaType::create(id);
    ...
    QMetaType::destroy(id, myClassPtr);
    myClassPtr = 0;
}

若我们想让流运算符 operator<<()operator>>() 可被用于存储了自定义类型的 QVariant 对象,则这个自定义类型必须提供 operator<<()operator>>() 运算符重载。

另请参阅:Q_DECLARE_METATYPE(),QVariant::setValue(),QVariant::value() 和 QVariant::fromValue().

成员类型文档

enum QMetaType::Type

下表是 QMetaType 内置支持的类型:

常量数值描述
QMetaType::Void43void
QMetaType::Bool1bool
QMetaType::Int2int
QMetaType::UInt3unsigned int
QMetaType::Double6double
QMetaType::QChar7QChar
QMetaType::QString10QString
QMetaType::QByteArray12QByteArray
QMetaType::Nullptr51std::nullptr_t
QMetaType::VoidStar31void *
QMetaType::Long32long
QMetaType::LongLong4long long
QMetaType::Short33short
QMetaType::Char34char
QMetaType::ULong35unsigned long
QMetaType::ULongLong5unsigned long long
QMetaType::UShort36unsigned short
QMetaType::SChar40signed char
QMetaType::UChar37unsigned char
QMetaType::Float38float
QMetaType::QObjectStar39QObject *
QMetaType::QVariant41QVariant
QMetaType::QCursor74QCursor
QMetaType::QDate14QDate
QMetaType::QSize21QSize
QMetaType::QTime15QTime
QMetaType::QVariantList9QVariantList
QMetaType::QPolygon71QPolygon
QMetaType::QPolygonF86QPolygonF
QMetaType::QColor67QColor
QMetaType::QColorSpace87QColorSpace(在 Qt 5.15 中被引入)
QMetaType::QSizeF22QSizeF
QMetaType::QRectF20QRectF
QMetaType::QLine23QLine
QMetaType::QTextLength77QTextLength
QMetaType::QStringList11QStringList
QMetaType::QVariantMap8QVariantMap
QMetaType::QVariantHash28QVariantHash
QMetaType::QIcon69QIcon
QMetaType::QPen76QPen
QMetaType::QLineF24QLineF
QMetaType::QTextFormat78QTextFormat
QMetaType::QRect19QRect
QMetaType::QPoint25QPoint
QMetaType::QUrl17QUrl
QMetaType::QRegExp27QRegExp
QMetaType::QRegularExpression44QRegularExpression
QMetaType::QDateTime16QDateTime
QMetaType::QPointF26QPointF
QMetaType::QPalette68QPalette
QMetaType::QFont64QFont
QMetaType::QBrush66QBrush
QMetaType::QRegion72QRegion
QMetaType::QBitArray13QBitArray
QMetaType::QImage70QImage
QMetaType::QKeySequence75QKeySequence
QMetaType::QSizePolicy121QSizePolicy
QMetaType::QPixmap65QPixmap
QMetaType::QLocale18QLocale
QMetaType::QBitmap73QBitmap
QMetaType::QMatrix79QMatrix
QMetaType::QTransform80QTransform
QMetaType::QMatrix4x481QMatrix4x4
QMetaType::QVector2D82QVector2D
QMetaType::QVector3D83QVector3D
QMetaType::QVector4D84QVector4D
QMetaType::QQuaternion85QQuaternion
QMetaType::QEasingCurve29QEasingCurve
QMetaType::QJsonValue45QJsonValue
QMetaType::QJsonObject46QJsonObject
QMetaType::QJsonArray47QJsonArray
QMetaType::QJsonDocument48QJsonDocument
QMetaType::QCborValue53QCborValue
QMetaType::QCborArray54QCborArray
QMetaType::QCborMap55QCborMap
QMetaType::QCborSimpleType52QCborSimpleType
QMetaType::QModelIndex42QModelIndex
QMetaType::QPersistentModelIndex50QPersistentModelIndex(在 Qt 5.5 中被引入)
QMetaType::QUuid30QUuid
QMetaType::QByteArrayList49QByteArrayList
QMetaType::User1024用户类型的基础值(译者注:即起始值
QMetaType::UnknownType0这是无效的类型编号,QMetaType 会在类型未注册时返回此值。

可以使用 Q_DECLARE_METATYPE() 注册额外的类型。

另请参阅:type() 和 typeName()。


enum QMetaType::TypeFlag

flags QMetaType::TypeFlags

此枚举类型描述了被 QMetaType 支持的类型的属性。

常量数值描述
QMetaType::NeedsConstruction0x1此类型具有非平凡的构造函数。若某类型不具备此标志,则可通过 memset() 安全地清零。
QMetaType::NeedsDestruction0x2此类型非平凡的析构函数。若某类型不具备此标志,则丢弃对象前不需要调用析构函数(译者注:即可以用 free() 释放对象
QMetaType::MovableType0x4具有此标志的类型实例可以通过 memcpy() 安全地移动。
QMetaType::IsEnumeration0x10此类型是枚举值。
QMetaType::PointerToQObject0x8此类型是指向继承自 QObject 的类型的指针。

TypeFlags 类型是 QFlags<TypeFlag> 的别名,支持通过操作合并不同的 TypeFlag 值。

成员函数文档

QMetaType::QMetaType(const int typeId = QMetaType::UnknownType)

构造一个包含 typeId 对应的类型信息的 QMetaType 对象。

注意: 默认参数在 Qt 5.15 中被引入。

此函数在 Qt 5.0 中被引入。


QMetaType::~QMetaType()

析构此对象。


[static] bool QMetaType::compare(const void *lhs, const void *rhs, int typeId, int *result)

比较 lhsrhs 对象,双方都需要是 typeid 中的类型。result 会被设为小于、等于或大于零,表示 lhs 小于、等于或大于 rhs。若比较成功则返回 true,否则返回 false

此函数在 Qt 5.2 中被引入。


[static] void *QMetaType::construct(int type, void *where, const void *copy)

在给定的内存地址 where 上构造对应 type 类型的对象,该对象是 copy 的副本,并返回 where。若 copy 是空指针,则执行默认构造。

这是用于显示管理存储 type 类型对象的内存的底层函数。若不需要此类底层控制,则考虑使用 create() 函数(也就是指,使用 new 而非 placement new)。

您必须确保 where 指向的内存区域大小足够存储 type 对应的数据,并且 where 地址需要对齐,对应类型的大小可通过 sizeOf() 获取。

内存对齐的规则是对齐至类型的自然边界,也就是大于等于类型大小的2的n次方值,直至平台有效对齐宽度上限为止。对于特定用途来说,超过 2 * sizeof(void*) 的对齐宽度只是某些特定硬件指令所必需的(例如,x86 平台中对齐后的 SSE 读取和存储)。

此函数在 Qt 5.0 中被引入。

另请参阅:destruct() 和 sizeOf()。


void *QMetaType::construct(void *where, const void *copy = 0) const

在给定的内存地址 where 上构造此 QMetaType 类型的对象,该对象是 copy 的副本,并返回 where。若 copy 是空指针,则执行默认构造。

这是用于显示管理存储 type 类型对象的内存的底层函数。若不需要此类底层控制,则考虑使用 create() 函数(也就是指,使用 new 而非 placement new)。

您必须确保 where 指向的内存区域大小足够存储 type 对应的数据,并且 where 地址需要对齐,对应类型的大小可通过 sizeOf() 获取。

内存对齐的规则是对齐至类型的自然边界,也就是大于等于类型大小的2的n次方值,直至平台有效对齐宽度上限为止。对于特定用途来说,超过 2 * sizeof(void*) 的对齐宽度只是某些特定硬件指令所必需的(例如,x86 平台中对齐后的 SSE 读取和存储)。

此函数在 Qt 5.0 中被引入。


[static] bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId)

from 对象从 fromTypeId 转换至 toTypeId 并存储到预分配空间 to 中。若转换成功则返回 true,否则返回 false

此函数在 Qt 5.2 中被引入。


[static] void *QMetaType::create(int type, const void *copy = nullptr)

假设 copy 的类型是 type,返回它的的拷贝。若 copy 是空指针,则返回默认构造的实例。

另请参阅:destroy(),isRegistered() 和 Type


void *QMetaType::create(const void *copy = 0) const

假设 copy 的类型是此 QMetaType ,返回它的的拷贝。若 copy 是空指针,则返回默认构造的实例。

此函数在 Qt 5.0 中被引入。

另请参阅:QMetaType::destroy()。


[static] bool QMetaType::debugStream(QDebug &dbg, const void *rhs, int typeId)

typeId 类型的 rhs 对象输出至调试流 debug,输出成功则返回 true,否则返回 false

此函数在 Qt 5.2 中被引入。


[static] void QMetaType::destroy(int type, void *data)

假设 data 的类型是 type,销毁该对象。

另请参阅:create(),isRegistered() 和 Type


void QMetaType::destroy(void *data) const

假设 data 的类型是此 QMetaType ,销毁该对象。

此函数在 Qt 5.0 中被引入。

另请参阅:QMetaType::create()。


[static] void QMetaType::destruct(int type, void *where)

假设 where 地址中存储的对象类型是 type,销毁该对象。

destroy() 不同,此函数只会执行该类型的析构函数,但不会执行 delete 运算符(译者注:即不会释放内存,与 placement new 相同机制)。

此函数在 Qt 5.0 中被引入。

另请参阅:construct()。


void QMetaType::destruct(void *data) const

假设 data 地址中存储的对象类型是此 QMetaType ,销毁该对象。

destroy() 不同,此函数只会执行该类型的析构函数,但不会执行 delete 运算符(译者注:即不会释放内存,与 placement new 相同机制)。

此函数在 Qt 5.0 中被引入。

另请参阅:QMetaType::construct()。


[static] bool QMetaType::equals(const void *lhs, const void *rhs, int typeId, int *result)

比较 lhsrhs 对象,双方都需要是 typeid 中的类型。若 lhs 等于 rhs,则 result 会被设为零。若比较成功则返回 true,否则返回 false

此函数在 Qt 5.5 中被引入。


QMetaType::TypeFlags QMetaType::flags() const

返回此 QMetaType 实例的类型标志。

此函数在 Qt 5.0 中被引入。

另请参阅:QMetaType::TypeFlagsQMetaType::typeFlags()。


[static] template <typename T> QMetaType QMetaType::fromType()

返回模板类型 T 对应的 QMetaType 实例。

此函数在 Qt 5.15 中被引入。


[static] template <typename T> bool QMetaType::hasRegisteredComparators()

若模板类型 T 已被注册至元对象系统则返回 true

此函数在 Qt 5.2 中被引入。


[static] bool QMetaType::hasRegisteredComparators(int typeId)

typeId 的类型已被注册至元对象系统则返回 true

此函数在 Qt 5.2 中被引入。


[static] bool QMetaType::hasRegisteredConverterFunction(int fromTypeId, int toTypeId)

若自 fromTypeIdtoTypeId 的类型转换已被注册至元对象系统则返回 true

此函数在 Qt 5.2 中被引入。


[static] template <typename From, typename To> bool QMetaType::hasRegisteredConverterFunction()

若自模板类型 FromTo 的类型转换已被注册至元对象系统则返回 true

这是一个重载函数。

此函数在 Qt 5.2 中被引入。


[static] template <typename T> bool QMetaType::hasRegisteredDebugStreamOperator()

若自模板类型 TQDebug 流运算符已被注册至元对象系统则返回 true

此函数在 Qt 5.2 中被引入。


[static] bool QMetaType::hasRegisteredDebugStreamOperator(int typeId)

若自 typeId 对应类型的 QDebug 流运算符已被注册至元对象系统则返回 true

此函数在 Qt 5.2 中被引入。


int QMetaType::id() const

返回此 QMetatype 实例的类型编号。

此函数在 Qt 5.13 中被引入。


[static] bool QMetaType::isRegistered(int type)

typeId 对应已被注册至元对象系统则返回 true,否则返回 false

另请参阅:type(),typeName() 和 Type


bool QMetaType::isRegistered() const

若此 QMetaType 包含某类型的有效信息则返回 true,否则返回 false

此函数在 Qt 5.0 中被引入。


bool QMetaType::isValid() const

若此 QMetaType 包含某类型的有效信息则返回 true,否则返回 false

此函数在 Qt 5.0 中被引入。


[static] bool QMetaType::load(QDataStream &stream, int type, void *data)

从数据流 stream 中读取对应 type 类型的对象至 data 中,若读取成功则返回 true,否则返回 false

此类型必须在这之前通过 qRegisterMetaType() 和 qRegisterMetaTypeStreamOperators() 完成注册。

通常来说,您不需要显示调用此函数,而是应使用 QVariantoperator>>(),该运算符依赖 load() 来传递自定义类型。

另请参阅:save() 和 qRegisterMetaTypeStreamOperators()。


const QMetaObject *QMetaType::metaObject() const

返回此类型对应的 QMetaObject

若此类型是 QObject 子类的指针,即 flags() 包含 QMetaType::PointerToQObject,则此函数会返回对应类型的 QMetaObject。这可被用于结合 QMetaObject::construct译者注:无此函数,请使用 QMetaObject::constructor QMetaType::construct)来创建此类型的 QObject 实例。

若此类型是 Q_GADGET,即 flags() 包含 QMetaType::IsGadget译者注:文档中未给出,但 QMetaType::TypeFlag 中的确包含此枚举值),则此函数会返回对应类型的 QMetaObject。这可以被用于获取 QMetaMethodQMetaProperty,并将其用于此类型的对象指针上(例如通过 QVariant::data 获取指针 译者注:文档中无此函数,但此函数的确存在)。

若此类型是枚举,即 flags() 包含 QMetaType::IsEnumeration,且该枚举值是通过 Q_ENUM 注册的成员枚举类型,则此函数会返回其所属的 QObject 对象的元对象,否则返回 nullptr

此函数在 Qt 5.5 中被引入。

另请参阅:QMetaType::metaObjectForType() 和 QMetaType::flags()。


[static] const QMetaObject *QMetaType::metaObjectForType(int type)

返回 type 类型对应的 QMetaType::metaObject

此函数在 Qt 5.0 中被引入。

另请参阅:metaObject()。


::QByteArray QMetaType::name() const

返回此 QMetaType 对应的类型名称,若无有效类型则返回空指针。

此函数在 Qt 5.15 中被引入。

另请参阅:typeName()。


[static] template <typename T> bool QMetaType::registerComparators()

将用户注册类型 T 的比较运算符注册至元对象系统。要求 T 具有 operator==operator< 运算符。若注册成功则返回 true,否则返回 false

此函数在 Qt 5.2 中被引入。


[static] template <typename From, typename To> bool QMetaType::registerConverter()

将类型 FromTo 的可能的隐式转换注册到元对象系统,若注册成功则返回 true,否则返回 false

此函数在 Qt 5.2 中被引入。


[static] template <typename MemberFunction, int> bool QMetaType::registerConverter(MemberFunction function)

这是一个重载函数。

将形如 To From::function() const 的成员方法 function 作为从 FromTo 的转换函数注册至元对象系统,若注册成功则返回 true,否则返回 false

此函数在 Qt 5.2 中被引入。

译者注:

第二个模板参数是官方使用 doxygen 生成文档时的变通写法,实际代码中的函数签名是 template<typename From, typename To> static bool registerConverter(To(From::*function)() const)。使用时无需指定 int 模板参数,在函数参数中直接填入用于转换的成员函数指针即可。


[static] template <typename MemberFunctionOk, char> bool QMetaType::registerConverter(MemberFunctionOk function)

这是一个重载函数。

将形如 To From::function(bool *ok) const 的成员方法 function 作为从 FromTo 的转换函数注册至元对象系统,若注册成功则返回 true,否则返回 false

此函数在 Qt 5.2 中被引入。

译者注:

第二个模板参数是官方使用 doxygen 生成文档时的变通写法,实际代码中的函数签名是 template<typename From, typename To> static bool registerConverter(To(From::*function)(bool*) const)。使用时无需指定 char 模板参数,在函数参数中直接填入用于转换的成员函数指针即可。


[static] template <typename UnaryFunction> bool QMetaType::registerConverter(UnaryFunction function)

这是一个重载函数。

将把类型 From 转换为类型 To 的一元函数 function 注册至元对象系统,若注册成功则返回 true,否则返回 false

此函数在 Qt 5.2 中被引入。

译者注:

原文描述地非常晦涩,实际指的是任何可被 To dst = function(src) 方式调用的函数对象,包括全局函数、类静态函数、仿函数或 lamba 等,比上文另外两个 registerConverter 的约束更为宽松。


[static] template <typename T> bool QMetaType::registerDebugStreamOperator()

将已注册类型 TQDebug 流运算符注册至元对象系统,要求类型 T 具备流运算符 operator<<(QDebug dbg, T)。若注册成功则返回 true,否则返回 false


[static] template <typename T> bool QMetaType::registerEqualsComparator()

将已注册类型 T 的等号运算符注册至元对象系统,要求类型 T 具备等号运算符 operator==。若注册成功则返回 true,否则返回 false

此函数在 Qt 5.5 中被引入。


[static] bool QMetaType::save(QDataStream &stream, int type, const void *data)

从数据流 stream 中读取对应 type 类型的对象至 data 中,若读取成功则返回 true,否则返回 false

此类型必须在这之前通过 qRegisterMetaType() 和 qRegisterMetaTypeStreamOperators() 完成注册。

通常来说,您不需要显示调用此函数,而是应使用 QVariantoperator>>(),该运算符依赖 load() 来传递自定义类型。

另请参阅:save() 和 qRegisterMetaTypeStreamOperators()。

type 类型对应的 data 对象输出至数据流 stream 中,若读取成功则返回 true,否则返回 false

此类型必须在这之前通过 qRegisterMetaType() 和 qRegisterMetaTypeStreamOperators() 完成注册。

通常来说,您不需要显示调用此函数,而是应使用 QVariantoperator<<(),该运算符依赖 save() 来传递自定义类型。

另请参阅:load() 和 qRegisterMetaTypeStreamOperators()。


[static] int QMetaType::sizeOf(int type)

返回 type 对应类型的以字节为单位的大小(即 sizeof(T),其中 Ttype 对应的实际类型)。

此函数通常结合 construct() 使用,来进行对此类型的更底层的内存管理。

此函数在 Qt 5.0 中被引入。

另请参阅:construct()。


int QMetaType::sizeOf() const

返回此类型的以字节为单位的大小(即 sizeof(T),其中 TQMetaType 对应的实际类型)。

此函数通常结合 construct() 使用,来进行对此类型的更底层的内存管理。

此函数在 Qt 5.0 中被引入。

另请参阅:QMetaType::construct() 和 QMetaType::sizeOf()。


[static] int QMetaType::type(const char *typeName)

返回名为 typeName 的类型的元类型编号,若无此元类型则返回 QMetaType::UnknownType

另请参阅:isRegistered(),typeName() 和 Type


[static] int QMetaType::type(const ::QByteArray &typeName)

这是一个重载函数。

返回名为 typeName 的类型的元类型编号,若无此元类型则返回 0译者注:即QMetaType::UnknownType)。

此函数在 Qt 5.5 中被引入。

另请参阅:isRegistered() 和 typeName()。


[static] QMetaType::TypeFlags QMetaType::typeFlags(int type)

返回 type 类型的类型标志。

此函数在 Qt 5.0 中被引入。

另请参阅:QMetaType::TypeFlags


[static] const char *QMetaType::typeName(int typeId)

返回 typeId 对应类型的类型名称,若该类型不存在则返回空指针。返回的指针不可被删除。

另请参阅:type(),isRegistered(),Typename()。

相关非成员函数

template <typename T> int qMetaTypeId()

返回类型 T 对应的元类型编号。若该类型未通过 Q_DECLARE_METATYPE() 声明,则会引发编译错误。

典型用法:

int id = qMetaTypeId<QString>();    // id 是 QMetaType::QString
id = qMetaTypeId<MyStruct>();       // 若 MyStruct 未被声明,则会产生编译错误

QMetaType::type() 返回值与 qMetaTypeId() 相同,但会基于类型名称进行运行时检索。QMetaType::type() 会稍慢一些,但即使类型未注册也能编译成功。

此函数在 Qt 4.1 中被引入。

另请参阅:Q_DECLARE_METATYPE() 和 QMetaType::type()。


template <typename T> int qRegisterMetaType(const char *typeName)

将类型 T 通过类型名称 typeName 注册至元对象系统,并返回 QMetaType 使用的类型编号。任何包含一个公共默认构造函数、公共拷贝构造函数、公共析构函数的类或结构体均可被注册。

此函数要求类型 T 在此函数调用时被完整定义;对于指针类型,同样要求被指向的类型被完整定义(译者注:即不可为前置声明类型)。可以使用 Q_DECLARE_OPAQUE_POINTER() 来注册前置声明类型的指针类型。

类型被注册后,可以在运行时动态地创建或销毁对象。

下述为注册 MyClass 类的示例:

qRegisterMetaType<MyClass>("MyClass");

此函数可被用于注册类型别名,以便于将别名用于 QMetaProperty队列连接中。

typedef QString CustomString;
qRegisterMetaType<CustomString>("CustomString");

警告: 此函数仅应被用于注册类型别名,其它场合请使用 Q_DECLARE_METATYPEqMetaTypeId()。

另请参阅:qRegisterMetaTypeStreamOperators(),isRegistered() 和 Q_DECLARE_METATYPE()。


template <typename T> int qRegisterMetaType()

调用此函数来注册类型 TT 必须被 Q_DECLARE_METATYPE() 所声明。返回此类型对应的元类型编号。

示例:

int id = qRegisterMetaType<MyStruct>();

此函数要求类型 T 在此函数调用时被完整定义;对于指针类型,同样要求被指向的类型被完整定义(译者注:即不可为前置声明类型)。可以使用 Q_DECLARE_OPAQUE_POINTER() 来注册前置声明类型的指针类型。

类型被注册后,可以在运行时动态地创建或销毁对象。

为了在 QVariant 中使用类型 T,使用 Q_DECLARE_METATYPE() 便已足够。若要在信号槽的队列连接中使用 T,则 qRegisterMetaType<T>() 必须在第一个连接建立前被调用。

同样地,若要在 QObject::property() 中使用 TqRegisterMetaType<T>() 必须在这之前被调用。通常在使用到 T 的类的构造函数中,或在 main() 函数中调用。

此函数在 Qt 4.2 中被引入。

另请参阅:Q_DECLARE_METATYPE()。


template <typename T> void qRegisterMetaTypeStreamOperators(const char *typeName)

通过类型名称 typeNameT 的流运算符注册至元对象系统。

在此之后,该类型可通过 QMetaType::load() 和 QMetaType::save() 进行序列化和反序列化。这两个函数在将 QVariant 传递至数据流时被调用。

qRegisterMetaTypeStreamOperators<MyClass>("MyClass");

流运算符需要具有下述的函数签名:

QDataStream &operator<<(QDataStream &out, const MyClass &myObj);
QDataStream &operator>>(QDataStream &in, MyClass &myObj);

另请参阅:qRegisterMetaType(),QMetaType::isRegistered() 和 Q_DECLARE_METATYPE()。


bool operator!=(const QMetaType &a, const QMetaType &b)

这是一个重载函数。

QMetaType a 的类型与 QMetaType b 不同则返回 true,否则返回false

此函数在 Qt 5.15 中被引入。


bool operator==(const QMetaType &a, const QMetaType &b)

这是一个重载函数。

QMetaType a 的类型与 QMetaType b 相同则返回 true,否则返回false

此函数在 Qt 5.15 中被引入。

宏定义文档

Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(Container)

此宏令容器类型 Container 作为关联型容器被注册至 QMetaType,即允许将 Container<T, U> 实例存入 QVariant,前提是 TU 也已经被注册为 QMetaType

注意: 所有 Qt 的关联型容器已被内置支持,无需使用此宏进行声明。std::map 容器也已被内置支持。

下述代码展示了 Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE() 的典型用法:

#include <unordered_list>

Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE(std::unordered_map)

void someFunc()
{
    std::unordered_map<int, bool> container;
    QVariant var = QVariant::fromValue(container);
    // ...
}

译者注:

用户的自定义类型只需要通过 Q_DECLARE_METATYPE(T) 注册后,即可被已注册的所有容器使用,无需再注册 Q_DECLARE_METATYPE(QMap<QString, T>)


Q_DECLARE_METATYPE(Type)

此宏将类型 Type 注册至 QMetaType ,前提是该类型具备一个公共默认构造函数、公共拷贝构造函数和公共析构函数。这是把类型 Type 用于 QVariant 的前提。

此宏要求类型 T 在此函数调用时被完整定义;对于指针类型,同样要求被指向的类型被完整定义(译者注:即不可为前置声明类型)。可以使用 Q_DECLARE_OPAQUE_POINTER() 来注册前置声明类型的指针类型。

理想情况下,此宏应被放置在该类型的声明位置之后。若不可行的话,也可以将其放置在一个私有头文件中,然后在每次在 QVariant 中使用此类型之前包含该头文件。

Q_DECLARE_METATYPE() 使此类型可被所有基于模板的函数使用,包括 QVariant 中的模板函数。注意,若想在信号槽的队列连接或 QObject 的属性系统中使用此类型,则还需要调用 qRegisterMetaType(),因为该类型的名称会在运行时被解析。

此示例为 Q_DECLARE_METATYPE() 的典型用法:

struct MyStruct
{
    int i;
    ...
};

Q_DECLARE_METATYPE(MyStruct)

MyStruct 处于命名空间中,则 Q_DECLARE_METATYPE() 宏必须在命令空间外使用:

namespace MyNamespace
{
    ...
}

Q_DECLARE_METATYPE(MyNamespace::MyStruct)

MyStruct 被注册至 QMetaType 后,便可将其用于 QVariant 中”

MyStruct s;
QVariant var;
var.setValue(s); // 将 v 拷贝至 QVariant

...

// 获取类型值
MyStruct s2 = var.value<MyStruct>();

下述类型已被自动注册,无需使用此宏:

另请参阅:qRegisterMetaType()。


Q_DECLARE_OPAQUE_POINTER(PointerType)

此宏使得前置声明类型的指针类型 PointerType 可被 Q_DECLARE_METATYPE() 或 qRegisterMetaType() 注册至 QMetaType

此函数在 Qt 5.0 中被引入。

另请参阅:Q_DECLARE_METATYPE() 和 qRegisterMetaType()。


Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(Container)

此宏令容器类型 Container 作为顺序容器被注册至 QMetaType,即允许将 Container<T> 实例存入 QVariant,前提是 T 已经被注册为 QMetaType

注意: 所有 Qt 的顺序容器已被内置支持,无需使用此宏进行声明。std::vectorstd::list 容器已也被内置支持。

下述代码展示了 Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE() 的典型用法:

#include <deque>

Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(std::deque)

void someFunc()
{
    std::deque<QFile*> container;
    QVariant var = QVariant::fromValue(container);
    // ...
}

译者注:

用户的自定义类型只需要通过 Q_DECLARE_METATYPE(T) 注册后,即可被已注册的所有容器使用,无需再注册 Q_DECLARE_METATYPE(QVector<T>)


Q_DECLARE_SMART_POINTER_METATYPE(SmartPointer)

此宏令智能指针类型 SmartPointer 作为智能指针被注册至 QMetaType,即允许将 Container<T> 实例存入 QVariant,前提是 T 已经被注册为 QMetaType

注意:QWeakPointerQSharedPointerQPointer 已被内置支持,无需使用此宏进行声明。

下述代码展示了 Q_DECLARE_SMART_POINTER_METATYPE() 的典型用法:

#include <memory>

Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)

void someFunc()
{
    auto smart_ptr = std::make_shared<QFile>();
    QVariant var = QVariant::fromValue(smart_ptr);
    // ...
    if (var.canConvert<QObject*>()) {
        QObject *sp = var.value<QObject*>();
        qDebug() << sp->metaObject()->className(); // Prints 'QFile'.
    }
}

译者注:

用户继承自 QObject 的自定义类型可直接被已注册的智能指针使用,无需再注册 Q_DECLARE_METATYPE(QSharedPointer<T>)

与容器不同的是,通过 Q_DECLARE_METATYPE(T) 注册的自定义类型无法直接被已注册的智能指针使用,必须单独注册 Q_DECLARE_METATYPE(QSharedPointer<T>)

已废弃成员

**QMetaType 类的以下成员已被废弃。**它们仅为了保证老代码能够运行而保留,我们强烈反对在新代码中使用。

静态公共成员

返回类型函数
(obsolete) void *construct(int type, const void *copy = nullptr)

成员函数文档

[static] void *QMetaType::construct(int type, const void *copy = nullptr)

在给定的内存地址 where 上构造对应 type 类型的对象,该对象是 copy 的副本,并返回 where。若 copy 是空指针,则执行默认构造。

这是用于显示管理存储 type 类型对象的内存的底层函数。若不需要此类底层控制,则考虑使用 create() 函数(也就是指,使用 new 而非 placement new)。

您必须确保 where 指向的内存区域大小足够存储 type 对应的数据,并且 where 地址需要对齐,对应类型的大小可通过 sizeOf() 获取。

内存对齐的规则是对齐至类型的自然边界,也就是大于等于类型大小的2的n次方值,直至平台有效对齐宽度上限为止。对于特定用途来说,超过 2 * sizeof(void*) 的对齐宽度只是某些特定硬件指令所必需的(例如,x86 平台中对齐后的 SSE 读取和存储)。

此函数在 Qt 5.0 中被引入。

此函数已被废弃,仅为了保证老代码能够运行而保留,我们强烈反对在新代码中使用。

构造对应 type 类型的对象,该对象是 copy 的副本。copy 的默认值是 nullptr

已弃用,该用新的静态函数 QMetaType::create(int type, const void *copy)。

使用元对象编译器(moc)

元对象编译器 moc 是用于处理 Qt 的 C++ 扩展 的程序。

moc 工具会阅读 C++ 头文件。若在类定义中发现了 Q_OBJECT 宏,则会建立一个 C++ 源文件,在其中包含了这些类的元对象代码。除此之外,元对象代码也被用于信号槽机制、运行时类型信息和动态属性系统。

moc 生成的 C++ 源文件需要随对应类的实现一同参与编译和链接。

若使用 qmake 来创建构建文件,则会自动生成按需调用 moc 的构建规则,因此无需显示调用 moc。若想了解更多 moc 的背景信息,详见 为何 Qt 使用 Moc 实现信号槽?

使用方式

moc 通常会输入一个包含类声明的文件,例如:

class MyClass : public QObject
{
    Q_OBJECT

public:
    MyClass(QObject *parent = 0);
    ~MyClass();

signals:
    void mySignal();

public slots:
    void mySlot();
};

除了上文中的信号槽,moc 在下一个范例中也被用于实现对象的属性。Q_PROPERTY() 宏定义了一条属性,Q_ENUM() 则定义了一组在类内部可被 property system 使用的枚举类型。

在下述范例中,我们声明了一个使用枚举类型 Priority 的属性,该属性同样被命名为 priority,具备一个读函数 priority() 和一个写函数 setPriority()

class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Priority priority READ priority WRITE setPriority)
    Q_ENUMS(Priority)

public:
    enum Priority { High, Low, VeryHigh, VeryLow };

    MyClass(QObject *parent = 0);
    ~MyClass();

    void setPriority(Priority priority) { m_priority = priority; }
    Priority priority() const { return m_priority; }

private:
    Priority m_priority;
};

Q_FLAGS() 宏用于声明被用为标志位的枚举,即可以被操作合并的值。另一个宏 Q_CLASSINFO() 则允许您为类的元对象添加名称/值 配对的附加信息。

class MyClass : public QObject
{
    Q_OBJECT
    Q_CLASSINFO("Author", "Oscar Peterson")
    Q_CLASSINFO("Status", "Active")

public:
    MyClass(QObject *parent = 0);
    ~MyClass();
};

moc 产生的输出必须被编译并链接,就如同程序中的其它 C++ 代码一样,否则构建过程会在最终的链接阶段失败。若使用 qmake,则这些操作会被自动完成。当 qmake 运行时,它会解析项目的所有头文件,为包含 Q_OBJECT 宏的文件生成调用 moc 的构建规则。

若类定义在 myclass.h 文件中,则 moc 的输出会被保存在 moc_myclass.cpp 文件中。该文件会如常规源文件一般,被编译生成为对象文件,如 Windows 中的 moc_myclass.obj 。该对象文件应被包含在链接对象文件列表中,用于参与程序最终的链接阶段。

编写调用 mocmake 规则

除了最简单的测试程序外,其它情况下都建议自动执行 moc。通过向 makefile 中添加一组规则,make 会在必要时执行 moc 并处理其输出内容。

我们推荐使用 qmake 生成工具来构建您的 makefile。此工具会生成一个 makefile 文件来处理所有 moc 操作。

若您打算自行创建 makefile,可以参阅下文的小贴士来添加 moc 操作。

对于头文件中类声明内的 Q_OBJECT ,下文是使用 GNU makemakefile 规则:

moc_%.cpp: %.h
        moc $(DEFINES) $(INCPATH) $< -o $@

若想更加灵活,则可以通过如下格式编写独立的规则:

moc_foo.cpp: foo.h
        moc $(DEFINES) $(INCPATH) $< -o $@

您还需谨记将 moc_foo.cpp 添加到 SOURCES 变量(或者其它您喜欢的名称)中,并将 moc_foo.omoc_foo.obj 添加至 OBJECTS 变量。

上文的两个范例都假定 $(DEFINES)$(INCPATH) 变量会被展开为传递至 C++ 编译器的宏定义和包含路径,这些被 moc 用于对源文件进行预处理。

虽然我们倾向于将 C++ 源文件命名为 .cpp,您仍可根据喜好使用其它扩展名,如 .C.cc.CC.cxx.c++

对于在实现文件(.cpp)中的类声明内的 Q_OBJECT ,我们建议使用类似下文的 makefile 规则:

foo.o: foo.moc

foo.moc: foo.cpp
        moc $(DEFINES) $(INCPATH) -i $< -o $@

这确保了 moc 会在 foo.cpp 编译前执行,于是可以将:

#include "foo.moc"

添加到 foo.cpp文件末尾,即可以感知到该文件中所有类定义的位置。

命令行选项

下表为 moc 提供的命令行选项:

选项描述
-o<file>将输出写入 <file>,而非标注输出流(stdout)。
-f[<file>]强令输出内容中包含一条 #include 语句。对于扩展名是 .h.H 开头的头文件,此选项默认启用。此选项对于不遵循标准命名约定的头文件尤其有用。<file> 部分是可选的。
-i不生成 #include 语句至输出内容。此选项可被用于对包含一个或多个类声明的 C++ 文件执行 moc。您需要在 .cpp 中通过 #include 包含输出的元对象代码。
-nw不生成任何警告(不建议开启)。
-p<path>另生成的 #include 语句中附加上 <path>/ 前缀。
-I<dir>为头文件添加包含路径。
-E仅执行预处理,不生成元对象代码。
-D<macro>[=<def>]添加宏定义,通过可选参数指定值。
-U<macro>取消宏定义。
-M<key=value>为插件附加额外的元信息。若某个类指定了 Q_PLUGIN_METADATA,则该键值对会被添加至元数据。这些数据会出现在插件运行时解析的 json 对象中(通过 QPluginLoader)。此参数通常被用于为静态插件添加编译系统提供的信息。
@<file><file> 中读取额外的命令行选项。该文件的每一行文本都被当作一条独立的选项,空行会被忽略。注意,此选项不支持在参数文件内使用(即参数文件不能包含另一个参数文件)。
-h显示使用说明和选项列表。
-v显示 moc 的版本号。
-Fdir用于 macOS。将 dir 指定的框架路径添加到头文件搜索路径列表顶部。这些路径会和 -I 制定的路径一同排布,以自左向右的顺序检索(详见 gcc 的 manpage)。通常使用 -F /Library/Frameworks/

您可以显示告知 moc 不要解析头文件中的某些部分。moc 会定义预编译宏 Q_MOC_RUN,因此用

#ifndef Q_MOC_RUN
    ...
#endif

包裹的代码会被 moc 跳过。

诊断

mocQ_OBJECT 类声明中检测到某些危险的或非法的代码结构时,会发出警告。

若在程序编译的最终步骤中出现链接错误,内容为 YourClass::className() 未定义,或者 YourClass 缺少虚表(vtable),则意味着某些步骤出现错误。通常来说,您可能忘记编译 moc 生成的 C++ 代码,或者忘记 #include 它们,或者编译了但忘记将输出的对象文件添加到链接指令。若您是使用 qmake,尝试重新执行它来更新 makefile,这通常会很有用。

限制

moc 无法处理所有的 C++ 代码。最大的问题是模板类不能使用 Q_OBJECT 宏,例如:

class SomeTemplate<int> : public QFrame
{
    Q_OBJECT
    ...

signals:
    void mySignal(int);
};

此代码结构是非法的。我们认为这些场合下有其它替代手法,所以移除这些限制的优先级并不高。

多继承要求 QObject 处于首位

若使用多继承,moc 会假定第一个被继承的类是 QObject 的子类。同样的,必须确保只有第一个被继承的类是 QObject

// 正确
class SomeClass : public QObject, public OtherClass
{
    ...
};

QObject 不支持虚继承。

函数指针不能为信号槽参数

对于绝大多数使用函数指针作为信号槽参数的场景,我们都认为使用继承是更好的手段。下文是非法语法的示例:

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(void (*apply)(List *, void *), char *); // 错误
};

但可以通过如下方式绕过限制:

typedef void (*ApplyFunction)(List *, void *);

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(ApplyFunction, char *);
};

有时使用继承和虚函数来代替函数指针,会是更好的选择。

译者注:

moc 并未携带完整的编译器前端,因此在识别函数指针嵌套语法时可能存在障碍。

因此可以推测,moc 可能无法正确识别过于复杂的模板参数,此时建议使用 typedefusing 指定较为简洁的别名。

信号槽参数中的枚举类型和类型别名必须包含完整修饰

QObject::connect() 通过文本对比来检查参数签名是否匹配。因此,Alignment and Qt::Alignment 被当作两个不同的类型。为绕过此限制,请确保声明信号槽和建立连接时都时使用完整修饰的数据类型。例如:

class MyClass : public QObject
{
    Q_OBJECT

    enum Error {
        ConnectionRefused,
        RemoteHostClosed,
        UnknownError
    };

signals:
    void stateChanged(MyClass::Error error);
};

译者注:

这是 Qt 4 风格的 connect() 的历史问题,该接口使用 SIGNALSLOT 宏来生成信号槽参数,这两个宏的返回值是字符串类型,Qt 只能在运行时通过字符串匹配进行校验,极易产生纰漏。

同理,qRegisterMetaType<TypeName>("TypeName") 的函数参数,也是用于校验信号槽,也需遵循相同规范。

但若使用 Qt 5 的基于函数指针的方式,则无需考虑本小节的限制,也无需为 qRegisterMetaType<T>() 手动指定字符串名称。

更多信息,详见 QObject::connect()

嵌套类不能拥有信号槽

下文是错误代码结构的示例:

class A
{
public:
    class B
    {
        Q_OBJECT

    public slots:   // 错误
        void b();
    };
};

信号槽返回值不能为引用

信号槽可以具备返回值,但返回引用会被当作返回 void

只有信号槽可以出现在类的 signalsslots 区域中

若将信号槽之外的其它代码结构放置类的在 signalsslots 区域中,moc 将会报错。

译者注:

推荐使用 Q_SIGNALSQ_SLOTS 单独标注信号槽,这样可以更精细地排列类成员布局,使得信号槽与其它成员按照能够逻辑关系排布,而非被 signalsslots 区域强行隔离开。

另请参阅: 元对象系统信号槽Qt 的属性系统

为何 Qt 使用 Moc 实现信号槽?

模板是 C++ 的内建机制,可以允许编译器基于传递的参数类型,在编译期生成代码。因此,框架编写者很喜欢使用模板,而我们也的确在 Qt 的许多地方使用了高阶的模板特性。然而,模板有限制的:有的东西可以用模板很轻易地表达,但同样也会有几乎无法用模板表达的东西。一个通用的向量容器类很容易用模板表达,即使它在针对指针类型时使用到了偏特化特性;然而一个基于 XML 字符串的内容描述来构建用户界面的函数,就无法用模板来表达。并且,在它俩之间还有一块灰色区域。强行使用模板来实现功能,会付出代码体积、可读性、可移植性、可用性、可扩展性、健壮性乃至最根本的设计美感上的代价。C++ 的模板和 C 的宏都可以用于扩展语法,来实现不可思议又难以置信的奇思妙想,但这只代表这些想法是可行的,而未必意味着这么做是正确的设计思路。很不幸,编写代码,并不是用来写进书籍(译者注:即阳春白雪般的纸上谈兵),而是在真实世界的操作系统中,被真实世界的编译器所编译。

这就是为什么 Qt 使用 moc 的原因:

语法很重要

语法并不只是:用于描述我们的算法的语法,会显著地影响我们的代码的可读性和可维护性。Qt 的信号槽语法被证明是非常成功的实践,它的语法非常直观,易于使用也易于阅读。学习 Qt 时,这种语法可以帮助人们更好地理解和使用信号槽这个概念——尽管它本质上是很高阶的通用抽象。这令开发者们在刚踏入门槛时,就能做出正确的设计,甚至都不需要去思考何为设计模式。

代码生成是好东西

Qt 的 moc(Meta Object Compiler,元对象编译器) 提供了一种简洁的方式,来超越目标语言本身的桎梏。它通过生成额外的 C++ 代码来达成目标,而这些代码可以被任何标准的 C++ 编译器所编译。moc 会阅读 C++ 源码文件,如果找到任何类声明中包含了 Q_OBJECT 宏,就会生成另一个 C++ 源码文件,在其中填充这些类的元对象相关的代码。moc 生成的 C++ 源码文件必须与对应类的实现文件共同编译和链接(也可以被#include包含到对应类的源文件中)。但通常来说,moc 并不会被手动调用,而是被编译系统自动调用,因此不需要开发者做额外工作。

moc 并不是 Qt 使用的唯一一个代码生成器。另一个典型范例则是 uic(User Interface Compiler,用户界面编译器),它接受 XML 格式的用户界面描述,来生成 C++ 代码并初始化界面。在 Qt 之外,代码生成器同样也被广泛应用,例如 rpcidl,可以支撑应用程序或对象跨越进程甚至跨机器进行通信。又比如,各种各样的词法分析工具,如大名鼎鼎的 lexyacc,它们将语法规范作为输入,来生成可以实现这些规范的状态机代码。代码生成器的其它替代品有改造编译器、专有语言或者可视化编程工具——后者提供单向的对话框/向导,用于在设计阶段而非编译阶段生成各种晦涩的代码。相比于将我们的客户绑定在专用的 C++ 编译器上,或者绑定到特定的集成开发环境(IDE)中,我们允许他们使用任何他们喜欢的工具。相较于强迫开发者将生成的代码添加到代码库中,我们更鼓励他们将我们的工具添加到他们的构建系统中:更加干净、更加安全、更具有 UNIX 精神。

译者注:UNIX 精神指的是用小型单一工具组合完成任务,而非庞大的、无所不能的复杂工具。

用户界面是动态的

C++ 是一门标准化的、强大的、经过精心设计的通用型语言,它是唯一一门被应用于如此广泛的软件开发领域的语言,囊括了所有种类的应用程序,包括完整的操作系统、数据库服务器、高级图形应用乃至于通用桌面应用。C++ 成功的关键之一,便是在提供可伸缩的语言特性的同时,聚焦于最大化性能和最小化内存占用,同时还保持了对 ANSI C 的兼容性。

除此之外,C++ 也有一些缺点。例如在面对基于组件的用户界面开发时,C++ 的静态对象模型相较于 Objective-C 的动态消息机制便是很显著的劣势。对于高级数据库服务器或者操作系统而言的优秀特性,却并非用户界面前端设计的正确选择。拥有了 moc 后,我们可以将这个劣势转化为优势,为满足安全又高效的用户界面开发提供了充足的灵活性。

我们的成果已经超越了通过模板能够做到的任何事情。例如,我们可以拥有对象属性,还可以重载信号槽,这在一门把重载作为关键特性的语言中,会令开发者感到无比自然。我们的信号机制不会让对象大小增加任何一个字节,这意味着我们在添加新的信号的同时不会破坏二进制兼容性。

另一个好处是我们可以在运行时检索一个对象的信号和槽。我们可以通过名称(译者注:字符串)来做到类型安全地建立连接,而并不需要知道我们连接的对象使用的具体类型(译者注:包括对象类型和参数类型),这对于基于模板的解决方案来说是不可能的。这种运行时的自省机制为我们开启了新的可能,例如,可以通过 Qt Designer 的 XML 界面文件来生成图形界面,并完成信号槽的连接。

调用性能并不代表一切

Qt 的信号槽实现并不能和基于模板的解决方案一样快。尽管发射一个信号大约只有四次常规模板函数的调用开销,整个信号槽执行的过程也只被 Qt 尽量压缩到10倍函数调用开销。这并不令人意外,因为 Qt 的机制包括了通用的序列化、自省、不同线程间的队列执行以及极致的脚本化。它并不需要激进的内联和代码展开,但却提供了远超于这些代价的运行时安全性。Qt 的信号分发机制是安全的,而那些速度更快的基于模板的系统则不是。即使在发送一个信号至多个不同的接收者的过程中,您依然可以安全地删除这些接收者,而不会引发程序崩溃。如果没有这份安全性,您的程序可能会偶发性地崩溃,并伴随一个痛苦的调试过程,来修复错误地对已经被释放掉的内存进行的读写操作。

译者注:

本段翻译有待商榷,其中一句

Qt 的信号分发机制是安全的,而那些速度更快的基于模板的系统则不是。

对应的原文是

Qt's iterators are safe while those of faster template-based systems are not.

从字面理解,该句是指 Qt 容器的迭代器。然而结合上下文,该句应该也是用于描述信号槽机制的安全性,所以此处理解为信号分发过程中,遍历分发目标的安全性,即 iteration 操作。

然而,从 Qt 容器安全性上理解也未尝不可。虽然迭代器算法是和容器数据结构强相关,对于相同类型的容器,Qt 迭代器与 STL 迭代器算法本质上并无区别,但 STL 容器是由标准库实现,而不同平台的标准库实现是针对该平台高度特化的,这导致了即使是很规范的 C++ 代码,移植到不同平台后依然可能因为平台差异(如大小端、位宽、对齐)产生 bug——而这不会出现在 Qt 中,因为 Qt 的设计是宁可舍弃平台特化的性能,也要保证兼容性。

尽管如此,难道就不能用基于模板的方案来提升使用信号槽的应用的性能吗?尽管 Qt 的确在信号槽调用中增加了一点点额外开销,这个开销对于槽的整个执行过程只占了很小的比例。只要在槽里做了任何有效的操作,例如一些简单的字符串处理,那么调用时的额外开销就可以忽略不计了。Qt 的系统已经经过充分优化,以至于任何需要new/delete的操作(例如字符串操作或向模板容器中插入/删除对象),都比发射一次信号有更可观的开销。

例外:如果您在某个性能敏感任务的内部循环中使用了信号槽,并且确认它成为性能瓶颈,那么可以考虑将其更换为监听者模式(译者注:更通用的称呼是发布-订阅或生产者-消费者)。在这类场景中,您可能只需要一对一的连接方式。例如,如果有一个对象从网络中下载数据,使用信号来标识需要的数据已经接收到,会是明智的设计;但如果需要将每个字节逐一发送至一个消费者,就应该使用监听者模式而非信号槽。

不受限制

因为有 moc 来实现信号槽,我们可以添加更多模板所不能做到的东西。其中便有带作用域的翻译器,可以通过生成的 tr() 函数使用;还有一个先进的属性系统,具备运行时的自省和类型信息。属性系统具有一个显