Qt学习笔记

入门

创建一个项目

步骤

Create Project > Application(Qt) > Qt Widgets Application

generate form:创建界面,暂不选

Base class

这里选择个QWidget(非窗口的普通类可以选顶级类QObject,以便加入对象树)

  • QWidget
    QDialogQMainClass的父类,是个空窗口
  • QDialog
    对话框
  • QMainClass
    一些菜单栏、工具栏、状态栏之类的
main.cpp
#include "widget.h"

//应用程序类头文件
#include <QApplication>

/**
 * 程序入口
 * @brief qMain
 * @param argc 命令行参数个数
 * @param argv 命令行参数数组
 * @return 
 */
int main(int argc, char *argv[])
{
    //实例化出一个应用程序对象a,该对象有且只有一个
    QApplication a(argc, argv);
    
    //窗口类(QWidget的子类)
    Widget w;
    
    //显示窗口
    w.show();
    
    /*
     * 进入消息循环机制(阻塞),关闭窗口时退出
     */
    return a.exec();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT //用于支持信号和槽的宏

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
};
#endif // WIDGET_H

命名规范

java

常用快捷键

  • 查询:ctrl + f
  • 注释:ctrl + /
  • 帮助:选中 + F1
  • 缩放:ctrl + 滚轮
  • 行移动:ctrl + shift + 上|下
  • 编码格式化:ctrl + i
  • 源文件与头文件切换:f4

QPushButton

widget.cpp,须引入头文件#include <QPushButton>

// widget.cpp

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

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //设置窗口名称
    setWindowTitle("测试QPushButton");
    
    //设置当前窗口大小:用户可缩放
    //resize(600, 400);

    //设置当前窗口大小:用户不可缩放
    setFixedSize(600, 400);

    /**
     * 创建一个按钮(对象)
     */
    /*方式一*/
    QPushButton* btn1 = new QPushButton;
    //设置父窗口
    btn1->setParent(this);
    //设置内容
    btn1->setText("按钮1");
    /*方式二*/
    QPushButton* btn2 = new QPushButton("btn2", this);
    
    //移动按钮
    btn2->move(100, 0);
}

Widget::~Widget()
{
}

对象树

无需关心对象的释放

Qt中对于new出来的对象,如果是QObject及其衍生类,都无需关心其内存的释放

QObject是以对象树的形式组织起来的

当创建一个QObject对象时,QObject的构造函数会接收一个QObject指针作为参数,名为parent,也就是对象指针

所以,在创建QObject对象时,可以提供一个它的父对象,而我们即将创建的这个QObject,就会被加入到父对象的children()列表中

当父对象析构的时候,这个列表中的所有对象也都会被调用析构

这里的父子对象指的是父子窗口的含义

当一个QObject对象在堆上创建时,Qt会同时为其创建一个对象树,要注意的是,对象树中的对象的顺序时没有定义的,所以,销毁这些对象的顺序也是未定义的

任何对象树种的QObject对象delete的时候,如果这个对象有parent,那么会同时将这个对象从它的parentchildren中移除;

同时,如果它自身的children()中的对象也会被delete

如果QObject在栈上被创建,Qt也一样会这样操作

构造与析构顺序

构造:QObject最先开始

析构:QObject最后执行
在这里插入图片描述

注意:对于打印的顺序,会先打印父对象中的析构函数中,因为这时候并没有进行释放操作,而只是追踪操作,即判断是否还有下级子对象,只有在确定没有子对象时才开始由子到父的顺序进行对象的释放

信号和槽

connect

语法

connect(信号发送者, 发送的信号, 信号接收者, 处理的槽函数)

案例:点击按钮关闭窗口
#include "widget.h"
#include <QPushButton>
#include "mypushbutton.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    MyPushButton* btn = new MyPushButton;
    btn->setText("点我关闭");
    btn->setParent(this);

    connect(btn, &QPushButton::clicked, this, &QWidget::close);
}

Widget::~Widget()
{
    qDebug() << "Widget 析构函数调用";
}

自定义信号和槽

书写规范
信号
  • 写在signals下,只需要声明,不需要实现
  • 返回值为void,可以有参数,且可以重载
  • 写在public slots
    5.0开始可以写成全局函数 或public下成员函数 或直接用lambda表达式
  • 需要声明,同时也要有实现
  • 返回值一般用void,可以有参数,也可以重载

案例

connect(班长, 起立, 学生, 老师好)

班长类
  • classmonitor.h
    #ifndef CLASSMONITOR_H
    #define CLASSMONITOR_H
    
    #include <QObject>
    
    class ClassMonitor : public QObject
    {
        Q_OBJECT
    public:
        explicit ClassMonitor(QObject *parent = nullptr);
    
    signals:
        void standUp();
    };
    
    #endif // CLASSMONITOR_H
    
    
  • classmonitor.cpp
    #include "classmonitor.h"
    
    ClassMonitor::ClassMonitor(QObject *parent)
        : QObject{parent}
    {
    
    }
    
学生类
  • student.h
    #ifndef STUDENT_H
    #define STUDENT_H
    
    #include <QObject>
    
    class Student : public QObject
    {
        Q_OBJECT
    public:
        explicit Student(QObject *parent = nullptr);
    
    signals:
    
    //槽函数除了写在这的public slots中,也可以写在全局函数或者public下
    public slots:
        void greeting();//需要在源文件中实现
    };
    
    #endif // STUDENT_H
    
    
  • student.cpp
    #include "student.h"
    #include <QDebug>
    
    Student::Student(QObject *parent)
        : QObject{parent}
    {
    
    }
    
    void Student::greeting() {
        qDebug() << "老师好 ~~ ";
    }
    
Widget
  • widget.h
    #ifndef WIDGET_H
    #define WIDGET_H
    #include "classmonitor.h"
    #include "student.h"
    #include <QWidget>
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
        ClassMonitor * monitor;
        Student * student;
    
        void classBegin();
    };
    #endif // WIDGET_H
    
  • widget.cpp
    #include "widget.h"
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
    {
        //传入this,将其放入对象树中
        this->monitor = new ClassMonitor(this);
        this->student = new Student(this);
    
        //创建信号槽:班长喊起立,学生喊老师好
        connect(monitor, &ClassMonitor::standUp, student, &Student::greeting);
        //上课:触发信号槽
        classBegin();
    }
    
    Widget::~Widget()
    {
    }
    
    /**
     * 上课:触发班长的《起立》信号
     * 班长:实例对象要和connect的一致
     * @brief Widget::classBegin
     */
    void Widget::classBegin() {
        emit this->monitor->standUp();
    }
    
    
    
    

重载

班长类
  • classmonitor.h
    //...
    signals:
        void standUp();
        void standUp(QString surname);
    };
    //...
    
学生类
  • student.h
    //...
    public slots:
        void greeting();//需要在源文件中实现
        void greeting(QString surname);
    };
    //...
    
  • student.cpp
    //...
    void Student::greeting(QString surname) {
        /*
         * QString是带引号的,如果要去除,需要将其转为char *
         * 1、surname.toUtf8() 将QString转为QByteArray
         * 2、.data() 将QByteArray转为char *
         */
        qDebug() << surname.toUtf8().data() << "老师好 ~~ ";
    }
    //...
    
Widget
  • widget.h
#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //传入this,将其放入对象树中
    this->monitor = new ClassMonitor(this);
    this->student = new Student(this);
    
    /*
     * 此时需要通过函数指针来指定函数地址
     * 返回值(作用域::*函数指针名)(参数类型列表) = &作用域::函数名
     */
    void(ClassMonitor::*monitorSignal)(QString) = &ClassMonitor::standUp;
    void(Student::*studentSignal)(QString) = &Student::greeting;
    connect(monitor, monitorSignal, student, studentSignal);
    classBegin();
}

void Widget::classBegin() {
    emit this->monitor->standUp("黄");
}

信号的连接与断开

连接

connect(btn, &QPushButton::clicked, monitor, monitorSingal) //此时无需再调用emit去触发信号

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

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    this->monitor = new ClassMonitor(this);
    this->student = new Student(this);

    //构建无参信号和槽
    void(ClassMonitor::*monitorSignal)() = &ClassMonitor::standUp;
    void(Student::*studentSignal)() = &Student::greeting;
    connect(monitor, monitorSignal, student, studentSignal);

    QPushButton* btn = new QPushButton("上课", this);
    connect(btn, &QPushButton::clicked, monitor, monitorSignal);
    resize(400, 600);

}

Widget::~Widget()
{
}
断开
disconnect(btn, &QPushButton::clicked, monitor, monitorSignal);

连接的对应关系

  • 一个信号可以连接多个槽函数
  • 多个信号可以连接同个槽函数

参数的对应关系

  • 信号函数和槽函数的参数类型必须能对应
  • 信号函数的参数个数可以有多余,但槽函数不可以
    //可以
    void(ClassMonitor::*monitorSignal)(Qstring) = &ClassMonitor::standUp;
    void(Student::*studentSignal)() = &Student::greeting;
    connect(monitor, monitorSignal, student, studentSignal);
    
    //不可以
    void(ClassMonitor::*monitorSignal)() = &ClassMonitor::standUp;
    void(Student::*studentSignal)(Qstring) = &Student::greeting;
    connect(monitor, monitorSignal, student, studentSignal);
    

Qt5之前版本的写法

不推荐使用

优势:参数直观

劣势:不会对参数类型做匹配

无参
connect(monitor, SIGNAL(standUp()), student, SLOT(greeting()));
emit monitor->standUp();
有参
connect(monitor, SIGNAL(standUp(QString)), student, SLOT(greeting(QString)));
emit monitor->standUp("张");

lambda表达式

c++ 11加入的匿名函数对象,用于简化编程

[capture](parameters)mutable -> return-type
{
	//...
}

[capture]

[]标识一个lambda的开始,capture表示函数对象参数

函数对象参数时传递给编译器自动生成的函数对象类的构造函数的

函数对象参数只能使用:当前lambda所在范围内的可见的局部变量(包括lambda所在类的this

函数对象参数的几种形式

  1. 表示没有使用任何函数对象参数
  2. =
    表示lambda函数体内可以使用其所在作用域范围内所有可见的局部变量(包括所在类的this),并且是值传递(相当于编译器自动按值传递的方式传递了所有局部变量)
    QPushButton* btn = new QPushButton("上课", this);
    [=]() {
        btn->setText("上课了"); //如果为空,这里是无法用到btn变量的
    }(); //用()来进行调用
    
  3. &
    =,只不过是用的引用传递的方式(相当于编译器自动按引用传递的方式传递了所有局部变量)
  4. this
    表示函数体内可以使用lambda所在类中的成员变量
  5. a
    表示将指定的局部变量a(其他变量将无法使用)按值传递的方式进行传递,这种方式函数体内不能直接去修改a的拷贝,因为默认时const的,需要先改造a的拷贝构造
  6. &a
    a以引用的方式进行传递
  7. =, &a, &b
    表示ab用引用传递,其余用值传递
  8. &, a, b
    表示ab用值传递,其余用引用传递
使用&可能引起的问题
//点击按钮,改变按钮的文本值
QPushButton *btn = new QPushButton("aaa", this);

/*
 * 当使用信号和槽的使用,其内部会会进入锁状态,即只读,
 * 所以如果使用&引用传递的方式的话可能会出问题
 * 所以一般使用=值传递的方式即可
 */
connect(btn, &QPushButton, this, [&](){
	btn->setText("bbb"); //可能会引起报错
})

mutable

该关键字可以省略

按值传递函数对象参数时,加上mutable关键字之后,可以修改按值传递进来的拷贝(注意是修改拷贝,而非值本身)

/*
 * 操作:先点击a按钮,再点击b按钮
 * 输出:
 *   10
 *   btn1:  20 //因为使用了mutable关键字,所以可以对m进行修改
 *   btn2:  10 //因为是值传递,所以改的其实是拷贝,原值未被修改
 */
QPushButton* btn1 = new QPushButton("a", this);
QPushButton* btn2 = new QPushButton("b", this);
btn2->move(100, 0);
int m = 10;
connect(btn1, &QPushButton::clicked, this, [m]()mutable {
    m = 20;
    qDebug() << "btn1: " << m;
});
connect(btn2, &QPushButton::clicked, this, [=](){
    qDebug() << "btn2: " << m;
});
qDebug() << m;

返回值

-> 返回值类型,如果返回值为void则可以省略

this的省略

对于信号和槽,如果槽函数使用的是lambda的形式,那么如果信号接收者是this的话,可以省略

QPushButton* btn = new QPushButton("点击关闭窗口", this);
connect(btn, &QPushButton::clicked, [=](){
    this->close();
});

QMainWindow

介绍

提供主窗口程序的类,包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个锚接部件(dock widgets)、一个状态栏(status bar)以及一个中心部件(center widget),是许多应用程序的基础(如文本编辑器、图片编辑器等)

在这里插入图片描述

菜单栏

一个窗口中最多只能有一个

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

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(600, 400);

    //创建一个菜单栏对象
    QMenuBar *bar = menuBar();

    //往菜单栏上添加菜单
    QMenu *fileMenu = bar->addMenu("文件");
    QMenu *editMenu = bar->addMenu("编辑");
    QMenu *viewMenu = bar->addMenu("View");

    //往菜单上添加菜单项
    QAction * newProject = fileMenu->addAction("新建项目");
    //添加一个分割线
    fileMenu->addSeparator();
    QAction * newFile = fileMenu->addAction("新建文件");

    //将菜单栏放入窗口中
    setMenuBar(bar);
}

MainWindow::~MainWindow()
{
}

工具栏

一个窗口中允许有多个

#include "mainwindow.h"
#include <QMenuBar>
#include <QToolBar>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(600, 400);

    //创建一个菜单栏对象
    QMenuBar *bar = menuBar();

    //往菜单栏上添加菜单
    QMenu *fileMenu = bar->addMenu("文件");
    QMenu *editMenu = bar->addMenu("编辑");
    QMenu *viewMenu = bar->addMenu("View");

    //往菜单上添加菜单项
    QAction * newProject = fileMenu->addAction("新建项目");
    //添加一个分割线
    fileMenu->addSeparator();
    QAction * newFile = fileMenu->addAction("新建文件");

    //将菜单栏放入窗口中
    setMenuBar(bar);

    //创建一个工具栏对象
    QToolBar *toolBar = new QToolBar(this);

    //设置可停靠的区域(默认上下左右都可以)
    toolBar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);

    //设置浮动性(默认可浮动)
    toolBar->setFloatable(false);

    //往工具栏中放置部件
    toolBar->addAction(newProject);
    //添加分割线
    toolBar->addSeparator();
    toolBar->addAction(newFile);

    //将工具栏放入窗口中(默认放窗口上方)
    addToolBar(Qt::LeftToolBarArea, toolBar); //放到窗口的左侧
}

状态栏

一个窗口最多有一个

#include <QStatusBar>
#include <QLabel>

//...................................

//创建一个状态栏对象
QStatusBar *staBar = statusBar();

//往状态栏中添加部件
QLabel *label1 = new QLabel("label1", this); //存文本标签部件
QLabel *label2 = new QLabel("label2", this);
staBar->addWidget(label1); //从左侧开始添加
staBar->addPermanentWidget(label2); //从右侧开始添加

//将菜单栏放入窗口中
setStatusBar(staBar);

锚接部件与中心部件

锚接部件也叫浮动窗口,可以有多个

中心部件可以是各种形式的部件,如文本编辑部件,最多只能有一个

#include <QDockWidget>
#include <QTextEdit>

//........................................

//创建一个锚接部件对象
QDockWidget *dock = new QDockWidget("dock1", this);

//设置停靠位置
dock->setAllowedAreas(Qt::TopDockWidgetArea|Qt::RightDockWidgetArea);

//创建和一个文本编辑类的中心部件(中心部件只能有一个)
QTextEdit *edit = new QTextEdit(this);

//将中心部件放入窗口中
setCentralWidget(edit);

//将锚接部件放入窗口中(停靠位置依赖于中心部件)
addDockWidget(Qt::RightDockWidgetArea, dock);

对话框

自定义对话框

自定义对话框可以分为模态对话框和非模态对话框,区别在于对话框期间是否允许对其他的窗口进行操作,其中模态对话框禁止操作

模态对话框

点击新建文件弹出对话框

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDialog>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->actionfile->setIcon(QIcon(":/image/aaa.png"));
    connect(ui->actionfile, &QAction::triggered, this, [=](){
        QDialog dialog(this); //创建模态对话框对象
        dialog.resize(200, 300); //设置对话框窗口大小
        dialog.exec(); //弹出对话框(该方法会阻塞,所以虽然是栈上创建的对话框对象,创建出来的对话框也不会立马消失)
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}
非模态对话框
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDialog>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->actionfile->setIcon(QIcon(":/image/aaa.png"));
    
    connect(ui->actionfile, &QAction::triggered, this, [=](){
        //创建非模态对话框对象,不能再创建在栈上,因为lambda直接结束对象消失,会导致对话框窗口一闪而逝
        QDialog* dialog = new QDialog(this);

        //设置对话框窗口大小
        dialog->resize(200, 300);

        /*
         * 由于是创建在堆上,所以对象只有大窗口关闭才会删除,只是关闭对话框不会被删除
         * 所以每次点击都会创建一个对象
         * 通过设置Qt::WA_DeleteOnClose属性,可以使得每次关闭对话框时删除其中的对象
         */
        dialog->setAttribute(Qt::WA_DeleteOnClose);

        //通过show方法弹出的对话框为非模态对话框
        dialog->show();
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

系统标准对话框

QColorDialog

选择颜色

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QColorDialog>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //弹出拾色器并自定义默认选中的颜色
    QColor color = QColorDialog().getColor(Qt::red);
    qDebug() << color.rgb();
    qDebug() << color.red() << color.green() << color.blue();
}

MainWindow::~MainWindow()
{
    delete ui;
}
QFileDialog

选择文件或目录

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QFileDialog>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //弹出打开文件窗口并设置默认路径、以及指定文件格式
    //返回:文件全路径
    QString fileName = QFileDialog::getOpenFileName(this, "打开文件", ".", "(*.cpp)");
    qDebug() << fileName;
}

MainWindow::~MainWindow()
{
    delete ui;
}
QFontDialog

选择字体

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QFontDialog>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    bool flag;
    
    //弹出字体选择框并指定默认字体和字号大小等
    QFont font = QFontDialog::getFont(&flag, QFont("仿宋", 16));
    qDebug() << font.family().toUtf8().data(); //字体
    qDebug() << font.pointSize(); //字号
    qDebug() << font.bold(); //是否有加粗
}

MainWindow::~MainWindow()
{
    delete ui;
}
QInputDialog

允许用户输入一个值,并将其值返回

QMessageBox

模态对话框,常用语显示信息、询问问题等

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QMessageBox::critical(this, "错误", "错误对话框");
    QMessageBox::warning(this, "警告", "警告对话框");
    QMessageBox::information(this, "信息", "信息对话框");
    /*
     * 参数4:关联的按钮,默认YES和NO,可自定义
     * 参数5:默认选中的按钮
     * 返回值:选中的按钮放
     */
    QMessageBox::StandardButton res = QMessageBox::question(this, "询问", "询问对话框", QMessageBox::Save|QMessageBox::Cancel, QMessageBox::Save);
    if(res == QMessageBox::Save) {
        qDebug("save");
    } else {
        qDebug("Cancel");
    }
}

MainWindow::~MainWindow()
{
    delete ui;
}
QPageSetupDialog

为打印机提供纸张相关的选项

QPrintDialog

打印机配置

QPrintPreviewDialog

打印预览

QProgressDialog

显示操作过程

widget.ui

布局

Verticl Layout

垂直布局

Horizontl Layout

水平布局

Grid Layout

网格布局,即自动几行几列的排好

按钮

Push Button

普通按钮,可以通过icon属性设置图标

Tool Button

工具按钮,一般用来用作图标按钮

  • QToolButton/toolButtonStyle
    如果想同时显示图标和文字,或者其他显示形式,可以修改此属性
  • QToolButton/autoRaise
    鼠标退出消散按钮(透明)外框,放入则显示 的特效
Radio Button

单选按钮

  • 分组
    默认所有单选项为一组,可通过容器Group Box进行分组
  • 默认选中
    //先设置好单选项的objectname
    ui->btn_man->setChecked(true);
    
  • 选中时输出信息
    connect(ui->btn_woman, &QRasioButton::clicked, [=](){
    	qDebut() << "选中了woman";
    });
    
Check Box

多选按钮

  • 分组
    一样可以用Group Box进行分组
  • 监听选中事件
    默认未开启半选中,通过QCheckBox/tristate属性来开启
    connect(ui->check_item1, &QCheckBox::stateChanged, [=](int state){
    	if (state == 0) qDebug() << "未选中";
    	else if(state == 1) qDebug() << "半选中";
    	else if(state == 2) qDebug() << "选中";
    })
    

Item Widgets

List Widget

以列表的形式来展示内容

#include "widget.h"
#include "ui_widget.h"
#include <QListWidgetItem>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    /*
     * 一次添加一个列表项:这种方式可以设置对齐方式(默认左对齐)
     */
    QListWidgetItem *item1 = new QListWidgetItem("床前明月光");
    //居中对齐
    item1->setTextAlignment(Qt::AlignCenter);
    QListWidgetItem *item2 = new QListWidgetItem("疑是地上霜");
    //放入ListWidget中
    ui->listWidget->addItem(item1);
    ui->listWidget->addItem(item2);

    /*
     * 一次添加多个列表项:这种方式不能设置对齐方式
     */
    //可以看做是List<String>
    QStringList list;
    list << "举头望明月" << "低头思故乡";
    ui->listWidget->addItems(list);

}

Widget::~Widget()
{
    delete ui;
}

在这里插入图片描述

Tree Widget

用树的形式来展示内

#include "widget.h"
#include "ui_widget.h"
#include <QListWidgetItem>
#include <QList>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //设置头部
    ui->treeWidget->setHeaderLabels(QStringList() << "Hero" << "Hero briefing");

    //设置根结点
    QTreeWidgetItem *powerHeros = new QTreeWidgetItem(QStringList() << "武力" << "法力");
    QTreeWidgetItem *iqHeros = new QTreeWidgetItem(QStringList() << "智力");
    QTreeWidgetItem *speedHeros = new QTreeWidgetItem(QStringList() << "速度");
    //设置二级结点
    QTreeWidgetItem *p1 = new QTreeWidgetItem(QStringList() << "张三丰" << "武力100;法力0");
    QTreeWidgetItem *p2 = new QTreeWidgetItem(QStringList() << "法海" << "武力20;法力90");
    QTreeWidgetItem *i1 = new QTreeWidgetItem(QStringList() << "诸葛亮" << "智力100");
    QTreeWidgetItem *i2 = new QTreeWidgetItem(QStringList() << "孙膑" << "智力95");
    QTreeWidgetItem *s1 = new QTreeWidgetItem(QStringList() << "盗跖" << "速度90");
    QTreeWidgetItem *s2 = new QTreeWidgetItem(QStringList() << "金鹏鸟" << "速度100");
    //将二级结点放入根结点
    powerHeros->addChild(p1);
    powerHeros->addChild(p2);
    iqHeros->addChild(i1);
    iqHeros->addChild(i2);
    speedHeros->addChild(s1);
    speedHeros->addChild(s2);
    //将结点放入树中
    ui->treeWidget->addTopLevelItem(powerHeros);
    ui->treeWidget->addTopLevelItem(iqHeros);
    ui->treeWidget->addTopLevelItem(speedHeros);

}

Widget::~Widget()
{
    delete ui;
}

在这里插入图片描述

Table Widget

用表格的形式来展示内

#include "widget.h"
#include "ui_widget.h"
#include <QRandomGenerator>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //设置列数
    ui->tableWidget->setColumnCount(3);

    //设置头
    ui->tableWidget->setHorizontalHeaderLabels(QStringList() << "姓名" << "性别" << "年龄");

    //设置行数
    ui->tableWidget->setRowCount(5);

    //设置内容
    QList<QString> names;
    names << "张三" << "李四" << "王五" << "赵六" << "周七";
    QStringList genders;
    genders << "男" << "女" << "男" << "男" << "女";
    for(int i = 0; i < 5; ++i) {
        int col = 0;
        ui->tableWidget->setItem(i, col++, new QTableWidgetItem(names[i]));
        ui->tableWidget->setItem(i, col++, new QTableWidgetItem(genders.at(i)));
        ui->tableWidget->setItem(i, col, new QTableWidgetItem(QString::number(QRandomGenerator::global()->bounded(20, 25))));
    }

}

Widget::~Widget()
{
    delete ui;
}

在这里插入图片描述

容器

Group Box

可以给单选按钮、多选按钮进行分组

Scroll Area

滚动条

Tool Box

类似QQ好友分组,通过(对象和类区域中)右键插入页添加分组项

Tab Widget

标签页

Stacked Widget

ui设计界面类似轮播图,内容可以是任意的组件,不过实际使用界面没有轮播按钮,所以一般要配合按钮使用,即点击按钮进入对应的内容页,所以可以理解为是个可纵可横的标签页

#include "widget.h"
#include "ui_widget.h"
#include <QRandomGenerator>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //设置默认选项
    ui->stackedWidget->setCurrentIndex(0);

    //绑定点击事件
    connect(ui->btn_tree, &QPushButton::clicked, [=](){
        ui->stackedWidget->setCurrentIndex(0);
    });
    connect(ui->btn_table, &QPushButton::clicked, [=](){
        ui->stackedWidget->setCurrentIndex(1);
    });
    connect(ui->btn_list, &QPushButton::clicked, [=](){
        ui->stackedWidget->setCurrentIndex(2);
    });

}

Widget::~Widget()
{
    delete ui;
}
Frame
Widget

可以对放置在里面的部件进行布局

MDI Area
Dock Widget

锚接部件

QAxWidget

Input Widgets

Combo Box

下拉框

ui->comboBox->addItem("选项1");
ui->comboBox->addItem("选项2");
ui->comboBox->addItem("选项3");
//默认选中项
ui->comboBox->setCurrentIndex(0); //或setCurrentText("选项1")
Font Combo Box

字体下拉框

Line Edit

类似<input>标签

Text Edit

类似<textarea>标签,其中文字可设置粗细、斜体、大小等样式

Plain Text Edit

同上,只不过没有样式

Spin Box

类似购物车商品数的加减

Double Spin Box

和上面的相比,数值为Double类型

Time Edit

时间选择器

Date Edit

日期选择器

Date/Time Edit

时间日期选择器

Dial
Horizontal Scroll Bar & Horizontal Slider

横向滚动条

Vertical Scroll Bar & Vertical Slider

纵向滚动条

Key Sequence Edit

Display Widgets

Label

标签,可以显示文本或图片(包括gif动图)

//放图片
ui->label_img->setPixmap(QPixmap(":/image/butterfly.png"));

//放动图
QMovie *movie  = new QMovie(":/image/demo.gif");
ui->label_movie->setMovie(movie);
movie->start();
Text Browser
Graphics View
Calendar Widget
LCD Number
Progress Bar
Horizontal Line
Vertical Line

控件的封装:SpinBox和滚动条的联动

项目基类选择空部件的QWidget即可

一、新建Qt文件

  1. 新建文件(选Qt
  2. 设计师界面类(除了.h.cpp还包括了ui文件)
  3. 选择界面模板(Widget

二、界面设计

四、联动逻辑编写

  • smallwidget.h
    #ifndef SMALLWIDGET_H
    #define SMALLWIDGET_H
    
    #include <QWidget>
    
    namespace Ui {
    class SmallWidget;
    }
    
    class SmallWidget : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit SmallWidget(QWidget *parent = nullptr);
        ~SmallWidget();
    
    private:
        Ui::SmallWidget *ui;
    };
    
    #endif // SMALLWIDGET_H
    
    
  • smallwidget.cpp
    #include "smallwidget.h"
    #include "ui_smallwidget.h"
    #include <QSpinBox>
    #include <QSlider>
    
    SmallWidget::SmallWidget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::SmallWidget)
    {
        ui->setupUi(this);
    
        //数值改变,进度条随之改变
        void(QSpinBox::*spinBoxP)(int) = &QSpinBox::valueChanged;
        connect(ui->spinBox, spinBoxP, [=](int spinBoxVal) {
            ui->horizontalSlider->setValue(spinBoxVal);
        });
    
        //进度条改变,数值随之改变
        connect(ui->horizontalSlider, &QSlider::valueChanged, [=](int sliderVal) {
            ui->spinBox->setValue(sliderVal);
        });
        
    }
    
    SmallWidget::~SmallWidget()
    {
        delete ui;
    }
    
    
    
  • smallwidget.ui
    在这里插入图片描述

三、引入设计好的界面

  1. 选择一个Widget容器
  2. 右键提升为
    1. 基类:QWidget
    2. 提升的类名称:新建文件的类名
    3. 全局包含:选中
    4. 添加
    5. 提升

事件

常用的鼠标事件

  1. mylabel.h
    #ifndef MYLABEL_H
    #define MYLABEL_H
    
    #include <QLabel>
    
    class MyLabel : public QLabel
    {
        Q_OBJECT
    public:
        explicit MyLabel(QWidget *parent = nullptr);
    
        //鼠标进入MyLabel
        void enterEvent(QEnterEvent *event);
    
        //鼠标离开MyLabel
        void leaveEvent(QEvent *event);
    
        //鼠标在MyLabel中(按下后)移动
        void mouseMoveEvent(QMouseEvent *event);
    
        //鼠标在MyLabel中压下
        void mousePressEvent(QMouseEvent *event);
    
        //鼠标在MyLable中放开
        void mouseReleaseEvent(QMouseEvent *event);
    
        //鼠标在MyLabel中双击
        void mouseDoubleClickEvent(QMouseEvent *event);
    
    signals:
    
    };
    
    #endif // MYLABEL_H
    
    
  2. mylabel.cpp
    #include "mylabel.h"
    #include <QDebug>
    #include <QMouseEvent>
    MyLabel::MyLabel(QWidget *parent)
        : QLabel{parent}
    {
    
    }
    
    void MyLabel::enterEvent(QEnterEvent *event){
        qDebug() << "鼠标进入MyLabel";
    }
    
    void MyLabel::leaveEvent(QEvent *event){
        qDebug() << "鼠标离开MyLabel";
    }
    
    void MyLabel::mouseMoveEvent(QMouseEvent *event){
    	//鼠标移动中获取按下的键需要用buttons(),因为移动过程中可能同时按下了多个键
    	if (event->buttons() & Qt::LeftButton) {
    		qDebug() << "移动过程中按下了鼠标左键";
    	}
        qDebug() << "鼠标在MyLabel中(按下后)移动";
    }
    
    void MyLabel::mousePressEvent(QMouseEvent *event){
        Qt::MouseButton btn = event->button();
        if (btn == Qt::LeftButton) {
            qDebug() << "按下了鼠标左键";
        }
        //x,  y:在控件MyLabel的位置
        qDebug() << QString("鼠标在MyLabel中压下:x=%1, y=%2").arg(event->x()).arg(event->y());
    }
    
    void MyLabel::mouseReleaseEvent(QMouseEvent *event){
        qDebug() << "鼠标在MyLable中放开";
    }
    
    void MyLabel::mouseDoubleClickEvent(QMouseEvent *event) {
        qDebug() << "鼠标在MyLable中双击";
    }
    
  3. ui中的Label提升为MyLabel

定时器

实现方式一:定时器事件
  • ui
    在这里插入图片描述

  • widget.h

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class Widget; }
    QT_END_NAMESPACE
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
        //定时器事件
        void timerEvent(QTimerEvent *event);
    private:
        Ui::Widget *ui;
    
        //每1秒定时器标识
        int timer1;
    
        //每2秒定时器标识
        int timer2;
    };
    #endif // WIDGET_H
    
    
  • widget.cpp

    #include "widget.h"
    #include "ui_widget.h"
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        //开启定时器(单位毫秒)
        this->timer1 = startTimer(1000);
        this->timer2 = startTimer(2000);
    
    
    }
    
    Widget::~Widget()
    {
        //删除定时器
        killTimer(this->timer1);
        killTimer(this->timer2);
        delete ui;
    }
    
    void Widget::timerEvent(QTimerEvent *event) {
        int timerId = event->timerId();
        static int timer1num = 1;
        static int timer2num = 1;
        if (timerId == this->timer1) {
            ui->label_per1->setText(QString::number(timer1num++));
        } else if (timerId == this->timer2) {
            ui->label_per2->setText(QString::number(timer2num++));
        }
    }
    
    
实现方式而:定时器类
#include "widget.h"
#include "ui_widget.h"
#include <QTimer>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //创建定时器类的对象
    QTimer *timer1 = new QTimer(this);
    QTimer *timer2 = new QTimer(this);

    //启动并设置interval,当达到事件时会抛出timeout信号
    timer1->start(1000);
    timer2->start(2000);

    //绑定timeout信号
    connect(timer1, &QTimer::timeout, [=](){
        static int num = 1;
        ui->label_per1->setText(QString::number(num++));
    });
    connect(timer2, &QTimer::timeout, [=](){
        static int num = 1;
        ui->label_per2->setText(QString::number(num++));
    });

}

事件的传递过程

  1. 事件被触发
  2. 事件过滤器
    • 作用:过滤事件
    • 步骤
      1. 给控件安装过滤器
      2. 重写过滤器事件
        bool eventFilter(Object* obj, QEvent *e);
  3. 事件分发器
    /*
     * 作用:可以对事件进行拦截处理
     * 返回值:是否继续往下分发事件,true表示不继续
     */
    bool event(QEvent *event);
    
  4. 事件
    如鼠标点击、定时器事件、自定义事件等
事件过滤器
  • widget.h
    class Widget: ...
    public:
    	bool eventFilter(QObject *, QEvent *);
    
  • widget.cpp
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
    	//给控件安装过滤器
    	ui->mylabel->installEventFilter(this);
    }
    
    /*
     * obj: 空间对象,用于区分控件
     * e: 事件对象
     */
    bool Widget::eventFilter(QObject *obj, QEvent *e) {
    	//拦截mylabel中的事件
    	if(obj == ui->mylabel) {
    		//拦截其中的鼠标按下事件
    		if(e->type() == QEvent::MouseButtonPress) {
    			//...
    			return true;
    		}
    	}
    	//其他对象的时间交给父类处理
    	return QWidget::eventFilter(obj, e);
    }
    
事件分发器
bool MyLabel::event(QEvent *e) {
	//拦截鼠标按下事件
	if(e->type() == QEvent::MouseButtonPress) {
		QMouseEvent *ev = static_cast<QMouseEvent *>(e);
		qDebug() << ev->x() << ev->y();
		return true; //不在继续往下分发
	}
	//其他事件交给父类处理
	return QLabel::event(e);
}

绘图

会自动触发事件

绘图事件

在这里插入图片描述

  • widget.h
    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class Widget; }
    QT_END_NAMESPACE
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
        void paintEvent(QPaintEvent *);
    
    private:
        Ui::Widget *ui;
        int posX;
    };
    #endif // WIDGET_H
    
  • widget.cpp
    #include "widget.h"
    #include "ui_widget.h"
    #include <QPainter>
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
    	this->posX = 0;
    	connect(ui->btn, &QPushButton::clicked, [=](){
    		posX += 20;
    
    		if (posX > this->width()) {
    			posX = 0; //如果超出窗口宽度,置0
    		}
    		//手动调用绘图事件
    		//repaint(); 效率比update低
    		update();
    	});
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    void Widget::paintEvent(QPaintEvent *)
    {
        /*
         * 创建一个绘画者对象
         * 构造器参数表示绘画设备,即在哪里画画
         * this表示在当前窗口中画
         */
        QPainter painter(this);
    
        /*
         * 设置画笔
         * 用于自定义画笔风格,可以不设置
         * 构造器可以指定画笔颜色
         */
        QPen pen(QColor("#f99"));
        //设置画笔宽度(默认1)
        pen.setWidth(2);
        //设置画笔风格
        pen.setStyle(Qt::DotLine);
        //添加画笔
        painter.setPen(pen);
    
        /*
         * 设置画刷
         * 用于填充封闭形状,可不设
         * 构造器可以指定画刷颜色
         */
        QBrush brush(Qt::cyan);
        //设置画刷风格
        brush.setStyle(Qt::Dense1Pattern);
        //添加画刷
        painter.setBrush(brush);
    
        //画线
        painter.drawLine(QPoint(0, 0), QPoint(200, 200));
    
        //画(椭)圆(圆心,max x半径,max y半径)
        painter.drawEllipse(QPoint(100, 100), 100, 50);
    
        /*
         * 画矩形
         * QRect(左上角坐标, 宽高)
         */
        painter.drawRect(QRect(QPoint(20, 20), QSize(50, 50)));
    
        /*
         * 写字(在指定的区域内)
         */
        painter.drawText(QRect(100, 10, 150, 20), "hello qt6");
    
    	//画资源图片(绘画起点|指定区域,资源图片位置)
    	painter.drawPixmap(this->posX, 0, QPixmap(":/image/luffy.png"));
    }
    
  • 手动触发绘图事件

点击移动按钮,移动图片位置

高级设置
抗锯齿

在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);

    QPen pen(QColor("#f99"));
    painter.setPen(pen);

    QBrush brush(Qt::cyan);
    painter.setBrush(brush);

    painter.drawEllipse(QPoint(100, 100), 50, 50);

    //设置抗锯齿能力(会降低成图效率)
    painter.setRenderHint(QPainter::Antialiasing);

    painter.drawEllipse(QPoint(300, 100), 50, 50);

}
绘画起点
//画一个矩形
painter.drawRect(QRect(20, 20, 50, 50));

//移动画家的绘画起点(默认(0, 0))
painter.translate(100, 0);

//在画一个一样的矩形(由于绘画起点不一样,所以不会被覆盖)
painter.drawRect(QRect(20, 20, 50, 50));

//保存当前的画家状态(绘画起点位置)
painter.save();

//再次移动画家的绘画起点((100, 0) -> (200, 0))
painter.translate(100, 0);

//恢复画家状态((200, 0) -> (100, 0))
painter.restore();
绘图设备

指的是继承QPainterDevice的子类,Qt提供了很多这样的类,如:QPixmap, QBitmap, QImage, QPicture

QPixmap

专门为图像在屏幕上显示做了优化,往窗口绘画示例

//写入文件示例

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    /*用QPixmap做绘图设备*/

    //创建设备对象并设置设备大小
    QPixmap pix(300, 300);

    //设置背景色(默认为黑色)
    pix.fill(Qt::white);

    QPainter painter(&pix);

    painter.setPen(QPen(QColor("#f99")));
    painter.drawEllipse(QPoint(150, 150), 100, 100);

    //将绘好的图保存
    pix.save("D:/data/demo.png");
}

Widget::~Widget()
{
    delete ui;
}
QBitmap

QPixmap的一个子类,色深限定为1(即只有黑白色),可以使用QPimap.isQBitmap()函数来确定一个QPixmap是不是QBixmap

QImage

专门为图像的像素级访问做了优化

//写入文件示例

//创建设备对象并设置设备大小以及色位类型
QImage img(300, 300, QImage::Format_RGB32);

//设置背景色(默认为黑色)
img.fill(Qt::white);

QPainter painter(&img);

painter.setPen(QPen(QColor("#f99")));
painter.drawEllipse(QPoint(150, 150), 100, 100);

//将绘好的图保存
img.save("D:/data/demo.png");
//往窗口绘图 & 修改像素点 示例

void Widget::paintEvent(QPaintEvent *) {
	QImage img;
	img.load(":/image/demo.png");
	QPinter painter(this);
	//修改像素点
	for(int i = 100; i < 150; ++i) {
		for(int j = 100; j < 150; ++j) {
			QRgb p = qRgb(255, 0, 0);
			img.setPixel(i, j, p); //将坐标为(i, j)的像素改为p
		}
	}
	painter.drawImage(0, 0, img);
}
QPicture

可以记录和重现QPainter的各条命令

//记录命令

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QPicture>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    QPicture pic;
    QPainter painter;

    //开始往设备pic上绘图
    painter.begin(&pic);

    painter.drawEllipse(QPoint(150, 150), 100, 100);

    //结束绘图
    painter.end();

    //保存绘图命令
    pic.save("D:/data/pic");
}

Widget::~Widget()
{
    delete ui;
}
//重现命令

void Widget::paintEvent(QPaintEvent *) {
	QPicture pic;
	pic.load("D:/data/pic");
	painter.drawPicture(0, 0, pic);
}

文件操作

资源文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    //在这行代码之后写自己的代码
    ui->setupUi(this);

    //使用绝对路径方式:根据ui设计中的变量名进行图标设置
    //ui->actionfile->setIcon(QIcon("C:\\Users\\Administrator\\Desktop\\aaa.png"));

    /*
     * 使用资源路径方式
     * 1.可以在项目根路径中添加一个image目录
     * 2.创建qrc文件:文件 -> new file -> Qt -> Qt Resource File(一般起名res)
     * 3.添加前缀:即资源文件子目录,如/
     * 4.添加文件:将image中的图片选中
     * 5.build
     * 6.可以看到rec.qrc -> / -> image -> aaa.png 这样的目录结构
     * 7.冒号 + 前缀名 + 文件名
     */
    ui->actionfile->setIcon(QIcon(":/image/aaa.png"));
}

MainWindow::~MainWindow()
{
    delete ui;
}

QFile

文件读写类

在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QFile>
#include <QMessageBox>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    connect(ui->check_btn, &QPushButton::clicked, [=](){
        QString filePath = QFileDialog::getOpenFileName(this, "打开文件", "A:\\Tools\\WinRAR", "(*.txt)");
        if (!filePath.isEmpty()) {
            ui->title_line->setText(filePath);

            //创建文件对象
            QFile file(filePath);

            /*读取文件*/
            //设置打开方式为只读
            file.open(QIODevice::ReadOnly);

            //一次全部读取
            //QByteArray array = file.readAll();

            //一次读取一行
            QByteArray array;
            while(!file.atEnd()) {
                array += file.readLine();
            }

            //QByteArray到QString存在隐式转换
            ui->content_text->setText(array);

            //关闭流
            file.close();

            /*写入文件*/
            //WriteOnly为覆盖,Append为追加
            file.open(QIODevice::Append);
            file.write("abc");
            file.close();
        } else {
            QMessageBox::warning(this, "警告", "未选择文件");
        }
    });
}

Widget::~Widget()
{
    delete ui;
}

QFileInfo

文件信息类

QFileInfo info(filePath);

//后缀名
info.suffix(); //txt

//文件大小
info.size();

//文件名
info.fileName();

//文件路径
info.filePath();

//创建时间(birthTime)
info.created().toString("yyyy-MM-dd hh:mm:ss");

//最后修改时间
info.lastModified().toString("yyyy/MM/dd hh:mm:ss");
  • 19
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值