QT的GUI设计


最早接触到这类设计其实是从 Borland C++ Builder 开始的,作为一个所谓的快速开发工具,其实我对其实现界面设计那块到现在都没有清晰的理解。后来接触了 Java 一段时间,那时候只懂得自己设计界面就是继承一个类,如主窗口或者 applet,然后在该类中添加很多其他的 component 作为其 protected 成员。可是很少考虑到怎么更方便的设计。因此,可以说接触到第一个这种设计思想的 GUI 库就是在 Qt 了。 不得不说 Qt 其实和 Java 很像,虽然说 Qt 是 C++ 写成,但是注意到它其实是单一祖先 QObject 一脉相承,通过 QMetaObject 实现的 RTTI,这多多少少和 Java单一祖先一致,但是 Qt 不排斥使用其他的 C++ class,只是失去了 signal/slot 机制。在 moc 的 man page 里面,其实介绍了 Qt 实现的种种局限性:
  • 我们无法使用 template 继承 QObject,换言之,下面代码无法被 moc 转换成为有效的 C++ compiler 可编译代码
    template
    class TemplateClass : public QObject {
      Q_OBJECT
      // ...
    public slots:
      // ...
    } ;
  • 使用 multiple inheritance 必须把 QObject 或者其子类放在第一个父类的位置(因为使用 Q_OBJECT 需要覆盖),然后继承别的类。颠倒后,如 g++ 会抱错,如
    multiclass.hpp:17: Warning: Class MultiClass inherits from two QObject subclasses NoneQtClass and QObject. This is not supported!
    moc_multiclass.cpp:39: error: ‘staticMetaObject’ is not a member of ‘NoneQtClass’
    moc_multiclass.cpp: In member function ‘virtual void* MultiClass::qt_metacast(const char*)’:
    moc_multiclass.cpp:55: error: ‘qt_metacast’ is not a member of ‘NoneQtClass’
    moc_multiclass.cpp: In member function ‘virtual int MultiClass::qt_metacall(QMetaObject::Call, int, void**)’:
    moc_multiclass.cpp:60: error: ‘qt_metacall’ is not a member of ‘NoneQtClass’
    make: *** [moc_multiclass.o] Error 1
    MultiClass 继承 QObject 和 NoneQtClass,插入的 Q_OBJECT 展开后,因为将 NoneQtClass 放在第一位后 MultiClass 的结构已经不是 QObject 在前面所以创建 staticMetaObject 出错,  【 帮客之家 www.Linuxidc.com 】 后面虚函数表也因为在后面所以导致使用的其实是前面那个 class 的 vtable。
  • 函数指针不能作为 signal/slot 的参数,这个可以用 typedef 克服,如
    class SomeClass : public QObject {
      Q_OBJECT
      //...
    public slots:
      // illegal
      void apply( void (*apply)(List *, void *), void * );
    };
    将被认为非法(主要是判断参数类型时会失败,记得 QMetaObject 存下来的是什么信息么?),但是
    typedef void (*ApplyFunctionType)( List *, void * );
    
    class SomeClass : public QObject {
      Q_OBJECT
      //...
    public slots:
      void apply( ApplyFunctionType, char * );
    };
    是可行的。
  • 友元声明最好不要放在 signal 和 slot 声明中,这很明显,因为多数情况下虽然根据宏替换 signals: 被替换为 protected:,slots 被替换为空,但是编译器处理 friend 可能并不完全这样无关的处理,理论上说标准的编译器应该能 work,如 g++ 4.3.3.
  • signal 和 slot 不能被 upgrade,这是因为 moc 需要一个完整的函数声明,而提升的时候声明是不完整的,如
    class SlotClass : public QObject {
      Q_OBJECT
    protected slots:
      int getValue() ;
    } ;
    
    class UpgradeSlotClass : public SlotClass {
      Q_OBJECT
    public slots:
      Slot::getValue ;
    } ;
    其中的 Slot::getValue 的提升在正常的情况是被允许的,可见报错的是 moc,
    /usr/bin/moc-qt4 -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4 -I. -I. -I. upgradeslotclass.hpp -o moc_upgradeslotclass.cpp
    upgradeslotclass.hpp:15: Error: Not a signal or slot declaration
  • signal 和 slot 的参数不能使用宏,这是因为 moc 不展开宏。
  • 嵌套类声明不应该出现在 signal 或者 slot 里面,也是因为 moc 不能处理的原因。
  • 构造函数不应出现在 signal 和 slot 里面,虽然是函数,但是没有必要。
  • Q_PROPERTY 宏声明属性时应在包含其读写函数的 public 节之前,写在同一个 public 里面不允许,注意
    #define Q_PROPERTY(text)
    其实在类声明时这句话没有任何作用,只是 moc 编译会产生对应的代码,而 moc 要求这部分必须以类似下面的方式书写
    class PropertyClass : public QObject {
      Q_OBJECT
      Q_PROPERTY( int value READ getValue WRITE setValue )
      int value ;
    public:
      PropertyClass() ;
      void setValue( int = 0 ) ;
      int getValue() const ;
    } ;
    这部分对应 moc 会产生如下代码
    int PropertyClass::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
    {
      _id = QObject::qt_metacall(_c, _id, _a);
      if (_id < _c ="=" _v =" _a[0];" _c ="=" _v =" _a[0];" _c ="=" _c ="=" _c ="=" _c ="=" _c ="=" _c ="=">

为什么在 QMetaObject 中提供支持 property 以及 Q_CLASSINFO 这些看起来完全没必要的东西呢?后者比如

Q_CLASSINFO("Version", "3.0.0")
这些其实从一个方面是对 GUI 设计程序提供支援。Qt 提供的 GUI 设计程序叫 Qt Designer(似乎 Qt Creator 也能 design?),这是一个图形界面,将需要的 widget 拖到窗口上,用适当的 layout 组织起来,在每一个 widget 被点中的时候有一个 property editor,可以在这里设置前面使用 Q_PROPERTY 声明并且是 DESIGNABLE 为 true 的属性,这就是所谓的 widget editing mode。另外还有 signal and slot editing mode,这里可以直接把发出 signal 的 widget 拖向 slot 的 widget,这会产生一根箭头,然后填写对应的 signal 和 slot 即可。在 buddy editing mode 里,我们将一些原则上不接受键盘响应 widget 拖向相关接受键盘响应的 widget,这将让他们具有等效力的处理键盘的能力。在 tab order mode 里面我们设置按 TAB 时遍历的顺序。另外还有 resource editor 供我们管理资源,如使用的图片,action editor 让我们编辑菜单上的 action(最后将菜单项 or 工具栏与之连接)。新加入的 QUiLoader 类允许 Qt 能像 glade 一样处理 XML 文件描述的界面,并动态生成。
  • 1
  • 2
  • 下一页

相关内容