stO 各位前来参观的巨佬 Orz

Qt快速上手指南

2020-03-13 20:04:09



写在前面的话


你是否想过要让你的程序图形化

你是否想过要让你的程序像其他程序一样拥有界面, 拥有多个对话框,可以让别人方便地使用?

除了再控制台上做手脚, 你还可以试试 Qt

说起它,你是否认为它很复杂? 其实你错了,想要上手, 其实很简单

这篇文章绝对不会像这个一样:

8lb83j.jpg

一定会为你把那些细节讲得简单又清楚, 现在就开始吧!

文章较长,可以分板块选择性阅读。


Part 1 :前置芝士


下载与安装。

提示:版本不要过低, 本文使用代码按照 Qt 5 标准编写, Qt 4 很可能无法成功编译(其实主要是 connect() 的定义有锅)!!!

注意这不是 Qt Creator 版本号!而是 Qt 库的版本号


Part 2 Qt 简介


Qt 是一个 $1991$ 年由 Qt Company 开发的跨平台C++图形用户界面应用程序开发框架。

它既可以开发 GUI 程序,也可用于开发非 GUI 程序,比如控制台工具服务器。

Qt 是面向对象的框架,使用特殊的代码生成扩展以及一些宏,Qt 很容易扩展,并且允许真正地组件编程

——百度百科

重点:使用C++,跨平台图形用户界面应用程序


Part 3 Qt 工作机制的介绍


Qt 主要使用一种信号与槽的机制, 由一个组件发出一个信号, 从而使一个或多个对应的槽触发事件。

这个信号有很多, 例如鼠标滑过,鼠标按下等。

如对于一个按钮(PushButton)的按下, 可以使用 ->clicked() 来表示信号, 即:

QPushButton *btn=new QPushButton();
btn->clicked();

是一个函数 (可以自定义,也可以使用自带的), 当收到信号, 就会自动调用对应的槽函数

要让一个信号和一个槽产生关联, 就需要用到 connect 函数。

下面的这个例子就可以很好地演示 (你需要先在 Qt Creator 中创建一个 Qt Widgets Application 项目,中间一直点下一步就行了):

以下文件在创建项目时就已经创建,大部分代码也已经为你写好,但是窝删掉了 UI 类。

\Header\mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPushButton>
//头文件,Qt的每一个组件都对应一个头文件
//(当然没有像 bits/stdc++.h 一样的万能文件,就算有,也会严重拖慢编译速度)
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);//窗口主函数
    ~MainWindow();//没啥用

private:
};

#endif // MAINWINDOW_H

\Source\main.cpp:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;//创建窗口类
    w.show();//显示窗口

    return a.exec();
}

\Source\mainwindow.cpp:

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    QPushButton *btn=new QPushButton(this);
    //创建PushButton,初始化函数的参数表示它的父节点,即它将显示在那个窗口上
    //若没有,则会另开一个窗口
    btn->setText("Quit");//设置显示字体
    connect(btn,&QPushButton::clicked,this,&MainWindow::close);
    //关键部分:连接按钮和槽函数
    //第一个参数为信号发出者,第二个参数为信号(在输入&时有提示)
    //第三个参数为接收者,第四个参数为槽函数
    //其中close是库中定义的函数,作用是关闭窗口
}

MainWindow::~MainWindow()//这个函数留空即可
{
}

按下 Ctrl+R ,你将看见这样的一个小窗口:

8Kflaq.png

点一下那个按钮, 窗口是不是消失了呢?

如果是,那恭喜你, 你的第一个 Qt 程序成功啦!庆祝一下吧

其实 connect 函数不是只有这种用法, 但是这种用法最常用,其它的也可以用这个来代替, 就只介绍这一种吧(雾。

在新建项目时,你会发现代码里自动创建了一个 UI 类, 其实它是可以图形化设计的, 但是它的灵活性远不及代码设计, 因为它很简单,所以读者可以自行尝试打开 Forms 目录下的 ui 文件, 进入设计界面设计窗口。


Part 4 菜单栏与工具栏的动作


Qt 使用 QAction 类来表示一个活动, 一般用它来处理菜单栏和工具栏各种事件

考虑到读者可能不了解菜单栏和工具栏的位置, 就在这里放个图示吧(徒手绘制,别嫌丑qwq):

8MSKOA.png

由于工具栏和菜单栏的操作极其相似(甚至可以说是相同), 所以只用一个类就可以实现对它们的控制。

  • QAction 类的食用方法

    开袋即食

    声明

    QAction *Aaction=new QAction(QIcon(""),tr("abc"),this);
    //QIcon()中的参数代表图标的位置
    //在菜单栏中会显示在最前面
    //在工具栏中是主要组成部分。
    //tr()中的参数为对这个事件的描述,即显示在菜单栏上的名称
    //而在工具栏中,如果没有可用图片,就会显示这个文字代替。
    //其实也可以直接使用引号包裹的字符串
    //但是使用tr()之后可以利用 Qt 提供的文件翻译成多国语言。
    //this 即父节点。

    设置们

    Aaction->setShortcut(tr("Ctrl+H"));
    //设置这个事件的快捷键
    //快捷键直接把键盘上的按键名称用加号连接即可。
    Aaction->setStatusTip(tr("Tip"));
    //设置这个对于这个事件的描述,将会显示在窗口下方。
    //这个提示不需要对槽进行连接
    //但是要在最后调用statusBar()。

    连接槽

    
    connect(Aaction, &QAction::triggered,this,&MainWindow::Baction);
    //前面讲机制时已经讲过各个参数的意义
    //QAction::triggered 即为菜单栏或工具栏被的点击事件。

到此已经完成对它的设置,完整代码如下:

\Header\mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    void Baction();

    QAction *Aaction;
};

#endif // MAINWINDOW_H

\Source\main.cpp:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

\Source\mainwindow.cpp:

#include <QAction>
#include <QMenuBar>
#include <QMessageBox>
#include <QStatusBar>//显示提示用
#include <QToolBar>

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    Aaction=new QAction(QIcon(""),tr("abc"),this);
    Aaction->setShortcut(tr("Ctrl+H"));
    Aaction->setStatusTip(tr("Tips"));
    connect(Aaction, &QAction::triggered,this,&MainWindow::Baction);
}

MainWindow::~MainWindow()
{
}

void MainWindow::Baction()
{
    //do something...
}

虽然现在完成了设置, 但是你会发现,窗口中并没有出现菜单栏或工具栏。 因为还没有把它添加到窗口中。

  • 菜单栏添加方法:

    类名 : QMenu

    对于 MainWindow 类的主窗口而言, 可以使用函数 menuBar() 来获得这个窗口的菜单栏的指针;

    添加一个子目addMenu(QString) , 这个子目在点击时只会展开下级目录, 不会执行任何活动

    重点:添加一个活动: addAction(QAction) 。 活动是不能再向下展开的,而子目下可以添加更多的子目。

  • 工具栏的添加方法:

    类名 : QToolBar,注意这里不是 QTool

    对于一个窗口而言, 可以使用函数 addToolBar(QString) 来为这个窗口添加工具栏,中间的文字并不会显示(雾;

    重点:添加一个工具(即活动): addAction(QAction)

另:在主函数末尾加上 statusBar();显示StatusTips

至此,已经将它们插入窗口。 接下来还需要导入图片,作为图标。

QIcon(QString) 的参数是图片的地址, 但是直接输入常规地址是找不到的, 这是就需要在 Qt 项目中加入存放图片的目录。 为了让 Qt 找到图片的位置, 还需要借助一个 QRC 文件。

首先打开项目中存放源代码的文件夹, 找不到可以右键 Qt Creator 中的 Sources 目录, 选“在 Explorer 中显示”(如图):

8QkWSx.png

新建一个文件夹,命名为 images , 当然其它名字也行:

8QArgP.png

把一个图片文件存入这个文件夹,格式为 .png (这里用你谷的 Logo 为例):

8QAWNj.png

回到刚才的目录,创建一个 .qrc 的文件, 用记事本打开:

8QESv6.png

在里面输入以下内容,并保存

<RCC>
    <qresource prefix="/">
        <file>images/logo.png</file>
    </qresource>
</RCC>

其实有意义的不多, 就第 $3$ 行:声明一个文件,在/images目录下, 文件名为Logo.png 。 还要加入更多图片的话, 可以继续在 <qresource> 标签内添加 <file> 标签

回到 Qt Creator,右键项目文件夹,选添加现有文件 (这时你的 Qt Creator 中是没有 Resources 目录的):

8QVuWR.png

选择刚才创建的那个 QRC 文件,打开:

8QVs0S.png

依次展开 Resources 目录, 找到 logo.png ,双击打开, 如果你看到的界面是这个样子的:

8QZEct.png

那么恭喜,你的图片插入成功了。

接下来就可以为工具栏添加图标啦! 完整代码如下: \Header\mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    void Baction();

    QAction *Aaction;
};

#endif // MAINWINDOW_H

\Sources\main.cpp:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

\Sources\mainwindow.cpp:

#include <QAction>
#include <QMenuBar>
#include <QMessageBox>
#include <QStatusBar>
#include <QToolBar>

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    Aaction=new QAction(QIcon(":/images/logo.png"),tr("abc"),this);
    //在这里给这个事件设置了图标,用:代表项目目录
    Aaction->setShortcut(tr("Ctrl+H"));
    Aaction->setStatusTip(tr("Tips"));
    connect(Aaction, &QAction::triggered,this,&MainWindow::Baction);
    QMenu *Menu=menuBar()->addMenu(tr("Something"));
    Menu->addAction(Aaction);
    QToolBar *Tool=addToolBar(tr("Something"));
    Tool->addAction(Aaction);
    statusBar();
}

MainWindow::~MainWindow()
{
}

void MainWindow::Baction()
{
    //do something
}

窗口图示

8QZRED.png

菜单栏:

8QZHDf.png

工具栏:

8QeuKx.png


Part 5 QMessageBox


一个完整的窗口应用程序, 一定不只有一个窗口, 除了主窗口之外, 还会有一些其它的对话框

现在就先介绍最简单的一种: QMessageBox

头文件: #include<QMessageBox>

QMessageBox 可分成很多种类型, 每种类型都对应一种函数,下面就分开讲解:

  • About窗口:

    定义:void about(QWidget,const QString&,const QString)

    第一个参数表示父节点,即弹出它的窗口;

    第二个参数表示对话框标题

    第三个参数表示对话框显示的内容

    这个对话框只有一个 OK 按钮

    这个对话框没有图片

    这个对话框弹出时没有音效

    执行 QMessageBox::about(this,"About","About this:...");的效果:

    8QlX3F.png

  • Information窗口:

    定义:StandardButton information(QWidget, const QString&,const QString,StandardButtons, StandardButton)

    第一个参数表示父节点,即弹出它的窗口;

    第二个参数表示对话框标题

    第三个参数表示对话框显示的内容

    第四个参数表示对话框的一些按钮,默认为OK可以不填

    第五个参数表示对话框的其它按钮,默认为没有可以不填

    这个对话框有一个蓝色 i 的信息图标

    这个窗口有弹出音效,是系统设置的弹出信息的音效。

    执行 QMessageBox::information(this,"Information","Information:...");的效果:

    8QUSkd.png

  • Critical窗口:

    定义: StandardButton critical(QWidget*,const QString&,constQString&, StandardButtons, StandardButton)

    第一个参数表示父节点,即弹出它的窗口;

    第二个参数表示窗口标题

    第三个参数表示窗口内容

    第四个参数表示窗口的一个按钮,默认为OK可以不填

    第五个参数表示窗口的另一个按钮,默认为没有,可以不填。

    这个窗口有一个红叉的错误图标

    这个窗口一般用来显示严重错误

    这个窗口有弹出音效,是系统设置的出错的音效。

    执行 QMessageBox::critical(this,"Error","Error:...");的效果:

    8Q36W8.png

  • Warning窗口:

    定义: StandardButton warning(QWidget*,const QString&,const QString&, StandardButtons, StandardButton)

    第一个参数表示父节点,即弹出它的窗口;

    第二个参数表示窗口标题

    第三个参数表示窗口内容

    第四个参数表示窗口的一些按钮,默认为OK可以不填

    第五个参数表示窗口的另一些按钮,默认为没有可以不填

    这个窗口有一个黄色感叹号的警告图标

    这个窗口一般用来显示警告不严重错误

    这个窗口有弹出音效,是系统设置的警告的音效。

    运行 QMessageBox::warning(this,"Warning","Warning:...");的效果:

    8QJRyD.png

  • Question窗口:

    定义: StandardButton question(QWidget*,const QString&,const QString&,StandardButtons, StandardButton)

    第一个参数表示父节点,即弹出它的窗口;

    第二个参数表示窗口标题

    第三个参数表示窗口内容

    第四个参数表示窗口的一些按钮,默认为Yes和No可以不填

    第五个参数表示窗口的另一些按钮,默认为没有,可以不填

    这个窗口有一个蓝色问号的问题图标

    这个窗口一般用来显示问题,并获取回答

    这个窗口没有有弹出音效

    运行QMessageBox::question(this,"Question","Do you want to...");的效果:

    8QNsFs.png

前 $4$ 种窗口多数情况下都不需要获取用户的操作, 但最后一种需要获取用户的选择, 所以接下来介绍如何拉取用户点击的按钮

注意到后面几种对话框都是有返回值的, 而这个返回值就是用户点击了的按钮对应的一个值, 所以只需要判断返回值与那个按钮对应的值相等就可以了。

以前面菜单栏和工具栏最后的那个程序为例, 把 void Baction() 的内容更改为:

void MainWindow::Baction()
{
    if(QMessageBox::question(this,tr("Question"),tr("Do you want to save?"))
            ==QMessageBox::Yes)//按下了Yes
    {
        QMessageBox::information(this,tr("Information"),tr("Yes"));//显示Yes
    }
    else
    {
        QMessageBox::information(this,tr("Information"),tr("No"));//显示No
    }
}

由于用户的这次选择操作只需要使用一次, 所以不需要用变量来存储。

运行后点击你谷的 logo 或按下 Ctrl+H, 就会弹出问题窗口。 效果图:

8Q0Vvq.png

按下Yes:

8Q0YKx.png

按下No:

8Q0csP.png

但是有些时候不仅需要两个选项, 这是就需要对可以不填第四个参数进行修改了。

由于 Qt 的每个按钮类型都对应二进制下的某一位上的 $1$ 。 而要让一个窗口有多个按钮,就只需要把它们用按位或 (即 | 运算符,有一出一,无一出零)连接就可以表示多个按键啦!

接下来就尝试高仿 Dev-C++ 在退出时有文件未保存的提示窗口吧! 预期效果(大家都见过吧?):

8QDM9J.png

这三个按键都是QMessageBox支持的, 所以表示显示这三个按钮的数值就是 (QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel)

注意:窗口被关闭时,返回值为QMessageBox::Cancel

现在就可以对 Baction() 函数再次进行修改:

void MainWindow::Baction()
{
    int ret=QMessageBox::question(this,tr("Confirm"),tr("保存为......"),
                         (QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel));
    //这里因为需要进行两次判断,所以用了一个int来存储返回值。
    if(ret==QMessageBox::Yes)
    {
        QMessageBox::information(this,tr("Information"),tr("Yes"));
    }
    else if(ret==QMessageBox::No)
    {
        QMessageBox::information(this,tr("Information"),tr("No"));
    }
    else
    {
        QMessageBox::information(this,tr("Information"),tr("Cancel"));
    }
}

这样就可以高仿 Dev-C++ 的退出窗口啦! 效果图:

8Qy4vd.png

点击各个按钮关闭窗口都会有反馈。


Part 6 QLabel


前面了解了一些窗口控件, 但是你总不可能用它们来显示信息鸭!

所以现在就介绍 Qt 中显示文字图像QLabel

头文件 #include<QLabel>

简单使用:显示一段文字

QLabel *label=new QLabel(this);//定义QLabel类的指针并创建新的QLabel
//将父节点设置为this,即显示在当前窗口上。
label->setText(tr("abc"));//设置显示的文字

还是以前面菜单栏和工具栏最后的那个程序为例, 修改 MainWindow::MainWindow(QWidget *parent) 为:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    Aaction=new QAction(QIcon(":/images/logo.png"),tr("abc"),this);
    Aaction->setShortcut(tr("Ctrl+H"));
    Aaction->setStatusTip(tr("Tips"));
    connect(Aaction, &QAction::triggered,this,&MainWindow::Baction);
    QMenu *Menu=menuBar()->addMenu(tr("Something"));
    Menu->addAction(Aaction);
    QToolBar *Tool=addToolBar(tr("Something"));
    Tool->addAction(Aaction);
    QLabel *label=new QLabel(this);
    label->setText(tr("abc"));
    statusBar();
}

在中间加入了上面的两行代码, 效果图:

8QgTbR.png

可以看到, 文字显示的位置并不理想, 和菜单栏冲突了。

为了移动它,就需要引入 move(int,int)函数, 它在很多控件中都有定义, 作用时移动当前控件到 (x,y) 位置, 这个坐标系得原点是它的父节点的左上角。 坐标加一,表示右移或下移 $1$ 像素, 坐标减一,表示左移或上移 $1$ 像素。 注意这里的两个参数都是非负数

所以在代码后加上label->move(0,50); 就可以把它向下移动到正确的位置啦!

这个位置可能在不同的电脑上有不同的效果

再把MainWindow::MainWindow(QWidget *parent) 修改为:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    Aaction=new QAction(QIcon(":/images/logo.png"),tr("abc"),this);
    Aaction->setShortcut(tr("Ctrl+H"));
    Aaction->setStatusTip(tr("Tips"));
    connect(Aaction, &QAction::triggered,this,&MainWindow::Baction);
    QMenu *Menu=menuBar()->addMenu(tr("Something"));
    Menu->addAction(Aaction);
    QToolBar *Tool=addToolBar(tr("Something"));
    Tool->addAction(Aaction);
    QLabel *label=new QLabel(this);
    label->setText(tr("abc"));
    label->move(0,50);//移动label
    statusBar();
}

效果图:

8Q2HyQ.png

位置不就好多了!

最后的说明: QLabel 中的文字是不可以选中的!


Part 7 QFont


QLabel设置完毕,可以在窗口中显示文字, 但是你真的愿意食用 Qt 默认的字体(宋体)吗? 你真的愿意只让字体有这种大小吗? 你真的愿意字体中规中矩,没有斜体粗体吗?

相信你是不愿意的。

为了设置字体,这里需要引进一个新的类: QFont

头文件 #include<QFont>

支持的操作:

  1. setFamily(const QString&)

    作用:设置字体;其中参数为字体的名称,如“宋体”。

  2. setPixelSize(int)

    作用:设置字体大小(按照像素)。

  3. setPointSize(int)

    作用:设置字体大小(你在Word中的设置的大小)。

  4. setUnderline(bool)

    作用:true为加下划线,false为不加。

  5. setStrikeOut(bool)

    作用:true为加删除线,false为不加。

  6. setOverline(bool)

    作用:true为在文本上方添加线(真不知该叫什么),false为不加。

  7. setItalic(bool)

    作用:true为斜体,false为正常。

  8. setBold(bool)

    作用:true为粗体,false为正常。

其实还有一些操作可用,只是不常用, 所以这里就只介绍这 $8$ 种。

对于很多控件,它们都支持 setFont(QFont&) 操作, 这是 QFont 就能派上用场,美化界面了。


Part 8 QTimer


相信你也发现了, Qt 的窗口控件都是一次成形的, 那么如果窗口中有需要动态更新的控件怎么办呢?

因此还需要引进一个类: QTimer

头文件#include<QTimer>

声明方法QTimer *timer=new QTimer();

作用:顾名思义, 这就是一个计时器。 但是这不是一个简单的计时器, 它在计时过程中, 且程序正常运行时 废话, 在时间到的时候, 它会发出一个 timeout 的信号, 而通常利用的就是它的这个信号

设置时间并开始timer->start(int);。 其中参数表示时长, 单位毫秒(ms)。

连接槽函数: connect(timer,&QTimer::timeout,this,&MainWindow::Baction);

这个时长可以为 0不能为负数

一下为持续执行活动的代码

    QTimer *timer=new QTimer();
    timer->start(0);
    connect(timer,&QTimer::timeout,this,&MainWindow::Baction);

其中 BactionMainWindow 类中自定义的一个函数, 这个函数将会在 MainWindow 被在 main.cpp 中声明后开始持续循环调用


Part 9 一些复杂一点的其它窗口


对于一个窗口应用程序, 弹出的其它窗口肯定不只是像 QMessageBox 一样简单, 它可能会包含一些复杂的东西。 放在这里,是因为在这些复杂的窗口中, 可能会用到之前介绍到的一些东西

首先你应该知道, 前面的 QLabel 也好, QMessagesBox 也好, 在它们的设置中都有一个父节点指针QMessageBox 可能看不出它的用处, 但是 QLabel中,它的作用就十分明显了, 因为它将显示在父节点指针指向的窗口上。

我们常用 QDialog 类来实现弹窗, 但是 QDialog不能作为一个父节点, 所以这是就需要借助到 QWidget 类, 因为父节点指针就是 QWidget 类的。

QWidget 头文件#include<QWidget>

QDialog 头文件#include<QDialog>

Qwidget 其实本身就可以当作一个窗口来使用, 但是 QWidget 只能创建非模态窗口, 而 QDialog 可以自由选择模态和非模态。

解释一下模态非模态窗口: 当一个模态窗口被创建后, 它的父节点指向的窗口是非活动的, 即,不能对它进行任何操作, 所以模态窗口就要求用户先处理弹窗后才能继续其它操作。 因此创建模态窗口的 exec() 函数会等待窗口被关闭后才退出。 而非模态窗口则相反, 它的父节点是活动的, 即,即使弹出了这个窗口, 用户也可以再次选中它的父节点指向的窗口, 并可以不受影响地进行其它操作, 之后再来处理弹窗。 因此创建模态窗口的 show() 函数的作用也就只是显示窗口, 不会阻碍代码的继续运行。

其实让 QDialogQWidget 产生联系也就两行代码, 其它情况下当作QWidget处理就行了。

    QDialog *dialog=new QDialog(this);
    QWidget *widget=new QWidget(dialog);

接下来所有的控件都可以像这个 QWidget 添加, 而 QWidget 本身就可以作为父节点, 所以以切都变得简单起来……

示例:向这个窗口插入一个 QLabel

    QLabel *label=new QLabel(widget);//由于Parent指针本身就是QWidget类型的
    //所以可以直接将widget作为父节点,使这个label与dialog产生关系
    //或者说是让它显示在dialog上。
    label->setText(tr("aaa"));

对于 QWidget 的操作和 MainWindow 的操作相差不大, 只是不能使用 menuBar()

最后是产生窗口的方法:

非模态窗口:

    dialog->show();

模态窗口:

    dialog->exec();

最后附上一个完整代码, 便于理解:

\Header\mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QApplication>
#include <QPushButton>
#include <QMenu>
#include <QToolBar>
#include <QWidget>
#include <QDialog>
#include <QLabel>
#include <QTimer>
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:

};

#endif // MAINWINDOW_H

\Sources\main.cpp:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

\Sources\mainwindow.cpp:

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    QDialog *dialog=new QDialog(this);
    QWidget *widget=new QWidget(dialog);
    QLabel *label=new QLabel(widget);
    label->setText(tr("aaa"));
    dialog->exec();//模态
    dialog->show();//非模态
}

MainWindow::~MainWindow()
{
}

效果图:

先弹出一个模态窗口, 主窗口并没有弹出,说明模态窗口阻拦了代码的继续运行:

8l0N0f.png

弹出一个非模态窗口,主窗口弹出, 且主窗口被选中,说明主窗口后弹出, 所以非模态窗口并未对代码的继续运行产生任何影响

8l0r1s.png


Part 11 Qt 中的一些其它常用控件及食用方法


  1. QPushButton

    早在Part 3介绍机制时,QPushButton就已经被提到。

    头文件#include<QPushButton>

    常见声明方法QPushButton *btn=new QPushButton(QWidget*) 其中参数为父节点

    常用的设置:

    setText(const QString&):设置按钮上的文字

    setParent(QWidget*):设置父节点

    resize(int,int):设置宽、高,即大小;

    move(int,int):设置位置

    setFont(const QFont&):设置文字的字体。 常用的信号: clicked:按钮被按下

  2. QLineEdit

    除了处理按钮,程序当然还要处理文字输入,这时就需要借助 QLineEdit 来输入文字了。

    头文件#include<QLineEdit>

    常见声明方法QLineEdit *edit=new QLineEdit(this); 其中参数为父节点

    常用的设置:

    setParent(QWidget*):设置父节点

    resize(int,int):设置宽、高,即大小;

    move(int,int):设置位置

    displayText():返回一个 QString ,表示当前输入框中的文本 常用的信号: returnPressed:光标在输入框内,且回车被按下,一般用来认定输入结束。

  3. QCheckBox

    有时需要用户进行多选,一直弹窗肯定不好,食用 QCheckBox 来进行选择,就方便多了。

    头文件#include<QCheckBox>

    常见声明方法QCheckBox *checkbox=new QCheckBox(this);,其中参数为父节点

    常见的设置:

    setParent(QWidget*):设置父节点

    toggle():使它进入选中状态;

    setEnable(bool):true为允许更改,false为不允许更改,默认为true;

    isChecked():返回值为bool,表示是否选择

    resize(int,int):设置宽、高,即大小;

    move(int,int):设置位置

最后再附上一个综合应用的示例, 读者也可以试着自己写一下

用一个文本输入框来获取输入, 并把输入的文字同步到下方的一个位置。 用一个 CheckBox 是否选中来决定是否显示同步的文字。

示例代码: \Header\mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QApplication>
#include <QPushButton>
#include <QMenu>
#include <QToolBar>
#include <QWidget>
#include <QDialog>
#include <QLabel>
#include <QTimer>
#include <QLineEdit>
#include <QCheckBox>
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
//建议像这样,把需要再mainwindow中用到的变量都定义在这个类的private里面
//一是更安全,二是Qt会为定义再这里的变量使用另一种颜色标记。
    QLabel *label;
    QLineEdit *edit;
    QCheckBox *checkbox;

    void changeText();//槽函数就必须定义在这里了
    //不然connect没有信号接收者(难不成些nullptr?)
};

#endif // MAINWINDOW_H

\Sources\main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

\Sources\mainwindow.cpp

#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    checkbox=new QCheckBox(this);
    checkbox->move(20,100);
    checkbox->resize(100,20);
    checkbox->setText("Show?");
    checkbox->toggle();
    this->resize(200,200);
    edit=new QLineEdit(this);
    edit->resize(190,20);
    label=new QLabel(this);
    label->move(0,30);//注意label的位置不能和CheckBox的位置冲突
    //否则会导致无法选中CheckBox
    label->resize(190,20);//以上为初始化窗口控件
    QTimer *timer=new QTimer();
    timer->start(0);//为了持续更新,使用QTimer计时0ms
    //即循环不断执行槽函数。
    connect(timer,&QTimer::timeout,this,&MainWindow::changeText);//连接信号和槽
}

MainWindow::~MainWindow()
{
}

void MainWindow::changeText()
{
    if(checkbox->isChecked())//判断是否选择同步展示
        label->setText(edit->displayText());//是就更新文字为LineEdit里的文字
    else
        label->setText(tr(""));//否则设置为空串,即可达到不显示的效果。
}

效果图

同步显示:

8lTXaF.png

不同步显示:

8l7EIe.png


Part 12 一个简单的项目实例


既然了解了这么多,何不写个小程序练练手?

这里就带着读者编写一个简单的计时器

首先需要考虑,一个计时器它需要些什么? 你需要让它在时间到的时候制造一些动静, 例如弹出一个窗口。 你需要让它在窗口显示剩余时间, 你还需要让它能够设置时间

这样大致的思路就比较清晰了: 利用菜单栏来激活时间的设置, 而设置时间应该弹出一个新的窗口。 这个窗口应该是模态窗口, 因为用户要么设置一个时间, 要么就取消, 不能同时进行其它操作。 开始计时也可以利用菜单栏来实现。 要持续更新时间, 就要用到 QTimer 来实现持续调用更新函数, 在时间到的时候需要弹出一个窗口提示, 这个窗口应该能够发出提示音, 如 QMessageBox::information

事不宜迟, 现在就开始行动吧!

下面是完整代码

\Header\mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>
#include <QTimer>
#include <QAction>
#include <QMenuBar>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QByteArray>
#include <QDialog>
#include <QWidget>
#include <QFont>
#include <cstring>
#include <cstdio>
#include <ctime>//各种头文件(大部分都用了吧……)

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private://还是建议把要用的变量定义在这里
    QLabel *TimeLabel;
    QDialog *SetTimeDialog;
    QLineEdit *InputTimeEdit;
    QWidget *SetTimeWidget;
    QPushButton *ButtonYes,*ButtonNo;
    QFont TimeFont,OtherFont;
    QAction *BeginAction,*SetAction;
    QTimer *timer;

    void Unpdate_Time();
    void Begin_Timing();
    void Set_Time();
    void Get_Time();
};

#endif // MAINWINDOW_H

\Sources\main.cpp

#include "mainwindow.h"
#include <QApplication>
//其实main.cpp自始至终都没有改变
//因为它的作用就是创建窗口,显示窗口,获取返回值。
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

\Source\mainwindow.cpp

//为了方便理解,窝还特意把代码的层次划分得十分明显
#include "mainwindow.h"
bool f=0;
int Last_Time=0,Remaining_Time=0,STime=0;//存储有关时间的变量
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    this->setWindowTitle(tr("Timer"));//设置窗口标题
    this->resize(1375,760);//设置窗口大小

    TimeFont.setPixelSize(400);//设置字体大小
    OtherFont.setPixelSize(20);

    TimeLabel=new QLabel(this);//初始化
    TimeLabel->setText(tr("00:00"));//设置初始显示字体,未设定默认为0
    TimeLabel->setAlignment(Qt::AlignCenter);//设置文字居中对齐
    TimeLabel->resize(1375,760);//设置大小,可以根据电脑分辨率适当调整
    TimeLabel->setFont(TimeFont);//设置字体

    BeginAction=new QAction(QIcon(),tr("Start timing"),this);//开始计时的事件
    BeginAction->setShortcut(tr("Ctrl+B"));
    BeginAction->setStatusTip(tr("Start timing."));
    connect(BeginAction,&QAction::triggered,this,&MainWindow::Begin_Timing);
    //连接槽函数

    SetAction=new QAction(QIcon(),tr("Set Time"),this);//设置时间的事件
    SetAction->setShortcut(tr("Ctrl+S"));
    SetAction->setStatusTip(tr("Set Time."));
    connect(SetAction,&QAction::triggered,this,&MainWindow::Set_Time);//连接槽函数

    QMenu *menu=menuBar()->addMenu(tr("Start"));
    menu->addAction(BeginAction);
    menu=menuBar()->addMenu(tr("Set"));
    menu->addAction(SetAction);
    //这里把两种操作分在不同的菜单下,当然你也可以为了方便分在一起

    timer=new QTimer();
    timer->start(0);//持续更新
    connect(timer,&QTimer::timeout,this,&MainWindow::Unpdate_Time);//连接

    statusBar();//设置好StatusTip
}

MainWindow::~MainWindow()
{
}

void MainWindow::Begin_Timing()
{
    f=1;//更改为正在计时状态
}

void MainWindow::Set_Time()
{
    SetTimeDialog=new QDialog(this);
    SetTimeWidget=new QWidget(SetTimeDialog);
    SetTimeDialog->resize(230,100);
    SetTimeWidget->resize(230,100);//设置窗口大小,注意Dialg要和Widget相同
    SetTimeDialog->setWindowTitle(tr("Plaese set time"));//设置窗口标题

    InputTimeEdit=new QLineEdit(SetTimeWidget);//初始化输入框
    InputTimeEdit->resize(150,30);//设置大小
    InputTimeEdit->move(10,10);//移动位置
    InputTimeEdit->setFont(OtherFont);//设置输入框中的字体

    QLabel *Tip=new QLabel(SetTimeWidget);//显示单位用
    Tip->setFont(OtherFont);
    Tip->setText(tr("(s)"));
    Tip->move(175,10);
    Tip->resize(50,30);

    ButtonNo=new QPushButton(SetTimeWidget);
    ButtonYes=new QPushButton(SetTimeWidget);//初始化两个按钮
    ButtonNo->resize(80,30);
    ButtonYes->resize(80,30);//设置两个按钮的大小,为了和谐美,它们应该尽量相同
    ButtonNo->move(110,50);
    ButtonYes->move(10,50);//移动到各自位置,一般确定在前
    ButtonNo->setText(tr("Cancel"));
    ButtonYes->setText(tr("OK"));//设置文字
    ButtonNo->setFont(OtherFont);
    ButtonYes->setFont(OtherFont);//设置字体,PushButton默认居中对齐

    connect(ButtonNo,&QPushButton::clicked,SetTimeDialog,&QDialog::close);
    //取消就直接关闭
    connect(ButtonYes,&QPushButton::clicked,this,&MainWindow::Get_Time);
    //确定就获取信息
    connect(ButtonYes,&QPushButton::clicked,SetTimeDialog,&QDialog::close);
    //当然也要关闭
    connect(InputTimeEdit,&QLineEdit::returnPressed,this,&MainWindow::Get_Time);
    connect(InputTimeEdit,&QLineEdit::returnPressed,SetTimeDialog,&QDialog::close);
    //习惯是按回车结束输入,为了方便,这里也让按下回车作为确定的标志
    SetTimeDialog->show();
}

void MainWindow::Unpdate_Time()
{
    if(Remaining_Time<0)//到了0之后再减一秒,为了防止弹窗不受控制地弹出(初始值为0嘛!)
    {
        Remaining_Time=STime;
        QMessageBox::information(this,tr("Time out!"),tr("!!!Time out!!!"));//提示
        f=0;//调回初始状态
    }
    int m,s;
    char str[10];
    m=Remaining_Time/60;
    s=Remaining_Time%60;
    sprintf(str,"%02d:%02d",m,s);//格式化写入字符串,Dev也可以用
    //写入的效果类似printf输出的效果
    TimeLabel->setText(str);//修改文字
    if(!f)
        return;
    if(Last_Time==0)//等于零说明刚开始,等待到下一个整秒保证精度
    {
        Last_Time=time(NULL);
        return;
    }
    if(time(NULL)!=Last_Time)
    {
        Remaining_Time--;//剩余时间减一
        Last_Time=time(NULL);//调整为新时间
    }
}

void MainWindow::Get_Time()
{
    char *str;
    QByteArray ba=InputTimeEdit->displayText().toLatin1();
    //利用QByteArray作为中转,把QString转换到char*,方便处理
    str=ba.data();
    Remaining_Time=0;
    for(int i=0;i<strlen(str);i++)
    {
        Remaining_Time=Remaining_Time*10+str[i]-'0';//就像快读里的一样,识别数字
        //但是这里没有判断,你也可以自己加上判断语句
        //过滤无用信息,防止乱码
    }
    STime=Remaining_Time;//复制一遍到这个变量,防止忘记~~
    //(其实是之后要用,你总不希望每次都重复地设置相同的时间吧)
}

效果图

881OGq.png

设置:

881jzV.png

计时中:

881xMT.png

结束提示:

883hk9.png

要是你也成功了的话, 那恭喜你,你已经成功完成了 Qt 的入门庆祝一下吧


写在最后的话


其实 Qt 中预置的类远不止这么一点儿, 它们都有各自的用处,读者可以自己了解, 这篇文章也只能帮助你了解 Qt , 并学到一点点的用法

这些确实只是 Qt 表面的一些东西, 如果你想用 Qt 来开发游戏, 那确实很困难, 因为你还需要详细地学习 Qt 的绘制系统, 虽然不得不承认它的绘制系统算法很好, 据说可以同时处理百万对象还不卡顿……

如同 Part 2 所提到的那样, Qt 是由一个个组件构成的, 不会某个组件不会影响你的制作, 学习它就像是在学习如何使用一个个组件, 而这些组件太多了……

但是利用 Qt 做一些简单的界面还是比 Win32 简单很多的。

相信你也可以做出属于自己的窗口应用程序!

小提示:显示中文可能出现乱码(如果你乱动了设置的话,叫你别乱调), 只需要进行如下修改 工具→选项→文本编辑器→文件编码→默认编码:UTF-8, UTF-8 BOM:目前存在了则保留 就可以了


完结撒花 ★,°:.☆( ̄▽ ̄)☆.:°★,