Qt之QFlags及相关宏学习小结

Qt中QFlags与Q_ENUMS详解

起源

准备为我们的5轴工作台定义一下控制接口

  • 首先定义一个enum类型:5个枚举值分别代表各个轴

enum AxisId {
    Axis_X,
    Axis_Y,
    Axis_Z,
    Axis_R,
    Axis_T
};
  • 定义接口类
    • 移动到某个绝对位置 moveA
    • 移动一个相对量 moveR
    • 归零操作 doHome

class StageControl:public QObject
{
    Q_OBJECT
public:
    StageControl();
public slots:
    void moveA(const QMap<AxisId, double> &);
    void moveR(const QMap<AxisId, double> &);
    void doHome(const QList<AxisId>&);
...

两个移动操作应该没什么可说的,命令总要包含:哪些轴要运动,分别运动到何处(一开始用了两个QList,显然QMap更合适一点)

对于doHome,我们只需要控制哪些轴运动。一个QList就够了,可是总感觉不好。于是改成

enum AxisId{
    Axis_X = 0x01,
    Axis_Y = 0x02,
    Axis_Z = 0x04,
    Axis_R = 0x08,
    Axis_T = 0x10
};
class StageControl:public QObject
{
...
    void doHome(int axes);

这样以来,就可以 doHome(Axis_X|Axis_Y) 了。可是,参数是一个 int,怎么想怎么不安全。万一随便传了一个整数进来咋办?

QFlags

不止一次(其实是非常多次)见过这个类,却一直没看过它。

事到如今,翻翻Manual,瞄瞄源码,哦,原来QFlags是这么简单的一个东西,就是一个定义了|&^~等操作的模板类。

于是容易写下如下代码:

typedef QFlags<AxisId> AxisIds;

StageControl:public QObject
{
...
void doHome(AxisIds axes);

但是,这并不会工作,doHome(Axis_X|Axis_Y)会找不到最佳匹配函数。还需要我们定义一个全局的

AxisIds operator|(AxisId, AxisId);

函数。

而类型定义(typedef)和操作符重载正是宏Q_DECLARE_FLAGS

Q_DECLARE_FLAGS(AxisIds, AxisId)

和 宏Q_DECLARE_OPERATORS_FOR_FLAGS

Q_DECLARE_OPERATORS_FOR_FLAGS(AxisIds)

所做的!

  • :这两个宏均和Qt的元对象系统无关!!

Q_FLAGS 与 Q_ENUMS

与前面两个宏不同,这两个是被moc进行处理的宏。而对C++预处理器来说,它们只不过就是一个空格。

既然与元对象信息有关,就先瞄瞄QMetaObject的Manual。竟然!没有QFlag的影子,只有

  • QMetaEnum QMetaObject::enumerator(int index) const
  • int QMetaObject::enumeratorCount() const
  • int QMetaObject::enumeratorOffset() const
  • int QMetaObject::indexOfEnumerator(const char * name) const

这些东西!!

在细细看看Q_FLAGS的manual:

  • Note: This macro takes care of registering individual flag values with the meta-object system, so it is unnecessary to use Q_ENUMS() in addition to this macro.

它说了什么?它告诉我们用了Q_FLAGS,就没必要用Q_ENUMS了。

怎么回事?

写个例子测试一下看看:

  • 只使用 Q_FLAGS (而不用Q_ENUMS)注册元对象信息
  • 然后输出元对象中的枚举量信息

class StageControl:public QObject
{
    Q_OBJECT

public:
    enum AxisId{
        Axis_X = 0x01,
        Axis_Y = 0x02,
        Axis_Z = 0x04,
        Axis_R = 0x08,
        Axis_T = 0x10
    };
    //Q_ENUMS(AxisId)
    Q_DECLARE_FLAGS(AxisIds, AxisId)
    Q_FLAGS(AxisIds)

    StageControl()
    {
        const QMetaObject * mobj = metaObject();
        for (int i=mobj->enumeratorOffset(); i<mobj->enumeratorCount(); ++i) {
            QMetaEnum menum = mobj->enumerator(i);
            qDebug()<<"name: "<<menum.name();
            for (int ii=0; ii<menum.keyCount(); ++ii) {
                qDebug()<<menum.key(ii);
            }
        }
    }
};

结果如下:

name:  AxisIds 
Axis_X 
Axis_Y 
Axis_Z 
Axis_R 
Axis_T 

有点意思,我们可以对比一下,只使用 Q_ENUMS 时的结果

name:  AxisId 
Axis_X 
Axis_Y 
Axis_Z 
Axis_R 
Axis_T 

呵呵,两个都用的呢?

name:  AxisId 
Axis_X 
Axis_Y 
Axis_Z 
Axis_R 
Axis_T 
name:  AxisIds 
Axis_X 
Axis_Y 
Axis_Z 
Axis_R 
Axis_T

相当于元对象系统中有了两个QMetaEnum对象!!

再看点什么呢?

瞅瞅moc生成的文件

当Q_ENUMS和Q_FLAGS同时使用时,确实是生成两个QMetaEnum对象。

static const uint qt_meta_data_StageControl[] = {

 // content:
       6,       // revision
       0,       // classname
       0,    0, // classinfo
       0,    0, // methods
       0,    0, // properties
       2,   14, // enums/sets
       0,    0, // constructors
       0,       // flags
       0,       // signalCount

 // enums: name, flags, count, data
      13, 0x0,    5,   22,
      20, 0x1,    5,   32,

 // enum data: key, value
      28, uint(StageControl::Axis_X),
      35, uint(StageControl::Axis_Y),
      42, uint(StageControl::Axis_Z),
      49, uint(StageControl::Axis_R),
      56, uint(StageControl::Axis_T),
      28, uint(StageControl::Axis_X),
      35, uint(StageControl::Axis_Y),
      42, uint(StageControl::Axis_Z),
      49, uint(StageControl::Axis_R),
      56, uint(StageControl::Axis_T),

       0        // eod
};

static const char qt_meta_stringdata_StageControl[] = {
    "StageControl\0AxisId\0AxisIds\0Axis_X\0"
    "Axis_Y\0Axis_Z\0Axis_R\0Axis_T\0"
};

其他

一时间突然明白,为何在manual中

enum Qt::WindowType
flags Qt::WindowFlags

enum Qt::WindowState
flags Qt::WindowStates

总是成对的介绍!

原来,潜意识中,总是觉得WindowType的枚举值全是Widget/Window这种单值,而Dialog属于WindowFlags

    enum WindowType {
        Widget = 0x00000000,
        Window = 0x00000001,
        Dialog = 0x00000002 | Window,
        Sheet = 0x00000004 | Window,
        Drawer = 0x00000006 | Window,
        Popup = 0x00000008 | Window,
        Tool = 0x0000000a | Window,
        ...
    }
    Q_DECLARE_FLAGS(WindowFlags, WindowType)

原来根本就没有这种区别!

回到一开始的例子,我最常用的是X、Y、R这三个轴,于是,我可以这样来定义enum

enum AxisId{
    Axis_X = 0x01,
    Axis_Y = 0x02,
    Axis_Z = 0x04,
    Axis_R = 0x08,
    Axis_T = 0x10,
    Axis_2D = Axis_X|Axis_Y|Axis_R
};

后记

乱七八糟,暂记于此。


<think>好的,我现在需要回答用户关于QFlagsQt::MouseButtons的问题。首先,我得回忆一下自己对这两个概念的理解,确保准确无误。 QFlagsQt框架中的一个模板类,用于处理枚举值的组合,允许将多个枚举值进行位或操作,形成一个集合。这在处理如鼠标按钮、键盘修饰符等需要同时表示多个状态的情况下非常有用。比如,当用户同时按下左右键时,可以用QFlags来组合这两个按钮的状态。 接下来是Qt::MouseButtons,这是一个枚举类型,定义了各种鼠标按钮的标识符,如左键Qt::LeftButton、右键Qt::RightButton等。这些枚举值通常作为位标志使用,因此它们的值都是2的幂,方便进行位运算。当处理鼠标事件时,比如QMouseEvent的buttons()方法会返回Qt::MouseButtons类型,表示当前按下的所有按钮。 我需要确认用户可能的需求:他们可能是在开发Qt应用时遇到了鼠标事件处理的问题,或者想了解如何同时检测多个鼠标按钮的按下状态。因此,在回答时应该结合使用场景,说明如何用QFlags处理这些枚举值的组合,例如如何检查某个按钮是否被按下,或者如何添加、移除按钮。 同时,要注意示例的正确性,比如使用testFlag()方法来检测单个按钮,而不要直接使用位与操作,因为QFlags可能有更安全的方法。此外,需要提到类型安全,QFlags如何帮助避免类型错误,以及其在Qt中的广泛应用,如键盘修饰符Qt::KeyboardModifiers。 需要避免错误,例如混淆QFlags和普通的枚举,或者错误地解释位操作。确保说明Qt::MouseButtons是枚举,而QFlags<Qt::MouseButton>是其对应的标志类型,但实际使用时,Qt已经为MouseButtons提供了typedef,可能直接使用Qt::MouseButtons即可,因为QFlags已经被封装好了。 可能还需要提到在信号槽中的使用,比如传递按钮状态时如何处理,以及在进行位运算时的注意事项,比如组合按钮使用按位或运算符。 最后,总结两者的关系和用途,强调QFlags的作用和Qt::MouseButtons的具体应用场景,确保用户能够理解如何在实际代码中使用它们来处理多按钮状态。</think>在 Qt 框架中,`QFlags` 和 `Qt::MouseButtons` 是处理枚举值组合的重要工具,尤其在处理鼠标事件时非常常见。以下是详细解释: --- ### **1. QFlags** - **是什么?** `QFlags` 是一个模板类,用于将枚举值(通常设计为位标志)组合成一个集合。它支持位运算(如 `|` 或、`&` 与、`~` 非等),方便高效地管理多个状态或选项。 - **为什么需要它?** Qt 中许多枚举值被设计为“位标志”(例如鼠标按钮、键盘修饰键),每个值对应一个二进制位。通过 `QFlags`,可以将多个标志组合成一个值,例如同时表示“左键+右键按下”。 - **关键方法** - `operator|`:组合多个标志。 - `testFlag()`:检查是否包含某个标志。 - `operator&`:过滤出包含的标志。 - **示例** ```cpp QFlags<Qt::MouseButton> buttons = Qt::LeftButton | Qt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值