Qt之QFlags及相关宏学习小结

122 篇文章 100 订阅

起源

准备为我们的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
};

后记

乱七八糟,暂记于此。


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值