C++ GUI Programming with Qt4 笔记 -- chap2 QDialog
以 finddialog 为例,介绍 QDialog。并对 Qt 的基本概念和技巧做了更进一步(chap1)的介绍。
1.MOC 扩展与signals–slots 机制
严格来说,Qt 开发,写的并不是标准 C++。
Meta-Object System 是 Qt 对 C++ 的一项重要扩展,简称 moc。
moc is a mechanism for creating independent software components
that can be bound together without any component knowing anything about the other components it is connected to
提供了 2 个关键内容:signals–slots and introspection。
其中,introspection 是实现 signals–slots 的基础。
The mechanism works as follows:
-
The Q_OBJECT macro declares some introspection functions that must be implemented in every QObject subclass: metaObject(), tr(), qt_metacall(), and a few more.
-
Qt's moc tool generates implementations for the functions declared by Q_OBJECT and for all the signals.
-
QObject member functions such as connect() and disconnect() use the introspection functions to do their work.
All of this is handled automatically by qmake, moc, and QObject, so you rarely need to think about it.
But if you are curious, you can read the QMetaObject class documentation
and have a look at the C++ source files generated by moc to see how the implementation works.
2. 技巧与细节
2.1 class forward declaration
在头文件中,如果只用到了某个类的指针,不依赖于类的定义与实现。
可以使用 class ClassName 声明类,取代 #include "ClassName.h"
相比于加载头文件,前置声明更简洁,性能更好。
2.2 国际化:用户可见的文字,使用 tr()
2.3 快捷键:使用 & 声明快捷键。
例如 &Find 声明快捷键为 F,
setBuddy(widget) 设置快捷键按下时,focus 到 widget 组件上,而非自身。
2.4 setDefault 按下 Enter 键时的行为。
2.5 setLayout 时会重新计算父子关系。
layout 中的所有组件,都变成 setLayout 对象的子对象。
2.6 使用 moc 机制(Q_OBJECT)时,类的定义必须在 .h 文件中,否则 moc 不会正常工作。
// finddialog.h #ifndef FINDDIALOG_H #define FINDDIALOG_H #include <QDialog> class QCheckBox; class QLabel; class QLineEdit; class QPushButton; class FindDialog : public QDialog { Q_OBJECT public: FindDialog(QWidget *parent = 0); signals: void findNext(const QString &str, Qt::CaseSensitivity cs); void findPrevious(const QString &str, Qt::CaseSensitivity cs); private slots: void findClicked(); void enableFindButton(const QString &text); private: QLabel *label; QLineEdit *lineEdit; QCheckBox *caseCheckBox; QCheckBox *backwardCheckBox; QPushButton *findButton; QPushButton *closeButton; }; #endif
// finddialog.cpp #include <QtGui> #include "finddialog.h" FindDialog::FindDialog(QWidget *parent) : QDialog(parent) { // widgets label = new QLabel(tr("Find &what: ")); lineEdit = new QLineEdit; label->setBuddy(lineEdit); caseCheckBox = new QCheckBox(tr("Match &case")); backwardCheckBox = new QCheckBox(tr("Search &backward")); findButton = new QPushButton(tr("&Find")); findButton->setDefault(true); findButton->setEnabled(false); closeButton = new QPushButton(tr("Close")); // connections connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(enableFindButton(const QString &))); connect(findButton, SIGNAL(clicked()), this, SLOT(findClicked())); connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); // layout QHBoxLayout *topLeftLayout = new QHBoxLayout; topLeftLayout->addWidget(label); topLeftLayout->addWidget(lineEdit); QVBoxLayout *leftLayout = new QVBoxLayout; leftLayout->addLayout(topLeftLayout); leftLayout->addWidget(caseCheckBox); leftLayout->addWidget(backwardCheckBox); QVBoxLayout *rightLayout = new QVBoxLayout; rightLayout->addWidget(findButton); rightLayout->addWidget(closeButton); rightLayout->addStretch(); QHBoxLayout *mainLayout = new QHBoxLayout; mainLayout->addLayout(leftLayout); mainLayout->addLayout(rightLayout); setLayout(mainLayout); setWindowTitle(tr("Find")); setFixedHeight(sizeHint().height()); } void FindDialog::findClicked() { QString text = lineEdit->text(); Qt::CaseSensitivity cs = caseCheckBox->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive; if (backwardCheckBox->isChecked()) { emit findPrevious(text, cs); } else { emit findNext(text, cs); } } void FindDialog::enableFindButton(const QString &text) { findButton->setEnabled(!text.isEmpty()); }
// main.cpp #include <QApplication> #include "finddialog.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); FindDialog *dialog = new FindDialog; dialog->show(); return app.exec(); }