2.1派生对话框类(Subclassing QDialog)

    技术2022-05-11  48

    2.1派生对话框类(Subclassing QDialog)

    第一个例子是一个用C++实现的查找对话框。我们把这个对话框实现为一个类,这样它就是一个独立的控件,并有自己的信号(signal)和slot函数

    类的源代码分别放在finddialog.h和finddialog.cpp中。首先看finddialog.h的代码

    1 #ifndef FINDDIALOG_H2 #define FINDDIALOG_H3 #include <QDialog>4 class QCheckBox;5 class QLabel;6 class QLineEdit;7 class QPushButton; 8 class FindDialog : public QDialog 9 {10     Q_OBJECT11 public:12     FindDialog(QWidget *parent = 0);13 signals:14     void findNext(const QString &str, Qt::CaseSensitivity cs);15     void findPrevious(const QString &str, Qt::CaseSensitivity cs);16 private slots:17     void findClicked();18     void enableFindButton(const QString &text);19 private:20     QLabel *label;21     QLineEdit *lineEdit;22     QCheckBox *caseCheckBox;23     QCheckBox *backwardCheckBox;24     QPushButton *findButton;25     QPushButton *closeButton;26 };

    27 #endif

    一共27行,第1,2,27行是为了避免头文件被多次包含。第3行包含QDialog头文件,这个类从QDialog继承,QDialog从QWidget继承。第4至7行是用到的Qt中类的前向声明。通过前向声明,编译器就知道这个类已经存在,而不用写出包含的头文件。这个问题稍后还要讲。第8至26行是类FindDialog的定义。第10行,Q_OBJECT是一个宏定义,如果类里面用到了signal或者slots,就要声明这个宏。第12行, FindDialog(QWidget *parent = 0);构造函数是Qt控件类的标准格式,默认的父参数为NULL,说明没有父控件。第13行,signal声明了这个对话框发出的两个信号,如果选择向前查找,那么对话框就发出findPrevious()信号,否则,发出findNext()信号。signal也是一个宏,在编译之前,C++预处理把它变成标准的c++代码。Qt::CaseSensitivity是一个枚举类型,有Qt::CaseSensitive和Qt::CaseInsensitive两个值。

    在类的私有部分,声明有两个slot函数。为了实现这两个函数,需要用到对话框的其他控件的信息,所以保存了一些控件的指针。slot关键字和signal一样,也是一个宏。

    对于私有成员变量,我们只是使用了它们的指针,没有对它们进行存取操作,编译器不需要知道它们的详细定义,所以只使用了这些类的前向声明。当然,也可以使用<QCheckBox>,<QLabel>等,但是,使用前向声明会让编译速度更快一些。

    下面看一下finddialog.cpp源文件代码:

    文件头和构造函数部分

    1  #include <QtGui>2  #include "finddialog.h" 3 FindDialog::FindDialog(QWidget *parent) 4     : QDialog(parent) 5 { 6     label = new QLabel(tr("Find &what:")); 7     lineEdit = new QLineEdit; 8     label->setBuddy(lineEdit); 9     caseCheckBox = new QCheckBox(tr("Match &case"));10     backwardCheckBox = new QCheckBox(tr("Search &backward"));11     findButton = new QPushButton(tr("&Find"));12     findButton->setDefault(true);13     findButton->setEnabled(false);14     closeButton = new QPushButton(tr("Close"));15     connect(lineEdit, SIGNAL(textChanged(const QString &)),16             this, SLOT(enableFindButton(const QString &)));17     connect(findButton, SIGNAL(clicked()),18             this, SLOT(findClicked()));19     connect(closeButton, SIGNAL(clicked()),20             this, SLOT(close()));21     QHBoxLayout *topLeftLayout = new QHBoxLayout;22     topLeftLayout->addWidget(label);23     topLeftLayout->addWidget(lineEdit);24     QVBoxLayout *leftLayout = new QVBoxLayout;25     leftLayout->addLayout(topLeftLayout);26     leftLayout->addWidget(caseCheckBox);27     leftLayout->addWidget(backwardCheckBox);28     QVBoxLayout *rightLayout = new QVBoxLayout;29     rightLayout->addWidget(findButton);30     rightLayout->addWidget(closeButton);31     rightLayout->addStretch();32     QHBoxLayout *mainLayout = new QHBoxLayout;33     mainLayout->addLayout(leftLayout);34     mainLayout->addLayout(rightLayout);35     setLayout(mainLayout);36     setWindowTitle(tr("Find"));37     setFixedHeight(sizeHint().height());38 }

    到这里FindDialog的构造函数就完成了。在传见控件和布局时我们使用了new,一般情况下,我们还需要写析构函数delete这些控件。但是在Qt中这是不需要的,当父控件销毁时,Qt自动删除它所有的子控件和布局。

    下面是FindDialog类的两个slot函数:39 void FindDialog::findClicked()40 {41     QString text = lineEdit->text();42     Qt::CaseSensitivity cs =43             caseCheckBox->isChecked() ? Qt::CaseSensitive44                                       : Qt::CaseInsensitive;45     if (backwardCheckBox->isChecked()) {46         emit findPrevious(text, cs);47     } else {48         emit findNext(text, cs);49     }50 }51 void FindDialog::enableFindButton(const QString &text)52 {53     findButton->setEnabled(!text.isEmpty());54 }当用户点击findButton按钮,findClicked()就会调用,根据backwardCheckBox状态,他发出findPrevious()或者findNext()信号。emit也是一个Qt的宏。当用户改变lineEdit中的文本,enableFindButton()slot函数就会调用。如果输入了文本,那么让findButton有效,否则就无效。

    最后,创建main.cpp测试FindDialog对话框。1 #include <QApplication>2 #include "finddialog.h"3 int main(int argc, char *argv[])4 {5     QApplication app(argc, argv);6     FindDialog *dialog = new FindDialog;7     dialog->show();8     return app.exec();9 }运行qmake编译程序。由于在FindDialog中包含了Q_OBJECT宏,由qmake生成的makefile会保换特殊的规则运行moc(Qt的原对象编译器)。为了确保moc正确工作,类定义必须放在头文件而不能放在实现文件中。由moc生成的代码中包含这个头文件,并加入它自己实现的C++代码。使用了Q_OBJECT宏的类必须运行moc。如果使用qmake,那么makefile里自动包含相关的规则。如果忘记了运行moc,就会发生连接错误。不同的编译器给出的提示信息不同,有的会非常晦涩。GCC给出的错误信息如下:   finddialog.o: In function 'FindDialog::tr(char const*, char const*)':     /usr/lib/qt/src/corelib/global/qglobal.h:1430: undefined reference to     'FindDialog::staticMetaObject'     Visual C++中的输出是这样:

         finddialog.obj : error LNK2001: unresolved external symbol     "public:~virtual int __thiscall MyClass::qt_metacall(enum QMetaObject     ::Call,int,void * *)"这时需要重新运行qmake,更新makefile,然后编译程序。运行程序,如果看到了快键,测试ALT+W,ALT+C,ALT+B,ALT+F引发相应的处理程序。使用TAB键在将焦点改变到不同的控件上。默认的TAB键是控件创建的顺序。QWidget::setTabOrder()可以改变这个顺序。提供合适的tab顺序和快键可以让用户不用鼠标也可以运行程序,通过键盘可以快速控制程序。 


    最新回复(0)