先从最基础的窗体结构开始,咱们首先接触到的,是对话框.对话框是一种很简单的窗体结构,为应用程序和用户之间提供了一种"交互"的可能.窗体的构建方式,主要分为两种:c++
1.手写代码构建正则表达式
2.Qt Designer辅助构建app
这两个咱们在下面的例子中都会接触到.ide
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 &); private: QLabel *label; QLineEdit *lineEdit; QCheckBox *caseCheckBox; QCheckBox *backwardCheckBox; QPushButton *findButton; QPushButton *closeButton; }; #endif
代码很长,分开来讲.布局
1.代码的前两行和最后一行,是c++的固定用法,防止头文件的多重包含.ui
2.有不少前置声明的class,注意,这里只是声明,不涉及具体的调用,这样会保证头文件是轻量级的,使得编译速度更快.this
3.Q_OBJECT宏,对于定义了信号-槽的类而言,是必要的翻译
4.注意c++里子类继承的方式,以及构造函数的书写.设计
finddialog.cpp
#include <QLabel> #include <QCheckBox> #include <QLineEdit> #include <QPushButton> #include <QHBoxLayout> #include <QVBoxLayout> #include "finddialog.h" // Constructor FindDialog::FindDialog(QWidget *parent) :QDialog(parent){ 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")); 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())); 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()); } // SLOT 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()); }
.cpp文件无疑就更大了,简单地说,主要分为两个部分:一个是构造函数的实现,设计程序窗体的搭建,二是槽函数的实现.
有这些须要注意的:
1.文件开始,对于一些头文件的包含(inlcude)是必要的.由于涉及到具体的类和声明,不然编译会没法经过.
2.注意到tr()函数常常出现,这是将它们翻译为其余语言的标志,即使你暂时没有翻译的打算,也最好将全部用户可见的字符串用tr()包裹,这是个很好的习惯.
3.注意到有些字符前面的'&'符号,这个与键盘的快捷键有关系,好比:&Find,在Alt+F的时候该按钮会被激发.
4.窗体的构建很费代码量,其实最核心的,是布局管理器的使用.基本顺序是:
新建窗口部件->将窗口部件放入布局管理器中->将布局管理器注册在主窗口中
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(); }
main.cpp就比较简单了,用第一讲中构建的方式,就能够直观的看到构建的窗体了.
下面抽一些时间,看看Qt中最重要的信号-槽的概念
基本语句是这样的:connect(sender, SIGNAL(signal), receiver, SLOT(slot)).有这么几个原则:
1.一个信号能够链接多个槽,具体的调用顺序不固定.
2.多个信号能够链接到同一个槽.
3.一个信号能够与另外一个信号链接.
4.链接能够被移除.经过调用disconnect()方法,不过不多被用到.
5.参数必定要匹配,若是信号的参数比槽的参数可能是能够的,可是多余的参数会被忽略.
Qt一样的提供了一种可视化的方式,来进行窗体的构建.这种方式无疑更加的简便,以一种"所见即所达"的方式,具体的代码,Qt负责生成,使用起来很方便.
借用Qt Designer以可视化的形式构建窗体,步骤大体以下:
1.建立并初始化子窗口部件
2.把子窗口部件放入布局中(让布局管理器本身寻找最合适的位置和大小)
3.设置Tab键顺序
4.创建信号-槽之间的链接
5.实现对话框中的自定义草(在代码中)
构建窗体,保存为gotocelldialog.ui文件,实际上是xml,可是以后Qt会以此为模板,生成一个新类,这里称之为Ui类,.h头文件会自动写好.
这个时候,最重要的一步:
建立一个新类,同时继承QDialog和Ui类,简单的增长一个间接层,同时实现了两个类的功能,这就是抽象的好处.
gotocelldialog.h
#ifndef GOTOCELLDIALOG_H #define GOTOCELLDIALOG_H #include <QDialog> #include "ui_gotocelldialog.h" class GoToCellDialog : public QDialog, public Ui::GoToCellDialog{ Q_OBJECT public: GoToCellDialog(QWidget *parent = 0); private slots: void on_lineEdit_textChanged(); }; #endif
原本是没有ui_gotocelldialog.h文件的,这里引用并不会出错,由于Qt会根据咱们写的.ui文件自动生成该文件.同时这里给出了槽函数的定义.
gotocelldialog.cpp
#include <QtWidgets> #include "gotocelldialog.h" GoToCellDialog::GoToCellDialog(QWidget *parent) : QDialog(parent) { setupUi(this); buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}"); lineEdit->setValidator(new QRegExpValidator(regExp, this)); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } void GoToCellDialog::on_lineEdit_textChanged(){ buttonBox->button(QDialogButtonBox::Ok)->setEnabled(lineEdit->hasAcceptableInput()); }
setupUi(this)是直接调用的Ui类里的方法,用来初始化窗体而后Ui类中的成员就能够直接访问了.这个地方为lineEdit添加了一个正则表达式的验证器,后面会细讲.
用new方法新建的类,会在它的父类删除的时候被一块删除,避免了内存的浪费.并且在形式上,子类是在父类的窗体里的.
main.cpp
#include <QApplication> #include <QDialog> #include "gotocelldialog.h" int main(int argc, char *argv[]){ QApplication app(argc, argv); GoToCellDialog *dialog = new GoToCellDialog; dialog->show(); return app.exec(); }
main函数就比简单了
除了这些简单的对话框以外,Qt还容许构建更为复杂的,如:扩展对话框,以及多页对话框.
下面这个是一个可扩展的对话框,部分窗体部件在不使用的时候是隐藏的,可是也能够经过调用而显示出来.
仍是先构建窗体,保存为.ui文件.新建子类,并继承.
sortdialog.h
#ifndef SORTDIALOG_H #define SORTDIALOG_H #include <QDialog> #include "ui_sortdialog.h" class SortDialog : public QDialog, public Ui::sortDialog{ Q_OBJECT public: SortDialog(QWidget *parent = 0); void setColumnRange(QChar first, QChar last); private slots: void on_moreButton_clicked(); }; #endif
sortdialog.cpp
#include <QtWidgets> #include "sortdialog.h" SortDialog::SortDialog(QWidget *parent) : QDialog(parent) { setupUi(this); secondGroupBox->setVisible(false); tertiaryGroupBox->setVisible(false); layout()->setSizeConstraint(QLayout::SetFixedSize); setColumnRange('A', 'Z'); } void SortDialog::setColumnRange(QChar first, QChar last){ primaryColumnCombo->clear(); secondColumnCombo->clear(); tertiaryColumnCombo->clear(); secondColumnCombo->addItem(tr("None")); tertiaryColumnCombo->addItem(tr("None")); primaryColumnCombo->setMinimumSize(secondColumnCombo->sizeHint()); QChar ch = first; while(ch <= last){ primaryColumnCombo->addItem(QString(ch)); secondColumnCombo->addItem(QString(ch)); tertiaryColumnCombo->addItem(QString(ch)); ch = ch.unicode() + 1; } } void SortDialog::on_moreButton_clicked() { secondGroupBox->setVisible(true); tertiaryGroupBox->setVisible(true); }
main.cpp
#include <QApplication> #include "sortdialog.h" int main(int argc, char*argv[]){ QApplication app(argc, argv); SortDialog *dialog = new SortDialog; dialog->setColumnRange('C', 'F'); dialog->show(); return app.exec(); }
这里窗体的show()和hide()是经过信号-槽机制,经过调用槽中的窗体部件的setVisible(bool)方法实现.
同时咱们能够看到,窗体部件的方法,能够在代码中很轻易的调用,从而很容易的改变它们的功能.
还有一种动态调用的方法,是用QUiLoader(.ui)这种形式直接调用.ui文件,不过不多使用,暂且不表.
这个部分比较轻松.Qt提供了一整套的内置窗口部件和经常使用对话框,能够知足绝大部分需求,这意味着:
咱们彻底没必要从新造轮子.可是,若是有时间,最好仍是把那些轮子拿来看看,是怎么实现的,毕竟不能永远的停留在入门的阶段!
窗口部件类咱们已经接触到不少,如:QLabel,QLineEdit,QPushButton等,对话框则有:QInputDialog,QFileDilog等,很是方便,一行代码就能够调用.这些,用的时候再说.
总结:之前一直都是在用Qt Creator这样的IDE写,的确不少步骤被简化,使入门变得容易.可是这本书估计写的时候尚未Qt,总之写的时候比较费劲,可是有助于深刻了解Qt将源代码编译为窗体程序的所有流程.这一讲只是构建了简单的对话框,后面有又更复杂,但也更精彩的窗体程序等待着探索.不管是对别人,仍是对本身,歇息的时候不忘说一声:加油!