The Book of QT4 翻译:2.1.1-2.1.2 对话框和小部件

25 篇文章 0 订阅




The Book of QT4 翻译


QT程序设计艺术


---------------------------------------------------------------------------------


原名:The Book of QT 4:The Art of Building Qt Applications


译名:The Book of QT 4中文版:QT程序设计艺术


译者:张小可 mcxiaoke@gmail.com


---------------------------------------------------------------------------------


 


第二章 创建对话框的工具和方法


 


现在你对Qt有一个大概的了解了,我们将转向更实际的例子,学习这些类如何一起工作。我们的第一个扩展程序将在十进制,十六进制和二进制标记法之间转换数字。如图2.1所示。
这个程序的使用者可以在任何一个输入框中输入任何单个字节的数字(从0到255).此程序将更新另外两个行编辑器的数字为转换后的值。


********************************************************************
******************* 2.1 对话框和小部件的区别 *****************************
********************************************************************



这个程序的main()函数和1.1节中的“Hello, world!”程序几乎相同:



// byteConverter/main.cpp
#include <QApplication>
#include "ByteConverterDialog.h"
int main(int argc, char *argv[])
{
 QApplication a(argc, argv);
 ByteConverterDialog bc;
 bc.setAttribute(Qt::WA_QuitOnClose);
 bc.show();
 return a.exec();
}


 



只有一个不同:QLabel类被ByteConverterDialog取代。这个类继承自QDialog类,它的类定义在头文件ByteConverterDialog.h中。#include指令使用(")标记而不是三角括号,将头文件集成到程序代码中,因为这个文件和main.cpp在同一个目录中。



我们还设置了对话框的WA_QuitOnClose属性以确保当程序终止时对话框也会关闭。在前面的例子中这不是必须的,因为我们没有使用任何继承自QDialog的类作为主窗口。由于对话框通常仅仅提供中间信息,QDialog的这个属性默认不是激活的。毕竟,关闭一个对话框不应该种植整个程序,除非有重大错误。
我们使用include包围的ByteConverterDialog.h文件,包含三个预处理指令#ifndef标记, #define标记和#endif标记:



// byteConverter/ByteConverterDialog.h
#ifndef BYTECONVERTERDIALOG_H
#define BYTECONVERTERDIALOG_H
#include <QDialog>
class QLineEdit;
class ByteConverterDialog : public QDialog
{
 Q_OBJECT
public:
 ByteConverterDialog();
private:
 QLineEdit* decEdit;
 QLineEdit* hexEdit;
 QLineEdit* binEdit;
};
#endif


 



在C/C++编程中,当多个文件试图包含一个头文件时,使用include guards是避免发生问题的一个标准方法。这通常发生在由很多独立的开发模块组成的大型程序中。在这,第一次包含ByteConverterDialog.h时,关键词BYTECONVERTERDIALOG_H被定义。如果之后源文件再次包含ByteConverterDialog.h文件, #ifndef...endif指令使预处理器跳过头文件的内容。如果没有include guards,编译器将注意到关键词和类被重复定义,并发出一个错误信号。我们包含了头文件QDialog,因为ByteConverterDialog类继承自QDialog。为使QDialog的函数在ByteConverterDialog类外部可用,我们使用了public访问控制符。



class QLineEdit是一个前置类声明。ByteConverterDialog类的对象包含三个指向QLineEdit对象的私有变量,因此C++编译器需要知道QLineEdit是一个类以便处理ByteConverterDialog的声明,但是在那儿它不需要知道类的精确定义。


在所有继承自基类QObject的类中都必须使用Q_OBJECT宏,包括间接继承的类,因为没有它定义的函数,信号-槽机制无法工作。(更多内容见2.1.1节)
构造器是这个类唯一的公开函数。我们将指向转换器部件将要显示的行编辑器对象的指针储存在三个成员变量(decEdit, hexEdit和binEdit)中,因为我们希望在用户没有输入数据的时刻更新输入框,确保这三个行编辑器显示相同的文本。由于这是ByteConverterDialog类的详细实现,我们将它们声明为私有变量。



2.1.1 继承自QObject



正如前面提到的,当一个类直接或间接继承自QObject时,你总是必须使用Q_OBJECT宏。该宏定义了几个用于实现信号-槽机制的函数。不幸的是,如果在继承自QObject的类定义中漏掉了Q_OBJECT宏,编译器和连接器都不会报告错误。结果是,Qt将仍然不知道这个类的信号和槽,但在运行时相应的连接将无法工作。
当执行带有调试信息的已编译程序时,将会发出运行时警告:试图访问未知的信号或槽。错误消息如下:
Object::connect: No such slot QObject::decChanged(QString)



然而,这个错误消息有点不确定性。如果你写错了信号或槽的名字,或者参数列表不正确,也可能看到它。
每个使用了Q_OBJECT宏的文件都必须提交给命令行程序moc。moc通过信号-槽机制将代码转换为纯C++代码。
如果你使用qmake创建工程,qmake在所有的.pro文件中包含的的头文件和源文件中搜索Q_OBJECT宏。当它找到时,qmake基于那些文件的内容自动为moc生成必要的构建指令。



当然,为了使qmake正确工作,你必须在工程文件中指定头文件。使用qmake的HEADERS变量指定头文件,就像你使用SOURCES变量指定源文件一样:
#byteConverter/byteConverter.pro
TEMPLATE = app
SOURCES = main.cpp \
ByteConverterDialog.cpp
HEADERS = ByteConverterDialog.h



如果包含Q_OBJECT的原件没有经过moc的处理,链接器将会抱怨未知的符号,GCC发出如下错误消息:
ld: Undefined symbols:
vtable for ByteConverterDialog
ByteConverterDialog::staticMetaObject



如果你看这个错误消息,检查下面的项:
是否在工程文件中正确定义了HEADERS变量?
如果使用qmake重新生成Makefile文件问题是否解决了?



2.1.2 更复杂的布局



现在我们开始来实现ByteConverterDialog类。当创建这个类的实例时,构造方法通过创建ByteConverterDialog对象生成所有要显示的行编辑器部件,并将它们插入一个布局。然而,这并不像以前那么简单:为了使得程序在用户改变对话框大小时表现良好,我们需要使用嵌套布局。图2.2展示了Qt如何保证输入框总是出现在窗口的顶部,而退出按钮总是出现在窗口右下的角落。



图2.2:在对话框中Qt布局对大小变化的反应
但是不要惊慌:尽管这个构造方法的源代码有点长,但它仅仅使用了简单的函数:

// byteConverter/ByteConverterDialog.cpp
#include "ByteConverterDialog.h"
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
ByteConverterDialog::ByteConverterDialog()
{
// Generate the necessary layouts
QVBoxLayout* mainLayout = new QVBoxLayout(this);
QGridLayout* editLayout = new QGridLayout;
QHBoxLayout* buttonLayout = new QHBoxLayout;
mainLayout->addLayout(editLayout);
mainLayout->addStretch();
mainLayout->addLayout(buttonLayout);
// Generate the labels and line-edits and add them
// to the object pointed at by editLayout
QLabel* decLabel = new QLabel(tr("Decimal"));
QLabel* hexLabel = new QLabel(tr("Hex"));
QLabel* binLabel = new QLabel(tr("Binary"));
decEdit = new QLineEdit;
hexEdit = new QLineEdit;
binEdit = new QLineEdit;
editLayout->addWidget(decLabel, 0, 0);
editLayout->addWidget(decEdit, 0, 1);
editLayout->addWidget(hexLabel, 1, 0);
editLayout->addWidget(hexEdit, 1, 1);
editLayout->addWidget(binLabel, 2, 0);
editLayout->addWidget(binEdit, 2, 1);
// Create the Quit button and add it to the object pointed
// at by buttonLayout
QPushButton* exitButton = new QPushButton(tr("Quit"));
buttonLayout->addStretch();
buttonLayout->addWidget(exitButton);

...
图2.3显示了窗口部件及其涉及的布局。当我们解释代码时请不要忘记它。



mainLayout对象,是一个垂直箱子布局,负责整个对话框的布局。因此当调用它的构造方法时我们将指向ByteConverterDialog对象的指针传递给它。为了做到这一点我们使用了this指针,因为我们在ByteConverterDialog类自己的函数中。
图2.3:ByteConverterDialog使用的布局



editLayout对象负责标签和行编辑器部件的布局,为了整齐的堆放这些元素并排列为单独的一栏,我们使用网格布局。


我们第三个new调用创建的buttonLayout,将负责管理退出按钮。然而,当我们生成这个按钮和其它部件并将它们添加到editLayout 和buttonLayout之前,我们必须使用addLayout()方法将这两个布局添加到mainLayout。布局的addLayout()方法等价于窗口部件的addWidget()方法。如果你将窗口部件添加到一个没有与某个部件绑定的布局,你将在终端窗口收到一个运行时错误:
QLayout::addChildWidget: add layout to parent before adding children to layout.



而且窗口部件仍保持不可见的状态。因此,你总是应该首先生成你的类的基本布局,然后继续添加其它的布局“层”。
为保证输入框总是位于ByteConverterDialog窗口的顶部,退出按钮总是位于它的右下角,我们使用伸缩条。
图2.4:没有使用伸缩条时大小改变后的对话框



伸缩条占据窗口部件不需要的空间,因而在你的对话框中创建空白空间。如果你打算忽略我们例子中的伸缩条,窗口部件将占据整个空间。当用户扩大那样一个没有使用伸缩条的对话框时,他看到的和图2.4显示的类似。


为了避免这样,我们在使用addStretch()函数在editLayout和buttonLayout之间添加了一个伸缩条。



现在我们可以创建标签和行编辑器并将它们委托给editLayout.我们在私有类变量decEdit, hexEdit和binEdit中aocun行编辑器对象,因为我们希望通过其它函数的代码改变它们的内容。对于所有其它对象,我们可以在没有相应指针的情况下进行管理,因为我们不需要在构造方法外部访问它们。
为保证退出按钮总是显示在对话框的右下角,在调整按钮本身之前,我们首先用一个伸缩条填充水平布局buttonLayout。



通过使用QObject::addWidget()和QObject::addLayout()方法,我们将所有的窗口部件和次级布局添加到mainLayout或它的次级布局,这样保证了通过构造方法生成的所有对象都继承自ByteConverterDialog对象。因为它们现在形成了堆分配对象层级,所以QT的内存管理将为我们管理它们,我们不需要手动删除它们中的任何一个。当ByteConverterDialog对象被删除时,所有的自对象将自动消失。



仅仅为了创建一个事实上非常简单的对话框,我们就得写这么多无关紧要的代码,你是不是变得有些失望了呢?更好的方法在第三章,那一章解释如何使用Qt设计师创建对话框并自动生成代码。有关布局的更多的细节和背景知识将在第五章提供。


作者:张小可
出处:http://mcxiaoke.cnblogs.com/
转载:原创翻译,欢迎转载,不得用于商业目的,必须保留本文的署名张小可(包含链接).

Tag标签: 翻译,C++,QT4,GUI,The Book of Qt 4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值