chapter2 建立對話框

chapter2. 建立對話框

先從最基礎的窗體結構開始,咱們首先接觸到的,是對話框.對話框是一種很簡單的窗體結構,爲應用程序和用戶之間提供了一種"交互"的可能.窗體的構建方式,主要分爲兩種:c++

1.手寫代碼構建正則表達式

2.Qt Designer輔助構建app

這兩個咱們在下面的例子中都會接觸到.ide

1.子類化QDialog

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.參數必定要匹配,若是信號的參數比槽的參數可能是能夠的,可是多餘的參數會被忽略.

2.快速設計對話框

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函數就比簡單了

3.改變形狀的對話框

除了這些簡單的對話框以外,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文件,不過不多使用,暫且不表.

4.內置的窗口部件類和對話框類

這個部分比較輕鬆.Qt提供了一整套的內置窗口部件和經常使用對話框,能夠知足絕大部分需求,這意味着:

咱們徹底沒必要從新造輪子.可是,若是有時間,最好仍是把那些輪子拿來看看,是怎麼實現的,畢竟不能永遠的停留在入門的階段!

窗口部件類咱們已經接觸到不少,如:QLabel,QLineEdit,QPushButton等,對話框則有:QInputDialog,QFileDilog等,很是方便,一行代碼就能夠調用.這些,用的時候再說.

總結:之前一直都是在用Qt Creator這樣的IDE寫,的確不少步驟被簡化,使入門變得容易.可是這本書估計寫的時候尚未Qt,總之寫的時候比較費勁,可是有助於深刻了解Qt將源代碼編譯爲窗體程序的所有流程.這一講只是構建了簡單的對話框,後面有又更復雜,但也更精彩的窗體程序等待着探索.不管是對別人,仍是對本身,歇息的時候不忘說一聲:加油!

相關文章
相關標籤/搜索