1.Qt开发初步

本文介绍了Qt库的特点,包括跨平台性、对象树管理、信号槽机制和使用QtCreator进行项目开发。详细讲解了如何创建窗口、设置布局、使用信号槽进行事件处理,以及QMainWindow和QDialog的使用。同时,讨论了对话框的模态和非模态特性,以及文件对话框和资源文件的应用。最后,简述了Qt的UI布局管理器。
摘要由CSDN通过智能技术生成


前言

Qt连载:
1.Qt开发初步
2.Qt常用控件
3.Qt嵌入式编程

QT下载:
http://download.qt.io
我边学习边总结的,很多内容来自于网上的好文,有错误的地方望指正
比较入门的知识,熟练使用还是少不了一本书,不管是深入学习还是查阅
我下载的源码版本:https://download.qt.io/archive/qt/4.8/4.8.4/qt-everywhere-opensource-src-4.8.4.tar.gz
我下载的qt creater:https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qtcreator/2.5/qt-creator-linux-x86_64-opensource-2.5.2.bin
附上多本Qt书的网盘链接:
https://blog.csdn.net/weixin_44705391/article/details/113863535

1.特点

1.可以看成是 “图形界面库” + “idl” 的组合。qt creater作为ide,结合qt源码来编译工程。
2.qt源码可以通过各种复杂配置来选择自己想要的功能,但是需要安装各种库比如opengl、zlib、tslib
3.注意版本,5以上需要c++11标准,注意自己的编译器是否满足
4.可通过 “交叉编译后的qmake” + “编译工具链” + “自己的工程” 生成可在嵌入式设备上执行的程序
5.qt creater安装时默认使用gcc,其qmake也默认使用gcc编译的qmake
6.Linux、Windows、MAC OS、ios、ARM等平台都可用

2.QT creator使用

1.选择合适的基类 在新建工程时需要选择基类,我们先选精简版的窗口
父类 Qwidget 精简版窗口
子类 QMainWindow 带菜单栏的窗口
子类 QDialog 对话框
2.QT助手是最靠谱的参考文档 (但是Qt的assistant帮助文档会更好用,在开始栏Qt文件夹中可找到)点击索引即可查找需要的控件,前提是记住控件名称

3.工程

1.pro工程文件

一般不需要改

2.按钮示例

(代码中的解释文字重要 )可选择添加ui或者不添加ui文件,以下为不添加ui示例:
main.cpp:

#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
    //QApplication应用程序类,初始化我们的应用程序
    QApplication a(argc, argv);

    //创建一个主窗口控件
    Widget w;
    //显示主窗口,hide隐藏窗口(窗口默认是隐藏的,所以创建了之后一定要调用show)
    w.show();

    //a.exec()主事件循环(带阻塞 等待用户操作界面)widget.cpp
    return a.exec();
}

widget.cpp:

#include "widget.h"
#include <QPushButton>

//界面的设计在这,窗口控件的构造函数中
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //this代表当前主窗口,如果不设置title,则窗口名字和类名相同
    this->setWindowTitle("第一个窗口");
    //固定窗口(默认是窗口大小可以拉的)
    //this->setFixedSize(800,600);  //窗口不可以拖动
    //设置窗口大小
    this->resize(400,300);      //窗口可以拖动

    //在窗口上放一个按钮,包含头文件,qmake中增加模块QT += widgets	(默认是添加了的),(QT助手)
    //parent父对象(和父类的概念不同,表示button将来希望被谁接管)this表示button放到当前窗口,将来负责析构button,
    //所以button下面是不需要显式调用delete的,也不需要调用~QPushButton析构函数
    QPushButton *button1 = new QPushButton("跟屁虫",this);
    //用new的好处:下面的对象树会讲,用局部变量,栈来存储会有什么坏处

    //如下分开设置
    QPushButton *button2 = new QPushButton;
    button2->setParent(this);
    button2->setText("Richard");
    //默认控件会显示到主窗口左上方,会覆盖button1
    //移动,move函数在QPushButton的爷爷类中
    button2->move(400,300);     //刚好被挡住,拖动窗口显示按钮
}

Widget::~Widget()
{
}

3.对象数

解释了为什么分配在堆上比分配在栈上好
一句话概括就是子对象会被交由父对象接管,关闭程序时父对象会负责清理子对象留下的垃圾。如上述代码。
在Qt中创建对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。

 QObject是以对象树的形式组织起来的。
 当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是 parent,也就是父对象指针。
这相当于,在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。
 当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类!)
这种机制在 GUI 程序设计中相当有用。例如,一个按钮有一个QShortcut(快捷键)对象作为其子对象。当我们删除按钮的时候,这个快捷键理应被删除。这是合理的。
 QWidget是能够在屏幕上显示的一切组件的父类。
 QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。
 当然,我们也可以自己删除子对象,它们会自动从其父对象列表中删除。比如,当我们删除了一个工具栏时,其所在的主窗口会自动将该工具栏从其子对象列表中删除,并且自动调整屏幕显示。

Qt 引入对象树的概念,在一定程度上解决了内存问题。
 当一个QObject对象在上创建的时候,Qt 会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。
 任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。
如果QObject在上创建,Qt 保持同样的行为。正常情况下,这也不会发生什么问题。来看下下面的代码片段:

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

作为父组件的 window 和作为子组件的 quit 都是QObject的子类(事实上,它们都是QWidget的子类,而QWidget是QObject的子类)。这段代码是正确的,quit 的析构函数不会被调用两次,因为标准 C++要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作用域时,会先调用 quit 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。
但是,如果我们使用下面的代码:

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

情况又有所不同,析构顺序就有了问题。我们看到,在上面的代码中,作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,也就是说, quit 此时就被析构了。然后,代码继续执行,在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第二次调用 quit 的析构函数了,C++ 不允许调用两次析构函数,因此,程序崩溃了。
由此我们看到,Qt 的对象树机制虽然帮助我们在一定程度上解决了内存问题,但是也引入了一些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以,我们最好从开始就养成良好习惯,在 Qt 中,尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建。

4.信号槽

(关于信号槽百度上有很多解释,在这就不多做解释了)
类似于中断绑定:
在这里插入图片描述
其中的关键就是connect函数:
connect(sender, signal, receiver, slot);
参数:
sender:发出信号的对象
signal:发送对象发出的信号
receiver:接收信号的对象
slot:接收对象在接收到信号之后所需要调用的函数

//点击按钮关闭窗口
QPushButton *p = new QPushButton("关闭",this);
//设置连接,点击按钮产生信号,会调用窗口的close函数
connect(p,&QPushButton::clicked,this,&QWidget::close);

自定义一个print槽函数
首先需要在widget类添加槽成员函数print:

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
public slots:				//添加槽成员函数
    void print();			//
};
#endif // WIDGET_H

实现槽函数:
注意如果信号没有参数,槽函数也不能有参数,如果信号有参数,槽函数可以有也可以没有

#include "widget.h"
#include <QPushButton>
#include <iostream>
#include <QDebug>
using namespace std;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    QPushButton *p = new QPushButton("点击打印",this);
    //如果信号没有参数,槽函数也不能有参数,如果信号有参数,槽函数可以有也可以没有
    connect(p,&QPushButton::pressed,this,&Widget::print);
}

void Widget::print()
{
    cout << "2021 Richard Stewart" << endl;
    //qDebug() << "2021 Richard Stewart";       //和上面代码效果一样
}
Widget::~Widget()
{
}

自定义信号:
考虑这样一个问题:新建两个窗口,每个窗口实现一个按钮,按钮功能为隐藏本窗口,显示对方窗口。
1.我们可以在父窗口中创建子窗口,因为有子窗口的地址,那么可以用父.hide();子->show();来实现。
2.但是子窗口中没有父窗口地址,不能用指针来实现,那么在子窗口定义一个信号,在父窗口设置接收到信号时回调一个槽函数,用槽函数来完成隐藏子窗口,显示父窗口。
在这里插入图片描述

方法一:将子窗口设为父窗口的成员

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    QWidget *sonwind;

public slots:
    void func_father();
    void func_son();
};
#endif // WIDGET_H
#include "widget.h"
#include <QPushButton>
#include <iostream>
#include <QDebug>
using namespace std;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->resize(300,300);

    QPushButton *button1 = new QPushButton("点击父窗口",this);
    connect(button1,&QPushButton::clicked,this,&Widget::func_father);

    sonwind = new QWidget;
    sonwind->resize(300,300);
    sonwind->show();

    QPushButton *button2 = new QPushButton("点击子窗口",sonwind);
    connect(button2,&QPushButton::clicked,this,&Widget::func_son);
    button2->move(50,50);
}
void Widget::func_father()
{
    this->hide();
    this->sonwind->show();
}
void Widget::func_son()
{
    this->show();
   this->sonwind->hide();
}
Widget::~Widget()
{
}

方法二:新建一个子窗口文件
右击工程->添加新文件->c++ class->输入名称SONwidget->选择基类为QWidget->完成,生成两个文件:sonwindow.hpp、sonwindow.cpp
sonwindow.hpp:

#ifndef SONWIDGET_H
#define SONWIDGET_H
#include <QWidget>
#include <QPushButton>
class SONwidget : public QWidget
{
    Q_OBJECT
public:
    explicit SONwidget(QWidget *parent = nullptr);
    QPushButton *button2;
signals:
    //信号没有返回值,可以有参数,参数会传给与之绑定的槽函数。信号函数不需要定义,只需要声明
    void show_hide_signal(int a);
public slots:
    void emit_signal();
};
#endif // SONWIDGET_H

sonwindow.cpp:

#include "sonwidget.h"

SONwidget::SONwidget(QWidget *parent) : QWidget(parent)
{
    this->resize(300,300);
    button2 = new QPushButton("点击子窗口",this);
    connect(button2,&QPushButton::clicked,this,&SONwidget::emit_signal);
}

void SONwidget::emit_signal()
{
    //发送:
    emit show_hide_signal(10);
}

widget.hpp:

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPushButton>
#include "sonwidget.h"
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    SONwidget *sonwindow;
    QPushButton *button1;
public slots:
    void button_set();
    void signal_set(int a);
};
#endif // WIDGET_H

widget.cpp:

#include "widget.h"
#include <QPushButton>
#include <iostream>
#include <QDebug>
using namespace std;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->resize(300,300);
    button1 = new QPushButton("点击父窗口",this);
    this->sonwindow = new SONwidget;
    sonwindow->show();
    connect(button1,&QPushButton::clicked,this,&Widget::button_set);
    connect(sonwindow,&SONwidget::show_hide_signal,this,&Widget::signal_set);
}
void Widget::button_set()
{
    this->hide();
    this->sonwindow->show();
}
void Widget::signal_set(int a)
{
    this->show();
    this->sonwindow->hide();
    cout << a << endl;
}
Widget::~Widget()
{
}

信号和槽函数可以重载,注意避免二义性问题,其解决方法如下:
方法1:

void (sub::*fun1)() = &sub::mysignal;
void (sub::*fun2)(int,QString) = &sub::mysignal;
connect(s,fun1,this,&QWidget::show);
connect(s,fun2,this,&QWidget::show);

方法2,使用Qt4的connect
局限性:SIGNAL、SLOT将变量变为字符串,导致不做类型判断
槽函数前面必须在类中加slots关键字

connect(s,SIGNAL(mysignal()),this,SLOT(dealSub()));
connect(s,SIGNAL(mysignal(int,QString)),this,SLOT(dealSlot()));

方法3,lambda表达式(仅限c++11,注意自己的编译器是否支持c++11)

connect(b4,&QPushButton::clicked,
        // = :把外部所有局部变量,类中所有成员以值传递方式(只读)
        // this :类中所有成员以值传递方式
        // & :把外部所有局部变量引用传递方式(不太安全)
        [=]()
        {
            b4->setText("123");
            qDebug() << "asfas";
        }
        //加mutable使得值可变
        [=](bool isCheck)mutable
        {
            qDebug() << isCheck;
        }
        );

关于信号和槽的总结:
1.槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响
2.发送者和接收者都需要是 QObject 的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
3.任何成员函数、static函数、全局函数、lambda表达式都可作为槽函数
4.槽函数的参数可以比信号少,但是顺序要保持一致,这是因为信号和槽的实现都是Qt完成的,使用时要遵循其内部实现原理。
拓展:
1.一个信号可以和多个槽相连
2.多个信号可以连接到一个槽
3.一个信号可以连接到另外的一个信号(当第一个信号发出时,第二个信号被发出。这种信号-信号的形式和信号-槽的形式没有什么区别。)
4.槽可以被取消链接(当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。)
5.信号槽可以断开:使用disconnect函数
6.使用lambda表达式

4.基类

已经讲了Qwidget、接下来讲QMainWindow、QDialog

1.初遇QMainWindow

之前的都是围绕QWidget进行的,接下来是QMainWindow内容。
mainwindow.cpp:

#include "mainwindow.h"
#include <QMenuBar>
#include <QAction>
#include <QToolBar>
#include <QStatusBar>
#include <QLabel>
#include <QDockWidget>
#include <QTextEdit>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{

    this->resize(500,500);
    //取出菜单栏
    QMenuBar *menubar1 = this->menuBar();
    //往菜单栏添加菜单
    QMenu *filemenu = menubar1->addMenu("文件");
    QMenu *fileedit = menubar1->addMenu("编辑");
    //向菜单添加菜单项
    QAction *open = filemenu->addAction("打开");
    //菜单项之间添加分割线
    filemenu->addSeparator();
    QAction *save = filemenu->addAction("保存");

    //获取工具栏
    QToolBar *toolbar = this->addToolBar("");
    //向工具栏添加工具(添加菜单项)
    toolbar->addAction(open);
    toolbar->addAction(save);

    //取出状态栏
    QStatusBar *status = this->statusBar();
    status->addWidget(new QLabel("状态"));//向状态栏添加控件

    //铆接控件(浮动窗口)
    //QDockWidget *dockwidget = new QDockWidget("这是一个铆接部件",this);
    //this->addDockWidget(Qt::TopDockWidgetArea,dockwidget);
    
    //中心控件
    QTextEdit *edit = new QTextEdit("文本编辑器"this);
    this->setCentralWidget(edit);
    
}

MainWindow::~MainWindow()
{
}

资源文件
通俗的讲就是将文件以二进制格式编译进工程,好处是不会像用外部链接一样当链接丢失时错误。
右击工程->添加新文件->qt->Qt Resource File->完成。
左边编辑按钮->添加 前缀->添加 文件->可以将资源改别名,这样以后就可以用这个别名->点击编译按钮即可将文件编译进刚刚新建的资源文件。
右击资源可以复制资源的路径。

QPixmap pic;    //定义一个图片对象
pic.load(":/new/prefix1/a.jpg");       //给图片对象加载图片
QPushButton->setIcon(QIcon(pic));

2.对话框 QDialog

概念
1.通常是一个顶层窗口,出现在程序最上层,用于实现短期任务或者简洁的用户交互。
2.分为模态对话框非模态对话框
模态对话框,就是会阻塞同一应用程序中其它窗口的输入。
非模态对话框,例如查找对话框
标准对话框(Qt实现的对话框)
QColorDialog: 选择颜色;
QFileDialog: 选择文件或者目录;
QFontDialog: 选择字体;
QInputDialog: 允许用户输入一个值,并将其值返回;
QMessageBox: 模态对话框,用于显示信息、询问问题等;
QPageSetupDialog: 为打印机提供纸张相关的选项;
QPrintDialog: 打印机配置;
QPrintPreviewDialog:打印预览;
QProgressDialog: 显示操作过程。
例子:

connect(button,&QPushButton::clicked,this,[](){
        //QString str = QFileDialog::getOpenFileName();
        //qDebug()<< str;
        //返回多个路径:
        QStringList strlist = QFileDialog::getOpenFileNames();
        qDebug()<<strlist;
    });

模态对话框
1.应用程序级别的模态
2.窗口级别的模态
一般默认是应用程序级别的模态。
如下代码调用了exec(),阻塞,这就是模态对话框。

 	QDialog dialog;
    	dialog.setWindowTitle(tr("Hello, dialog!"));
dialog.exec();

非模态对话框
将exec()修改为show(),对话框闪退

QDialog dialog(this);
    	dialog.setWindowTitle(tr("Hello, dialog!"));
   	 	dialog.show();

这是因为,show()函数不会阻塞当前线程,对话框会显示出来,然后函数立即返回,代码继续执行。dialog 是建立在栈上的,show()函数返回,MainWindow::open()函数结束,dialog 超出作用域被析构,因此对话框消失了。
将 dialog 改成堆上建立:

QDialog *dialog = new QDialog;
    dialog->setWindowTitle(tr("Hello, dialog!"));
    dialog->show();

但是这样dialog 存在内存泄漏。dialog 使用 new 在堆上分配空间,却一直没有 delete。解决方案也很简单:将 MainWindow 的指针赋给 dialog 即可,变成父对象

QDialog *dialog = new QDialog(this);
    dialog->setWindowTitle(tr("Hello, dialog!"));
    dialog->show();

这样做有一个问题:如果我们的对话框不是在一个界面类中出现呢?由于QWidget的 parent 必须是QWidget指针,那就限制了我们不能将一个普通的 C++ 类指针传给 Qt 对话框。另外,如果对内存占用有严格限制的话,当我们将主窗口作为 parent 时,主窗口不关闭,对话框就不会被销毁,所以会一直占用内存。在这种情景下,我们可以设置 dialog 的WindowAttribute:

QDialog *dialog = new QDialog;
	dialog->setAttribute(Qt::WA_DeleteOnClose);
	dialog->setWindowTitle(tr("Hello, dialog!"));
	dialog->show();

setAttribute()函数设置对话框关闭时,自动销毁对话框。
消息对话框
QMessageBox用于显示消息提示。我们一般会使用其提供的几个 static 函数:
1.显示关于对话框。
void about(QWidget * parent, const QString & title, const QString & text)
这是一个最简单的对话框,其标题是 title,内容是 text,父窗口是 parent。对话框只有一个 OK 按钮。
例子:注意lambda的=,&

connect(button1,&QPushButton::clicked,this,[=](){
        QMessageBox::about(this,"标题","内容");
    });

2.显示关于 Qt 对话框。该对话框用于显示有关 Qt 的信息。
void aboutQt(QWidget * parent, const QString & title = QString()):
3.显示严重错误对话框。
StandardButton critical(QWidget * parent,
const QString & title,
const QString & text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton):
这个对话框将显示一个红色的错误符号。我们可以通过 buttons 参数指明其显示的按钮。默认情况下只有一个 Ok 按钮,我们可以使用StandardButtons类型指定多种按钮。
与QMessageBox::critical()类似,不同之处在于这个对话框提供一个普通信息图标。
StandardButton information(QWidget * parent,
const QString & title,
const QString & text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton)
与QMessageBox::critical ()类似,不同之处在于这个对话框提供一个问号图标,并且其显示的按钮是“是”和“否”。
StandardButton question(QWidget * parent,
const QString & title,
const QString & text,
StandardButtons buttons = StandardButtons( Yes | No ),
StandardButton defaultButton = NoButton)
与QMessageBox::critical()类似,不同之处在于这个对话框提供一个黄色叹号图标。
StandardButton warning(QWidget * parent,
const QString & title,
const QString & text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton)
我们可以通过下面的代码来演示下如何使用QMessageBox。

connect(button,&QPushButton::clicked,this,[=](){
            //QMessageBox::question(this,"标题","你需要吗");后面两个参数可以省略,默认为yes|no
            StandardButton ret = QMessageBox::question(this,"标题","你需要吗",QMessageBox::Open|QMessageBox::Save);
    if(ret == QMessageBox::Open)
        qDebug() << "open";
    else 
        qDebug() << "save";
    });
if (QMessageBox::Yes == QMessageBox::question(this,
              tr("Question"), tr("Are you OK?"),
              QMessageBox::Yes | QMessageBox::No,
              QMessageBox::Yes))
    QMessageBox::information(this, tr("Hmmm..."),tr("I'm glad to hear that!"));
else
    QMessageBox::information(this, tr("Hmmm..."),tr("I'm sorry!"));

我们使用QMessageBox::question()来询问一个问题。

这个对话框的父窗口是 this。
QMessageBox是QDialog的子类,这意味着它的初始显示位置将会是在 parent 窗口的中央。
第二个参数是对话框的标题。
第三个参数是我们想要显示的内容。
第四个参数是关联的按键类型,我们可以使用或运算符(|)指定对话框应该出现的按钮。比如我们希望是一个 Yes 和一个 No。
最后一个参数指定默认选择的按钮。
这个函数有一个返回值,用于确定用户点击的是哪一个按钮。按照我们的写法,应该很容易的看出,这是一个模态对话框,因此我们可以直接获取其返回值。

QMessageBox类的 static 函数优点是方便使用,缺点也很明显:非常不灵活。我们只能使用简单的几种形式。为了能够定制QMessageBox细节,我们必须使用QMessageBox的属性设置 API。如果我们希望制作一个询问是否保存的对话框,我们可以使用如下的代码:

QMessageBox msgBox;
msgBox.setText(tr("The document has been modified."));
msgBox.setInformativeText(tr("Do you want to save your changes?"));
msgBox.setDetailedText(tr("Differences here..."));
msgBox.setStandardButtons(QMessageBox::Save
                          | QMessageBox::Discard
                          | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Save);
int ret = msgBox.exec();
switch (ret)
{
	case QMessageBox::Save:
	    qDebug() << "Save document!";
	    break;
	case QMessageBox::Discard:
	    qDebug() << "Discard changes!";
	    break;
	case QMessageBox::Cancel:
	    qDebug() << "Close document!";
	    break;
}

msgBox 是一个建立在栈上的QMessageBox实例。我们设置其主要文本信息为“The document has been modified.”,informativeText 则是会在对话框中显示的简单说明文字。下面我们使用了一个detailedText,也就是详细信息,当我们点击了详细信息按钮时,对话框可以自动显示更多信息。我们自己定义的对话框的按钮有三个:保存、丢弃和取消。然后我们使用了exec()是其成为一个模态对话框,根据其返回值进行相应的操作。
标准文件对话框
QFileDialog,也就是文件对话框。在本节中,我们将尝试编写一个简单的文本文件编辑器,我们将使用QFileDialog来打开一个文本文件,并将修改过的文件保存到硬盘。
首先,我们需要创建一个带有文本编辑功能的窗口。

openAction = new QAction(QIcon(":/images/file-open"),tr("&Open..."), this);
openAction->setStatusTip(tr("Open an existing file"));

saveAction = new QAction(QIcon(":/images/file-save"), tr("&Save..."), this);
saveAction->setStatusTip(tr("Save a new file"));

QMenu *file = menuBar()->addMenu(tr("&File"));
file->addAction(openAction);
file->addAction(saveAction);

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

textEdit = new QTextEdit(this);
setCentralWidget(textEdit);

我们在菜单和工具栏添加了两个动作:打开和保存。接下来是一个QTextEdit类,这个类用于显示富文本文件。也就是说,它不仅仅用于显示文本,还可以显示图片、表格等等。不过,我们现在只用它显示纯文本文件。QMainWindow有一个setCentralWidget()函数,可以将一个组件作为窗口的中心组件,放在窗口中央显示区。显然,在一个文本编辑器中,文本编辑区就是这个中心组件,因此我们将QTextEdit作为这种组件。

我们使用connect()函数,为这两个QAction对象添加响应的动作:

connect(openAction, &QAction::triggered,this, &MainWindow::openFile);
connect(saveAction, &QAction::triggered,this, &MainWindow::saveFile);

下面是最主要的openFile()和saveFile()这两个函数的代码:

//打开文件
void MainWindow::openFile()
{
    QString path = QFileDialog::getOpenFileName(this,tr("Open File"), ".", tr("Text Files(*.txt)"));
    if(!path.isEmpty())
	{
        QFile file(path);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
		{
            QMessageBox::warning(this, tr("Read File"), tr("Cannot open file:\n%1").arg(path));
            return;
        }
        QTextStream in(&file);
        textEdit->setText(in.readAll());
        file.close();
	}
	else
        QMessageBox::warning(this, tr("Path"), tr("You did not select any file."));
}
//保存文件
void MainWindow::saveFile()
{
    QString path = QFileDialog::getSaveFileName(this,tr("Open File"), ".", tr("Text Files(*.txt)"));
    if(!path.isEmpty())
	{
        QFile file(path);
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
		{
            QMessageBox::warning(this, tr("Write File"),tr("Cannot open file:\n%1").arg(path));
            return;
        }
        QTextStream out(&file);
        out << textEdit->toPlainText();
        file.close();
    }
else
        QMessageBox::warning(this, tr("Path"),tr("You did not select any file."));
}

在openFile()函数中,我们使用QFileDialog::getOpenFileName()来获取需要打开的文件的路径。这个函数原型如下:

QString getOpenFileName(QWidget * parent = 0,
                        const QString & caption = QString(),
                        const QString & dir = QString(),
                        const QString & filter = QString(),
                        QString * selectedFilter = 0,
                        Options options = 0)

不过注意,它的所有参数都是可选的,因此在一定程度上说,这个函数也是简单的。这六个参数分别是:
parent:父窗口。
Qt 的标准对话框提供静态函数,用于返回一个模态对话框;
caption:对话框标题;
dir:对话框打开时的默认目录
“.” 代表程序运行目录
“/” 代表当前盘符的根目录(特指 Windows 平台;Linux 平台当然就是根目录),这个参数也可以是平台相关的,比如“C:\”等;
filter:过滤器。
我们使用文件对话框可以浏览很多类型的文件,但是,很多时候我们仅希望打开特定类型的文件。比如,文本编辑器希望打开文本文件,图片浏览器希望打开图片文件。过滤器就是用于过滤特定的后缀名。如果我们使用“Image Files(.jpg .png)”,则只能显示后缀名是 jpg 或者 png 的文件。如果需要多个过滤器,使用“;;”分割,比如“JPEG Files(.jpg);;PNG Files(.png)”;
selectedFilter:默认选择的过滤器;
options:对话框的一些参数设定
比如只显示文件夹等等,它的取值是enum QFileDialog::Option,每个选项可以使用 | 运算组合起来。
QFileDialog::getOpenFileName()返回值是选择的文件路径。我们将其赋值给 path。通过判断 path 是否为空,可以确定用户是否选择了某一文件。只有当用户选择了一个文件时,我们才执行下面的操作。
在saveFile()中使用的QFileDialog::getSaveFileName()也是类似的。使用这种静态函数,在 Windows、Mac OS 上面都是直接调用本地对话框,但是 Linux 上则是QFileDialog自己的模拟。这暗示了,如果你不使用这些静态函数,而是直接使用QFileDialog进行设置,那么得到的对话框很可能与系统对话框的外观不一致。这一点是需要注意的

5.ui布局管理器

创建工程勾选ui项
widget.cpp中ui_widget.h的来历:系统下有个叫做UIC的工具解析ui文件就生成了.h文件,这个文件在编译之后才会有。
可以给按钮增加图片:右下栏的QAbstractButton->icon->添加图片
之后会在ui文件中看到图片的路径,此路径不是相对ui文件的路径,而是相对makefile的路径。我们在qt下运行是可以的,但是在windows下点击.exe文件运行则图片显示不出了,因为图片的路径不对,不是相对.exe文件的路径。所以之前讲过的资源文件的作用就体现出来了。制作资源文件后在icon栏选择添加资源。

参考文章:
关于对象树的解释来自:https://www.cnblogs.com/gd-luojialin/p/9215707.html
对话框来自https://blog.csdn.net/weixin_45525272/article/details/106858517

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值