风中落叶

顾毅的博客

QtDesigner

Qt备忘录之四:可视化窗体设计原理

| 2 条评论

可视化的窗体设计其实并不陌生,在MFC或者.net框架中都用的到。Qt也提供了类似的功能。不论是在Qt Creator中或是用VS-AddIn新建或打开Qt工程,默认都会使用Qt Designer对打开.ui文件进行编辑。用Qt的可视化功能搭建窗体操作流程与其他工具类似,本文将不同之处列举一下,并探究一下原理。本文中所用的简单示例可点此下载

Buddy及其设置

Buddy是Qt里面比较特殊的一种控件间关系。从效果上,这种关系可以简单表述为:当按下一个label对象的快捷键之后,焦点会被它的buddy接收。例如在Qt Designer里新建一个如下所示的窗体:

Buddy示例-1

其中,label对象的Text表示为Go to My &Buddy,即定义了Alt+B作为该label的快捷键。现在编译之后生成的窗体如下图所示,其中label的Text依旧是Go to My &Buddy,“&”标识还可见,按下Atl+B也没有任何效果。

没有编辑buddy是的窗体效果

Edit(编辑)——Edit Buddies(编辑伙伴)之后,可以编辑label对象的buddies。

编辑buddy

buddy箭头

编辑之后,可以看见buddy视图中,有一个从label指向PushButton的箭头,即表示这个PushButton作为label的buddy。返回Widget视图后,可以看见label的Text变为“Go to My Buddy”。

添加buddy之后的widget界面

检查label对象的属性,可以看见在buddy一栏出现了pushButton。

label属性中的buddy设置

编译之后,按下Alt+B,确实可以触发pushButton动作。

增加buddy后的窗体效果

建立同窗体signal与slot的连接

现在在这个工程中添加一个LCD Number对象,然后通过Edit(编辑)——Edit Signals/Slots(编辑信号/槽)建立pushButton与LCD Number的signal/slot连接。

Edit Signals and Slots

建立连接后,在signals/slots视图中,可以看见一个由pushButton指向LCD Number的箭头,箭头的两端分别是clicked()和hide(),即表示将pushButton的clicked()与LCD Number的hide()相连接。

Edit Signals and Slots 2

也可以通过查看signals & slots关系图,证明上述关系已建立。

Edit Signals and Slots 3

编译后运行,单击pushButton或者按Alt+B,LCD Number消失(隐藏)。程序执行如预期。

Edit Signals and Slots 4

把.ui炼成.h

关闭Qt Designer,用文本查看工具打开.ui文件,可以发现其实.ui文件是一个XML格式的文件。看看这里面有些什么:

qtdesignertest.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>QtDesignerTest</class><!--主类-->
 <widget class="QWidget" name="QtDesignerTest"><!--声明名为QtDesignerTest的QWidget对象-->
  <property name="geometry"><!--QWidget对象的geometry(几何)属性-->
   <rect>
    <x>0</x>
    <y>0</y>
    <width>315</width>
    <height>42</height>
   </rect>
  </property>
……

所有在Qt Designer中设计的对象,都会以对象/属性声明的方式在XML中体现,与一般XML的用法差别不大。体现Qt特点的buddy和signals/slots用如下的方法体现:

……
  <widget class="QLabel" name="label"><!--声明名为label的Qlabel对象-->
   <property name="geometry">
    <rect>
     <x>11</x>
     <y>11</y>
     <width>84</width>
     <height>16</height>
    </rect>
   </property>
   <property name="text"><!--label的text属性-->
    <string>Go to My &amp;Buddy</string><!--&符号被转换为&amp;-->
   </property>
   <property name="buddy"><!--buddy也是label的属性-->
    <cstring>pushButton</cstring><!--这里是指定pushButton作为label的buddy-->
   </property>
  </widget>
……

从这里再次印证buddy是QLabel的属性。只是这里将buddy的前后用<cstring>标签包起来,没有理解是什么意思。以后如果想明白了,就在这里补充。

……
<layoutdefault spacing="6" margin="11"/><!--这个似乎是默认的布局-->
 <resources/><!--这个标签很奇怪,只有尾没有头-->
 <connections><!--这一节就是定义signal和slot的连接了-->
  <connection>
   <sender>pushButton</sender>
   <signal>clicked()</signal>
   <receiver>lcdNumber</receiver>
   <slot>hide()</slot>
   <hints><!--这两个是Qt Designer中connect箭头的前后位置,其实对于程序没有作用的-->
    <hint type="sourcelabel">
     <x>199</x>
     <y>30</y>
    </hint>
    <hint type="destinationlabel">
     <x>263</x>
     <y>30</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>

这一段中,</resource>标签比较奇怪,只有结尾没有开头,不符合XML的规范。按我的理解,应该是指代这个标签之上的所有代码都是“资源”吧。Qt会将这个.ui文件转换为一个以ui_为前缀的.h文件,大概长这个样子:

ui_qtdesignertest.h

/********************************************************************************
** Form generated from reading UI file 'qtdesignertest.ui'
**
** Created by: Qt User Interface Compiler version 5.4.0
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_QTDESIGNERTEST_H
#define UI_QTDESIGNERTEST_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLCDNumber>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_QtDesignerTest
{
public:
    QLabel *label;
    QPushButton *pushButton;
    QLCDNumber *lcdNumber;

    void setupUi(QWidget *QtDesignerTest)
    {
        if (QtDesignerTest->objectName().isEmpty())
            QtDesignerTest->setObjectName(QStringLiteral("QtDesignerTest"));
        QtDesignerTest->resize(315, 42);
        label = new QLabel(QtDesignerTest);
        label->setObjectName(QStringLiteral("label"));
        label->setGeometry(QRect(11, 11, 84, 16));
        pushButton = new QPushButton(QtDesignerTest);
        pushButton->setObjectName(QStringLiteral("pushButton"));
        pushButton->setGeometry(QRect(110, 10, 86, 23));
        lcdNumber = new QLCDNumber(QtDesignerTest);
        lcdNumber->setObjectName(QStringLiteral("lcdNumber"));
        lcdNumber->setGeometry(QRect(208, 11, 64, 23));
        lcdNumber->setProperty("value", QVariant(3.14));
#ifndef QT_NO_SHORTCUT
        label->setBuddy(pushButton);
#endif // QT_NO_SHORTCUT

        retranslateUi(QtDesignerTest);
        QObject::connect(pushButton, SIGNAL(clicked()), lcdNumber, SLOT(hide()));

        QMetaObject::connectSlotsByName(QtDesignerTest);
    } // setupUi

    void retranslateUi(QWidget *QtDesignerTest)
    {
        QtDesignerTest->setWindowTitle(QApplication::translate("QtDesignerTest", "QtDesignerTest", 0));
        label->setText(QApplication::translate("QtDesignerTest", "Go to My &Buddy", 0));
        pushButton->setText(QApplication::translate("QtDesignerTest", "I'm The BUDDY", 0));
    } // retranslateUi

};

namespace Ui {
    class QtDesignerTest: public Ui_QtDesignerTest {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_QTDESIGNERTEST_H

这已经是c++的标准语法了,都看得明白,就是把刚刚在.ui里面实现的东西写成c++的一个类。其中setupUi(QWidget*)用于将各种对象根据给定的规则搭建窗体,retranslateUi(QWidget*)用于将与用户交互的各个对象的文字部分进行国际化设置(使用translate()函数)。

回到cpp文件中,我们看看界面是怎么形成的。

qtdesignertest.cpp

#include "qtdesignertest.h"
#include "ui_qtdesignertest.h"

QtDesignerTest::QtDesignerTest(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::QtDesignerTest)
{
    ui->setupUi(this);
}

QtDesignerTest::~QtDesignerTest()
{
    delete ui;
}

就一句,调用setupUi(QWidget*),也就是上面ui_qtdesignertest.h中的setupUi(QWidget*)。其中ui是UI::QtDesignerTest类的实例对象。这里的QtDesignerTest类和UI::QtDesignerTest类是不同的,ui_qtdesignertest.h中可以看到,UI::QtDesignerTest是由Ui_QtDesignerTest类继承而来。

由此产生一个想法:后面的工程中,直接继承Ui_QtDesignerTest类来实现界面的修改和功能添加应该也是可以的。

二零一五年三月十六日

顾毅 写于厦门

» 订阅本站:http://www.xiamengy.net/
» 除非注明,本站文章均属原创。转载请注明来源:风中落叶——顾毅的博客 » Qt备忘录之四:可视化窗体设计原理

共有2 条评论

  1. 脑公你认真工作的样子好帅呀呀呀口牙(花痴脸)

顾毅进行回复 取消回复

Required fields are marked *.