The Book of Qt 4 翻译:2.1.3-2.1.4 可用性和槽

来源:互联网 发布:知微 数据 编辑:程序博客网 时间:2024/04/29 12:09

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

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

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

 

2.1.3 增加可用性

 

除开改良了的布局,这个对话框在下列几个方面表现的并不完美:目前窗口标题显示的是程序的名字byteConverter。一些东西可以更具描述性。退出按钮应该成为对话框的默认按钮,默认按钮即使即使当前没有键盘焦点,也可以由回车键激活。大部分窗口部件以一种特殊的样式高亮默认按钮。现在你可以在行编辑器中输入任何数字。我们应该限制用户只能输入正确的值,即十进制数字智能在0到255之间,十六进制数字最多为两位数,二进制数字最多为8位数。
我们可以通过添加下面几行解决这些问题:
// byteConverter/ByteConverterDialog.cpp (continued)
...
exitButton->setDefault(true);
// Limit input to valid values
QIntValidator * decValidator =
new QIntValidator(0, 255, decEdit);
decEdit->setValidator(decValidator);
QRegExpValidator * hexValidator =
new QRegExpValidator(QRegExp("[0-9A-Fa-f] {1,2 }"), hexEdit);
hexEdit->setValidator(hexValidator);
QRegExpValidator * binValidator =
new QRegExpValidator(QRegExp("[01] {1,8}"), binEdit);
binEdit->setValidator(binValidator);
setWindowTitle(tr("Byte Converter"));
...

设置窗口标题
开头两个问题每个都用额外的单独一行代码解决了。为了解决第一个问题,我们使用setWindowTitle()函数,它用于设置窗口部件的标题,如果该部件占据了一个顶层窗口。这个函数是QWidget类的一个方法。由于ByteConverterDialog使用QWidget作为它的基类,它也继承了这个函数,我们可以简单的调用。
指定默认按钮
通过通知这个按钮(而不是你可能期待的对话框)说它事实上是默认按钮,为一个对话框指定了默认按钮。(然而,请注意,调用一个QPushButton对象的setDefault(true)方法仅当这个按钮用于一个基于对话框的主窗口且没有默认按钮时才有效。如果你试图为一个主窗口定义默认按钮,Qt将使得它看起来像一个默认按钮,但是当用户按回车键时无法激活它。)


检查用户输入
第三个问题,限制行编辑器只能输入正确的值,需要多一些的工作,但是可以通过验证器解决。这些类都继承自基类QValidator。一个验证器关联于一个父对象:接受输入并通知父对象是否应该接受输入的值。
为了检查十进制数字的有效性,我们使用QIntValidator对象。我们通过将允许输入的最小和最大值作为第一和第二个参数传递给它的构造方法来创建它。这里的decEdit是一个指向行编辑器对象的指针,该行编辑器也成为验证器的父对象。这个调用,除了将验证器绑定到输入部件之外,也使得它接受自动内存管理,因此当输入部件释放时验证器也将被自动释放。然后我们调用setValidator()使得验证器持续检查指针指向的对象decEdit的输入。现在用户只能在输入框中输入0到255之间的数字。
为了检查十六进制数字的有效性,我们必须使用另一种类型的验证器:QRegExpValidator(正则表达式验证器)。这个验证器将输入作为一个字符串,与一个正则表达式比较。在我们的例子中,正则表达式是[0-9A-Fa-f]{1,2}。第一个方括号之前的次级表达式指定了允许输入的字符:数字0-9,字母A-F(大写或小写)。后面的次级表达式{1,2}限制输入字符串的长度为至少1个,至多2个字符。
Qt中的正则表达式来自于Perl,但是它们有一些重要的区别。例如,必须在Perl样式的正则表达式中的反斜杠前面加上另外一个反斜杠,才是符合Qt样式的表达式,因为在C/C++中单独的反斜杠被用作转义字符。QRegExp将双反斜杠识别为一个简单的反斜杠。这意味着如果想要以Qt样式指定一个合法的反斜杠,我们必须键入四个反斜杠。
我们也使用QRegExpValidator和正则表达式[01]{1,8}作为二进制数字输入框的验证器。这个表达式仅允许输入字符0和0,但是字符串长度可以从1到8。

 

2.1.4 实现槽

 

最后,我们需要实现功能连接,使得退出按钮按我们期待的那样工作,以及三个输入框可以互相同步。
为确保点击退出按钮时关闭字节转换对话框,我们扩充ByteConverterDialog类的构造函数,用来关联按钮的clicked()信号和对话框的accept()槽,这个槽是由ByteConverterDialog的基类QDialog类提供的。
// byteConverter/ByteConverterDialog.cpp (continued)
...
connect(exitButton, SIGNAL(clicked()),
this, SLOT(accept()));
...

当accept()方法被调用时,简单的关闭了对话框。我们在这里使用accept()遵循了一个一般的约定:许多对话框都有一个确定按钮和一个取消按钮;确定按钮连接至accept()槽,取消按钮连接至reject()槽。两个槽都关闭对话框,第一个退出时返回一个正值,第二个返回一个负值(参见第六章,第161页)。在这个例子中,我们只有一个按钮,因此不需要关注返回值,只要动作就可以了。
然而,我们的转换程序真实的事件处理逻辑包含为通常的信号和槽增加一些自定义的连接,根据我们的ByteConverterDialog类的功能。只要三个行编辑器中的任何一个发出了指示对象的输入框的文本发生改变的 textChanged()信号,这些信号-槽连接应该立即起作用。为达到这个目的,我们用如下的代码扩充我们的类定义:
// byteConverter/ByteConverterDialog.h (continued)
class ByteConverterDialog : public QDialog
{
...
private slots:
void decChanged(const QString&);
void hexChanged(const QString&);
void binChanged(const QString&);
};

除了访问控制之外,声明槽就和声明普通函数的方法相同,对于槽,我们使用public slots:,protected slots:和private slots:标识符;而不是通常的 public:, protected:, and private: 保护模式。
这三个槽中的每一个都接受一个 const QString&类型的参数,通过这种方法 textChanged()信号可以将行编辑器的新文本传递给槽。
作为信号/槽的参数类型,我们没有选择简单的QString,而选择了一个const QString的引用。这样做有两个理由。首先,通过使用引用传递而不是值传递,在信号和槽被调用时,QString对象包含的将要传递给信号/槽的已更新的输入将不会被复制,因此代码效率更高。然而,引用传递的使用允许函数修改实参,但信号和槽不应该这样做,因此形参被声明为一个指向常量的引用。第二步是一个推荐的防御性编程(defensive programming)实践:即任何时候一个函数都不应该改变通过引用传递的实参的值。
尽管槽的声明与其它函数的声明有少许区别,它仍然是一个普通的函数,因此可以以通常的方式实现和调用。这里是ByteConverterDialog.cpp文件中decChanged()槽的定义:
// byteConverter/ByteConverterDialog.cpp (continued)
void ByteConverterDialog::decChanged(const QString& newValue)
{
 bool ok;
 int num = newValue.toInt(&ok);
 if (ok) {
  hexEdit->setText(QString::number(num, 16));
  binEdit->setText(QString::number(num, 2));
 } else {
  hexEdit->setText("");
  binEdit->setText("");
}
}
这个函数接受十进制行编辑器部件显示的新字符串,作为它的newValue形参的实际值,它还更新十六进制和二进制行编辑器部件显示的字符串。首先,我们需要确定输入的字符串相应的数值。作为一个QString类的对象,newValue有一些将字符串转换为数字的函数。我们将使用toInt()函数,因为输入的字符串表示一个整数。
toInt()函数接受一个布尔指针类型的可选参数:如果指定了这个参数,当字符串成功的转换成了数值时,toInt()函数将这个参数指向的值设置为true,如果转换不成功,即该字符串不表示一个整数,则设置为false。
如果转换成功,我们将另外两个行编辑器(hexEdit和binEdit)显示的文本设置为了与这个新值相等的十六进制和二进制形式。为了做到这一点,我们将数字转换成了以十六进制和二进制形式表示的字符串。
这就是QString类的静态函数number()的用处,该函数返回一个表示一个数字的字符串。数字本身是它的第一个参数。
number()函数要求用所使用的进制的基数作为它的第二个参数,在这个例子中,16表示十六进制,2表示二进制。第二个参数是可选的,如果没有指定,number()函数假定基数为10(十进制计数法),也是最常用的。
如果toInt()函数不能将十进制行编辑器部件中输入的字符串转换为一个数字,我们使用setText()在其它两个行编辑器部件上显示空白文本。得益于我们为decEdit对象添加的验证器,它保证了只有0到255之间的数字才可以输入,因此只有唯一的情况下转换才会失败:就是用户清空了输入框。
我们用同样的方式实现剩下的两个槽:
// byteConverter/ByteConverterDialog.cpp (continued)
void ByteConverterDialog::hexChanged(const QString& newValue)
{
...
if (ok) {
decEdit->setText(QString::number(num));
binEdit->setText(QString::number(num, 2));
} else {
...
}
}
void ByteConverterDialog::binChanged(const QString& newValue)
{
...
if (ok) {
decEdit->setText(QString::number(num));
hexEdit->setText(QString::number(num, 16));
} else {
...
}
}

对于这些函数,当它们将字符串转换为整数值时,我们在可选的第二个参数中给toInt()指定了进制的基数;和 QString::number()一样, 如果忽略第二个参数,toInt()使用默认的基数10.为了使程序的这些部分按照我们的设计协同工作,我们必须将 textChanged()信号连接到每个行编辑器对象相应的槽上。于是,我们最后一次扩充了构造函数:
// byteConverter/ByteConverterDialog.cpp (continued)
...
connect(decEdit, SIGNAL(textChanged(const QString&)),
this, SLOT(decChanged(const QString&)));
connect(hexEdit, SIGNAL(textChanged(const QString&)),
this, SLOT(hexChanged(const QString&)));
connect(binEdit, SIGNAL(textChanged(const QString&)),
this, SLOT(binChanged(const QString&)));
}

ByteConverterDialog类的构造函数的代码现在讲完了,它完成了三个不同的任务:
它生成了所有的对话框部件,并把它们放入恰当的布局中,设置了对话框对象的层次关系。
它限制用户只能输入合理的值。设置了所有必要的信号-槽连接。
程序的整个逻辑包含在槽的实现代码,以及它们与相应的信号的连接中。

 

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

Tag标签: 翻译,C++,QT4,GUI,The Book of Qt 4

原创粉丝点击