Qt基础知识

说明

本博总结的是豆子先生写的Qt 学习之路2的内容。

窗口部件

基础窗口部件QWidget

//Qt::FramelessWindowHint删除了边框
QWidget *widget = new QWidget(0, Qt::Dialog | Qt::FramelessWindowHint);
//Qt::WindowStaysOnTopHint使窗口置于所有窗口之上
QLabel *label = new QLabel(0, Qt::SplashScreen | Qt::WindowStaysOnTopHint);

返回窗口位置信息:

//返回位置信息
widget.pos();
//返回不包含边框的窗口内部矩形,左上角是(0,0)点
widget.rect();
//返回不包含边框窗口的内部矩形
widget.size();
//返回窗口内部宽高
widget.width();
widget.height();
//调整窗口大小
widget.resize();

信号槽

1.信号和槽

所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,用自己的一个函数(成为槽(slot))来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。

我们关键留意connect函数,它的常用形式:

//connect(sender, signal, receiver, slot);
//发出信号对象,发出的信号,接收信号的对象,接收到信号后需要调用的函数
QPushButton button("Quit");
QObject::connect(&button, &QPushButton::clicked, &QApplication::quit);

connect函数有五个重载:

QMetaObject::Connection connect(const QObject *, const char *,
                                const QObject *, const char *,
                                Qt::ConnectionType);

QMetaObject::Connection connect(const QObject *, const QMetaMethod &,
                                const QObject *, const QMetaMethod &,
                                Qt::ConnectionType);

QMetaObject::Connection connect(const QObject *, const char *,
                                const char *,
                                const Qt::ConnectionType);

QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
                                const QObject *, PointerToMemberFunction,
                                Qt::ConnectionType)

QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
                                Functor);

2.使用信号槽实现观察者模式效果

定义一个报纸类Newspaper,一个订阅者类Subscriber,当Newspaper有了新的内容的时候,Subscriber可以立即得到通知。

在经典的实现代码中,观察者会将自身注册到被观察者的一个容器中(比如subscriber.registerTo(newspaper))。被观察者发生了任何变化的时候,会主动遍历这个容器,依次通知各个观察者(newspaper.notifyAllSubscribers())。

newspaper.h

//继承QObject才能使用信号槽
#include <QObject>
class Newspaper : public QObject
{
    //凡是QObject类(不管是直接子类还是间接子类),都应该在第一行代码写上Q_OBJECT。
    //不管是不是使用信号槽,都应该添加这个宏。(放在头文件里)
    Q_OBJECT
public:
    Newspaper(const QString & name) :
        m_name(name)
    {
    }
    
    //信号作为函数名,不需要在 cpp 函数中添加任何实现
    //没有实现的函数名能通过编译,是因为 moc 会帮我们实现信号函数所需要的函数体
    void send()
    {
        //emit 是一个关键字(其实也是一个宏)。
        //emit 的含义是发出,也就是发出newPaper()信号。
        emit newPaper(m_name);
    }

signals:
    void newPaper(const QString &name);

private:
    QString m_name;
};

reader.h

#include <QObject>
#include <QDebug>

class Reader : public QObject
{
    Q_OBJECT
public:
    Reader() {}

    //槽函数必须实现,同时也会收到public、private约束
    void receiveNewspaper(const QString & name)
    {
        qDebug() << "Receives Newspaper: " << name;
    }
};

main.cpp

#include <QCoreApplication>

#include "newspaper.h"
#include "reader.h"

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    Newspaper newspaper("Newspaper A");
    Reader reader;
    QObject::connect(&newspaper, &Newspaper::newPaper,
                     &reader,    &Reader::receiveNewspaper);
    //报纸类发出信号
    newspaper.send();

    return app.exec();
}

当我们运行上面的程序时,会看到终端输出 Receives Newspaper: Newspaper A 这样的字样。

Qt模块

Qt 基础模块分为以下几个:

  • Qt Core,提供核心的非 GUI 功能,所有模块都需要这个模块。这个模块的类包括了动画框架、定时器、各个容器类、时间日期类、事件、IO、JSON、插件机制、智能指针、图形(矩形、路径等)、线程、XML 等。所有这些类都可以通过 头文件引入。
  • Qt Gui,提供 GUI 程序的基本功能,包括与窗口系统的集成、事件处理、OpenGL 和 OpenGL ES 集成、2D 图像、字体、拖放等。这些类一般由 Qt 用户界面类内部使用,当然也可以用于访问底层的 OpenGL ES 图像 API。Qt Gui 模块提供的是所有图形用户界面程序都需要的通用功能。
  • Qt Multimedia,提供视频、音频、收音机以及摄像头等功能。这些类可以通过 引入,而且需要在 pro 文件中添加 QT += multimedia。
  • Qt Network,提供跨平台的网络功能。这些类可以通过 引入,而且需要在 pro 文件中添加 QT += network。
  • Qt Qml,提供供 QML(一种脚本语言,也提供 JavaScript 的交互机制) 使用的 C++ API。这些类可以通过 引入,而且需要在 pro 文件中添加 QT += qml。
  • Qt Quick,允许在 Qt/C++ 程序中嵌入 Qt Quick(一种基于 Qt 的高度动画的用户界面,适合于移动平台开发)。这些类可以通过 引入,而且需要在 pro 文件中添加 QT += quick。
  • Qt SQL,允许使用 SQL 访问数据库。这些类可以通过 引入,而且需要在 pro 文件中添加 QT += sql。
  • Qt Test,提供 Qt 程序的单元测试功能。这些类可以通过 引入,而且需要在 pro 文件中添加 QT += testlib。
  • Qt Webkit,基于 WebKit2 的实现以及一套全新的 QML API(顺便说一下,Qt 4.8 附带的是 QtWebkit 2.2)。

添加动作QAction

QAction包含了图标、菜单文字、快捷键、状态栏文字、浮动帮助等信息。当把一个QAction对象添加到程序中时,Qt 自己选择使用哪个属性来显示,无需我们关心。

QMainWindow中使用QAction
mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

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

private:
    void open();

    QAction *openAction;
};

#endif // MAINWINDOW_H

mainwindow.cpp

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

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    //tr()函数,提取字符串,进行国际化
    setWindowTitle(tr("Main Window"));

    //使用QIcon传入图像,输入的是字符串(图像路径)
    //第二个参数中,文本前加"&"表示是快捷键
    openAction = new QAction(QIcon(":/images/doc-open"), tr("&Open..."), this);
    
    //setShortcut()用于说明 QAction 的快捷键
    //QKeySequence定义了内置的快捷键,根据平台不同而改变
    openAction->setShortcuts(QKeySequence::Open);
    
    //实现了当用户鼠标滑过这个 action 时,会在主窗口下方的状态栏显示相应的提示。
    openAction->setStatusTip(tr("Open an existing file"));
    
    //此处connect函数将这个QAction的triggered()信号与MainWindow类的open()函数连接起来。
    connect(openAction, &QAction::triggered, this, &MainWindow::open);

    //向菜单栏添加了一个 File 菜单,并且把这个QAction对象添加到这个菜单
    //menuBar()是QMainWindow提供的函数
    QMenu *file = menuBar()->addMenu(tr("&File"));
    file->addAction(openAction);

    //同时新增加了一个 File 工具栏,也把QAction对象添加到了这个工具栏
    QToolBar *toolBar = addToolBar(tr("&File"));
    toolBar->addAction(openAction);

    statusBar() ;
}

MainWindow::~MainWindow()
{
}

void MainWindow::open()
{
    QMessageBox::information(this, tr("Information"), tr("Open"));
}

对象模型

标准 C++ 对象模型在运行时效率方面卓有成效,但是在某些特定问题域下的静态特性就显得捉襟见肘。GUI 界面需要同时具有运行时的效率以及更高级别的灵活性。为了解决这一问题,Qt “扩展”了标准 C++。所谓“扩展”,实际是在使用标准 C++ 编译器编译 Qt 源程序之前,Qt 先使用一个叫做 moc(Meta Object Compiler,元对象编译器)的工具,先对 Qt 源代码进行一次预处理(注意,这个预处理与标准 C++ 的预处理有所不同。Qt 的 moc 预处理发生在标准 C++ 预处理器工作之前,并且 Qt 的 moc 预处理不是递归的。),生成标准 C++ 源代码,然后再使用标准 C++ 编译器进行编译。

通过继承QObeject类可以使用这个特性。

Qt 对象树

MainWindow中的 parent 指针的作用:QObject是以对象树的形式组织起来的。当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是 parent,也就是父对象指针。这相当于,在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类!)例如,一个按钮有一个QShortcut(快捷键)对象作为其子对象。当我们删除按钮的时候,这个快捷键理应被删除。这是合理的。

QWidget是能够在屏幕上显示的一切组件的父类。QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。

Qt 引入对象树的概念,在一定程度上解决了内存问题。

任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。

当QObject在栈上创建:

{
    QWidget window;
    QPushButton quit("Quit", &window);
}

这段代码在超出作用域时,会先调用 quit 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。

布局管理

只要把组件放入某一种布局,布局由专门的布局管理器进行管理。当需要调整大小或者位置的时候,Qt 使用对应的布局管理器进行调整。

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QWidget window;
    window.setWindowTitle("Enter your age");

    QSpinBox *spinBox = new QSpinBox(&window);
    QSlider *slider = new QSlider(Qt::Horizontal, &window);
    spinBox->setRange(0, 130);
    slider->setRange(0, 130);

    //两个connect函数完成双向的数据绑定
    QObject::connect(slider, &QSlider::valueChanged, spinBox, &QSpinBox::setValue);
    //因为QSpinBox有两个信号:
    //valueChanged(int)和valueChanged(const QString&)
    //编译器会不知道调用那个函数,故创建的时候指定这个函数指针参数为int
    void (QSpinBox:: *spinBoxSignal)(int) = &QSpinBox::valueChanged;
    QObject::connect(spinBox, spinBoxSignal, slider, &QSlider::setValue);
    spinBox->setValue(35);

    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(spinBox);
    layout->addWidget(slider);
    window.setLayout(layout);

    window.show();

    return app.exec();
}

Qt 提供了几种布局管理器:

  • QHBoxLayout:按照水平方向从左到右布局;
  • QVBoxLayout:按照竖直方向从上到下布局;
  • QGridLayout:在一个网格中进行布局,类似于 HTML 的 table;
  • QFormLayout:按照表格布局,每一行前面是一段文本,文本后面跟随一个组件(通常是输入框),类似 HTML 的 form;
  • QStackedLayout:层叠的布局,允许我们将几个组件按照 Z 轴方向堆叠,可以形成向导那种一页一页的效果。

菜单栏、工具栏和状态栏

QMenuBar代表的是窗口最上方的一条菜单栏。我们使用其addMenu函数为其添加菜单。尽管我们只是提供了一个字符串作为参数,但是 Qt 为将其作为新创建的菜单的文本显示出来。

QToolBar就是工具栏。我们使用的是addToolBar()函数添加新的工具栏。

QToolBar *toolBar = addToolBar(tr("&File"));
toolBar->addAction(openAction);

QToolBar *toolBar2 = addToolBar(tr("Tool Bar 2"));
toolBar2->addAction(openAction);

对话框

简介

Qt 中使用QDialog类实现对话框。就像主窗口一样,我们通常会设计一个类继承QDialog。QDialog(及其子类,以及所有Qt::Dialog类型的类)的对于其 parent 指针都有额外的解释:如果 parent 为 NULL,则该对话框会作为一个顶层窗口,否则则作为其父组件的子对话框。

void MainWindow::open()
{
    //没有设置对话框的parent指针
    QDialog dialog;
    dialog.setWindowTitle(tr("Hello, dialog!"));
    dialog.exec();
}

显示该对话框会作为一个顶层窗口,出现在屏幕的中间。

void MainWindow::open()
{
    QDialog dialog(this);
    dialog.setWindowTitle(tr("Hello, dialog!"));
    dialog.exec();
}

出现在父窗口的中间。

对话框分为模态对话框和非模态对话框。

  • 模态对话框:就是会阻塞同一应用程序中其它窗口的输入。
  • 非模态对话框:例如查找搜索框。

同时模态对话框又分:

  • 应用程序级别的模态:当该种模态的对话框出现时,用户必须首先对对话框进行交互,直到关闭对话框,然后才能访问程序中其他的窗口。
  • 窗口级别的模态:该模态仅仅阻塞与对话框关联的窗口,但是依然允许用户与程序中其它窗口交互。

Qt 使用QDialog::exec()实现应用程序级别的模态对话框,使用QDialog::open()实现窗口级别的模态对话框,使用QDialog::show()实现非模态对话框。

使用show()实现非模态:

void MainWindow::open()
{
    //这里使用QDialog dialog(this);的话会看不到结果
    //因为show不会阻塞线程,show返回,open函数结束,对话框被析构
    QDialog *dialog = new QDialog;
    dialog->setWindowTitle(tr("Hello, dialog!"));
    dialog->show();
}

对话框数据传递

模态对话框使用了**exec()**函数将其显示出来。exec()函数的真正含义是开启一个新的事件循环。所谓事件循环,可以理解成一个无限循环。Qt 在开启了事件循环之后,系统发出的各种事件才能够被程序监听到。这个事件循环相当于一种轮询的作用。既然是无限循环,当然在开启了事件循环的地方,代码就会被阻塞,后面的语句也就不会被执行到。因此,对于使用了exec()显示的模态对话框,我们可以在exec()函数之后直接从对话框的对象获取到数据值。

示例代码:

void MainWindow::open()
{
    QDialog dialog(this);
    dialog.setWindowTitle(tr("Hello, dialog!"));
    //这里执行exec会阻塞代码,result会一直无法输出直到关闭窗口
    dialog.exec();
    //qDebug类似于std::cout
    qDebug() << dialog.result();
}

exec是有返回值的:Accepted或者Rejected

一般根据返回值判断用户点击的是“确认”还是“取消”:

QDialog dialog(this);
if (dialog.exec() == QDialog::Accepted) {
    // do something
} else {
    // do something else
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值