冷王的弃妃txt下载:使用Qt设计器进行界面设计

来源:百度文库 编辑:偶看新闻 时间:2024/04/29 13:43:55
Book of QT4 翻译
QT程序设计艺术
---------------------------------------------------------------------------------
原名:The Book of QT 4:The Art of Building Qt Applications
译名:The Book of QT 4中文版:QT程序设计艺术
---------------------------------------------------------------------------------
第三章 使用Qt设计器进行界面设计
 
3.2 将Qt设计器生成的文件集成进你的Qt工程
 
当你使用菜单项文件--保存窗体或者保存窗体为时,Qt设计器根据窗体中每个部件的信息生成一个.ui文件。这个.ui文件可以在qmake工程文件中指定,如下所示:FORMS = byteconverterdialog.ui
 
在我们的例子中,qmake只考虑用户界面文件byteconverterdialog.ui;可以用空格分隔指定几个文件,也可以在另外一行通过这种格式添加:FORMS += file.ui。
当构建工程时,make依赖于用户界面编辑器uic将Qt设计器生成的.ui文件转换成C/C++头文件。在这一步中。有一个固定的命名约定:例如,如果Qt设计器生成的.ui文件表示的类名字为ByteConverterDialog(可以检查objectName属性的值确定类的名字),那么uic生成的结果文件的名字是ui_byteconverterdialog.h。
在这里非常重要的一点是,工程中的其它文件至少要有一个包含了这个生成的头文件。在qmake运行前,你必须添加合适的#include声明。否则,在下次运行make时,它将不会使用相关的用户界面描述文件作为参数调用uic。
 
注意,生成的头文件包含的一个辅助类只有两个方法:setupUi()方法用于生成GUI界面,如果程序允许用户运行时改变语言,retranslateUi()方法可以被调用。
两个方法都需要一个(参数)指向窗口部件的指针,通过这个指针,可以找到Qt设计器描述的GUI对象。即使你已经在Qt设计器中选择了一个模板,在这里,你也可以自由的选择这个指针指向的窗口部件类。MainWindow模板是唯一的一个必须与QMainWindow一起使用的模板。
 
uic生成的类的可用形式为Ui::ByteConverterDialog或Ui_ ByteConverterDialog,一般是像Ui::classname或Ui_ class这样的名字,类的名字对应于Qt设计器中创建的窗体的objectName属性。
目前有三个方法使用和实际开发创建好的窗口部件。最好使用哪一种方法依赖于特定的环境。
 
3.2.1 使用设计器生成的类作为辅助类
 
如果你只要显示一次Qt设计器创建的用户界面,在它初始化完成后不需要再次接触对应的对象,那么合适的方法是,直接实例化生成的类,并使用setupUi()将这个实例绑定到先前创建的窗口部件上。这个方法将.ui文件中描述的GUI元素安排在窗口部件上。并通过在Qt设计器中指定的布局锚定它们。
我们将以Qt设计器生成的ByteConverterDialog类为例演示这个方法:
// simple/main.cpp #include  #include "ui_byteconverterdialog.h" int main(int argc, char * argv[]) { QApplication app(argc, argv); QDialog dlg; Ui::ByteConverterDialog ui; ui.setupUi(&dlg); dlg.setAttribute(Qt::WA_QuitOnClose); dlg.show(); return app.exec(); }
 
由于Qt设计器生成的对话框部件作为UI类的公共访问成员可用,通过调用各自的部件的方法,我们可以在后面的代码中调整它们。它们的信号和槽也可以参与信号-槽连接。无论Ui::ByteConverterDialog类是像现在这样在main()函数中初始化,还是在继承自QDialog的类的构造函数中初始化,都没有什么区别。
 
然而,在我们的例子中,上面展示的方法有一个问题:我们不能连接退出按钮的clicked()信号和对话框的accept()槽,而且虽然我们可以将binChanged(), hexChanged()和binChanged()连接至各个QTextEdit部件的textChanged()信号,却无法在槽中访问指向任何uic生成的部件的指针。
 
由于这个原因,直接调用setupUi()方法的用途是非常有限的:如果我们像这样做,就将自己限制在使用uic生成的类的实例和像QWidget或QDialog这样的标准类的实例之中。然而,在某些情况下,这个过程是完全足够的,例如,在一个只需要调用exec()的简单模态输入对话框中。exec调用开启了一个单独的事件循环,只有当accept(), reject()或者其它方法关闭了对话框时才返回。因为当对话框关闭后,这个对话框对象还继续存在,通过使用setupUi(),随后的代码就可以毫无风险的获取放置在对话框里面的部件的返回值,因此在那些情况下,你可以不使用QDialog的子类。
 
很重要的一点是,在试图访问用户界面对象(在当前的例子中,即ui)的成员之前,你总是应该首先调用Qt设计器生成的类的实例的setupUi()方法。否则,程序将会被未初始化的指针弄崩溃。
对于未实例化Qt设计器生成的类产生的争论由这一事实引起:公共成员变量是可以从外部访问的,这将导致对面向对象编程最重要的原则之一,私密原则的违反。
一个类的内部细节因此“隔离”于其它的类,,你可以改变一个类内部的设计而不必调整使用这个类的程序的其余部分。只要你仅仅把UI类当作一个短期的启动类使用,对封装原则的破坏并不是十分重要。
3.2.2 使得Qt设计器生成的部件始终可用
 
为了应付刚刚描述的缺点,一个好主意是包含uic生成的类并把它当作一个成员变量。要达到这个目的,我们首先继承必要的类,在我们的例子中是QDialog。
main()函数与第二章的那个相似,因为从它的角度看,ByteConverterDialog还是一个“黑箱”。
关键的区别在于类的声明。我们将uic生成的类声明为QDialog子类的一个私有成员。通过这个新创建的ui,继承自QWidget的ByteConverterDialog类的成员变量,这允许访问Qt设计器生成的类的部件:
// member/byteconverterdialog.h ... #include  #include "ui_byteconverterdialog.h" class QLineEdit; class ByteConverterDialog : public QDialog { ... private: Ui::ByteConverterDialog ui; };
通过ui成员变量,构造函数和所有的槽现在都可以访问生成的类:
// member/byteconverterdialog.cpp ... ByteConverterDialog::ByteConverterDialog(QWidget * parent) : QDialog(parent) { ui.setupUi(this); connect(ui.decEdit, SIGNAL(textChanged(const QString&)), this, SLOT(decChanged(const QString&))); connect(ui.hexEdit, SIGNAL(textChanged(const QString&)), this, SLOT(hexChanged(const QString&))); connect(ui.binEdit, SIGNAL(textChanged(const QString&)), this, SLOT(binChanged(const QString&))); }void ByteConverterDialog::decChanged(const QString& newValue) { bool ok; int num = newValue.toInt(&ok); if (ok) { ui.hexEdit->setText(QString::number(num, 16)); ui.binEdit->setText(QString::number(num, 2)); } else { ui.hexEdit->setText(""); ui.binEdit->setText(""); } }
 
覆盖原则在这里同样适用:非常重要的一点是,在我们能够以任何方式使用UI类之前,应该首先调用setupUi()方法进行初始化。这个方法的缺点是它的间接性,即必须通过成员变量调用。但是这个方法的优点是它减少了封装问题的危险性,限制了由对话框类的作用于引起的问题。。在任何情况下,任何来自对话框类外部的访问都是不可能的。另外一个好处是:它不包含Qt设计器生成的部件的代码。此外,这个方法特别适合于那些必须保持二进制兼容性的库中的部件,因为只有生成的类的实例的指针改变编译输出的二进制格式。
 
3.2.3 多重继承
 
奇趣推荐多重继承作为理想的解决方法。不过就同前一个方法一样,只有当你打算新建一个你自己的子类时才管用。
使用这种方法,新建的部件不仅继承自QWidget,也继承自uic生成的UI类。要特别强调的一点就是在继承指令中使用的private关键字。这样做保证所有从UI类继承的方法在新类中都是私有类变量,尽管它们事实上在UI类自身中式可公开访问的:
 
这个方法因此一举解决了很多问题:我们可以像标准的成员变量一一样使用uic生成的部件指针,而不需要绕个大弯通过一个辅助对象,并且它们保持私有状态,因此维持了对外部的封装性。
对于我们的例子来说,这意味着构造函数要像下面这样更改:
 
// inherit/byteconverterdialog.cpp ... ByteConverterDialog::ByteConverterDialog(QWidget * parent) : QDialog(parent) { setupUi(this); 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&))); }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(""); } }
 
跟前面一样,我们只需要在最开始的位置调用setupUi()方法,我们再次使用指向我们当前类作用域的部件的指针作为参数传递给它。
提醒:在这种方法中,继承顺序是很重要的。这个类首先必须继承QDialog类,然后才是设计器生成的类。如果没有这样做,编译器将会抛出一个难于理解的错误,可能很快使程序员绝望:
moc_byteconverterdialog.cpp:43: error: ‘staticMetaObject’ is not a member of type ‘Ui::ByteConverterDialog’ moc_byteconverterdialog.cpp: In member function ‘virtual void* ByteConverterDialog::qt_metacast(const char * )’: moc_byteconverterdialog.cpp:60: error: ’class Ui::ByteConverterDialog’ has no member named ’qt_metacast’ moc_byteconverterdialog.cpp: In member function ‘virtual int ByteConverterDialog::qt_metacall(QMetaObject::Call, int, void**)’: moc_byteconverterdialog.cpp:66: error: ’class Ui::ByteConverterDialog’ has no member named ’qt_metacall’ make: *** [moc_byteconverterdialog.o] Error 1
 
原因在于元对象编译器的工作机制,它只检查继承列表上的第一个父类是否继承自QObject。这也意味着通常不可能继承自多个使用QObject作为基类的类。

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