zoukankan      html  css  js  c++  java
  • chapter2 创建对话框

    chapter2. 创建对话框

    先从最基础的窗体结构开始,我们首先接触到的,是对话框.对话框是一种很简单的窗体结构,为应用程序和用户之间提供了一种"交互"的可能.窗体的构建方式,主要分为两种:

    1.手写代码构建

    2.Qt Designer辅助构建

    这两个我们在下面的例子中都会接触到.

    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++的固定用法,防止头文件的多重包含.

    2.有很多前置声明的class,注意,这里只是声明,不涉及具体的调用,这样会保证头文件是轻量级的,使得编译速度更快.

    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将源代码编译为窗体程序的全部流程.这一讲只是构建了简单的对话框,后面有又更复杂,但也更精彩的窗体程序等待着探索.无论是对别人,还是对自己,歇息的时候不忘说一声:加油!

    却道,此心安处是吾乡
  • 相关阅读:
    document.getElementById的简便方式
    uri编解码
    javascript数组
    前端网站收藏
    html5 canvas
    interview material
    Merge into(oracle)
    机器学习入门二 ----- 机器学习术语表
    机器学习入门一 ------- 什么是机器学习,机器学习的在实际中的用处
    Dubbo 源码分析系列之一环境搭建
  • 原文地址:https://www.cnblogs.com/lucifer25/p/7723872.html
Copyright © 2011-2022 走看看