QT学习笔记

#### 一.QT目录结构

1659935313652

1.FirstDemo.pro

#-------------------------------------------------
#
# Project created by QtCreator 2022-08-08T11:15:30
#
#-------------------------------------------------

QT       += core gui    //  QT包含的模块

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets     // 大于版本4以上,包含widget模块

TARGET = FirstDemo      // 生成的exe程序名称
TEMPLATE = app          // 应用程序模板


SOURCES += main.cpp\
        widget.cpp

HEADERS  += widget.h

FORMS    += widget.ui

2.main.cpp

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

int main(int argc, char *argv[])
{
    // a应用程序对象
    QApplication a(argc, argv);
    // 窗口对象 Widget父类 -> QWidget
    Widget w;
    w.show();

    // 让应用程序对象进入消息循环,阻塞在这一行,等待消息
    return a.exec();
}

3.widget.h

#ifndef WIDGET_H    // 相当于#pragma once
#define WIDGET_H

#include <QWidget>

namespace Ui {
class Widget;
}

class Widget : public QWidget   // widget继承于QWidget窗口类
{
    Q_OBJECT    // Q_OBJECT宏,允许类中使用信号和槽的机制

public:
    explicit Widget(QWidget *parent = 0);   // 有参构造函数
    ~Widget();                              // 析构函数

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

4.widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)		// 初始化列表	
{
    ui->setupUi(this);
}

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

二.QPushButton控件

QPushButton* btn = new QPushButton(this);
// QPushButton* btn = new QPushButton("点击", this);

// 设置文本
btn->setText("点击");   

// 显示控件,可省略
btn->show();          

// 移动
btn.move(200, 100);

// 按钮控件自定义大小
btn.resize(50, 30);

// 设置窗口大小
this->resize(600, 400);

// 设置固定窗口大小
this->setFixedSize(600, 400);

// 设置窗口标题
this->setWindowTitle("例子");

三.对象树

1.当创建的对象在堆区的时候,如果其指定的父亲是QObject派生类或者其子类的派生类,可以不用管new对象以后的释放操作,该对象会放入对象树中。

2.一定程度上简化了内存回收机制,再关闭窗口时,其子类将会被自动释放。

对象树

注:调试打印

// QDebug
#include "QDebug"

qDebug() << "打印调试信息: " << message;	// 会自动换行

四.信号和槽

1.信号和槽

连接函数:connect ( )

参数: 1 信号的发送者(发送端

​ 2 发送的信号(函数地址)

​ 3 信号的接收者(接收端

​ 4 处理的槽函数(函数地址)

松散耦合:信号的发送端和接收端本身是没有关联的,他们是通过connect()连接,将两端耦合在一起。

// 信号和槽
connect(btn, &QPushButton::clicked, this, &QWidget::close);

2.自定义信号和槽

// 触发信号
emit  p->hungry();

1.自定义信号:

​ (1)写到 signals 下

​ (2)返回void,需要声明,不需要实现

​ (3)可以有参数,可以重载

2.自定义槽

​ (1)写到 public slot 下

​ (2)返回void,需要声明,也需要实现

​ (3)可以有参数,可以重载

    // 自定义信号和槽
    this->sgl = new mySignals(this);
    this->slt = new mySlot(this);
    // 绑定信号和槽
    connect(sgl, &mySignals::mySgl, slt, &mySlot::mySlt);
	// 触发
    emit this->sgl->mySgl();

3.自定义信号和槽发生重载

    // 自定义信号和槽的重载
    this->sgl = new mySignals(this);
    this->slt = new mySlot(this);

	// 函数指针 ---> 函数地址
    void (mySignals:: *mySignal_sgl)(QString) = &mySignals::mySgl;
    void (mySlot:: *mySlot_slt)(QString) = &mySlot::myslt;

	// 绑定
    connect(sgl, mySignal_sgl, slt, mySlot_slt);

注意:

QString类型输出的时候会默认加上 ” “,双引号。

解决办法:

​ **QString 转为 char ***

QString str;
// 先利用 .toUtf8() 转为 QByteArray
// 再利用 .Data() 转为 char*
str.toUtf8().data()

拓展:

1、信号是可以连接信号

2、一个信号可以连接多个槽函数

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

4、信号和槽函数,参数类型必须一一对应,信号函数的参数个数大于等于槽函数

5、断开连接:disconnect ( 参数一致 )

五.Lambda表达式

​ [ ] ( ) {

​ } ( ) ;

>[ ] 里的参数:
>
>​	**=:值传递(推荐)**
>
>​	&:引用传递
>
>( ) 重载参数:
>
>​	标识重载的 ( ) 操作符的参数

注意:

// 1.
// mutable 声明,在需要对值传递进来的拷贝进行修改时,加上mutable关键字,否则会报错
// 注意是能修改拷贝,而不是值本身。


    // 按钮1
    QPushButton* btn1 = new QPushButton("按钮1", this);
    btn1->move(240,100);
    // 按钮2
    QPushButton* btn2 = new QPushButton("按钮2", this);
    btn2->move(240,200);
    // 窗口大小
    this->resize(600, 400);

    int m = 10;
    // 绑定信号和槽
    connect(btn1, &QPushButton::clicked, this, [m]() mutable {m+=1; qDebug()<<m;});
    connect(btn2, &QPushButton::clicked, this, [=](){qDebug()<<m;});


// 2.
// lambda表达式若有返回值
int ret = [] () -> int {
  			 return 10;
		 }()

总结:

信号和槽

信号和槽注意点:

  1. 发送者和接受者都需要时Object子类 ( 槽函数是全局函数 / lambda表达式无需接受者除外 );

  2. 使用 signals 标记信号时,信号是一个函数的声明,返回 void ,不需要实现;

  3. 槽函数是普通的成员函数,会受到 public、private、protected 的影响;

  4. 使用 emit 发送信号

六.QMainWindow

1.QMenuBar、QToolBar、QLabel、QStatusBar、
{
    this->resize(800,500);
    
    // 菜单栏 QMenuBar
    QMenuBar* menubar = new QMenuBar();
    
    // 放入窗体中
    setMenuBar(menubar);
    
    // 添加菜单项
    QMenu* fileBar = menubar->addMenu("文件");
    menubar->addMenu("编辑");
    
    // 添加菜单项子项
    fileBar->addAction("新建");
    fileBar->addAction("打开");
    
    QLabel* label = new QLabel("1234567", this);
    label->move(123,123);

}

1660035042356

七.常用控件---面向百度

img

八.自定义控件

  1. Add New --> Qt --> Qt设计师界面类(.ui .cpp .h) --> 选择界面

  2. 在新ui文件中设计窗口

    1660544792675

  3. 按照新ui类型(QWidget),在主ui界面中放置一个Widget框,右击提升为,填写类名与.h名,注意大小写,添加,提升

    1660544835697

        // SPinBox change Slider
        void(QSpinBox:: *mySpinBox)(int) = &QSpinBox::valueChanged;
        connect(ui->spinBox, mySpinBox, ui->horizontalSlider, &QSlider::setValue);
    
        // Slider change SPinBox
        connect(ui->horizontalSlider, &QSlider::valueChanged, ui->spinBox, &QSpinBox::setValue);
    
  4. 举例

    1660547991026

    smallWidget.h

    public:
        int getNum();
        void setNum(int num);
    

    smallWidget.cpp

    int SamllWidget::getNum()
    {
        qDebug() << "getNum function";
        return ui->spinBox->value();
    }
    void SamllWidget::setNum(int num)
    {
        qDebug() << "setNum function";
        ui->spinBox->setValue(num);
    }
    

    widget.cpp

        // click btn_get
        connect(ui->pushButton_get, &QPushButton::clicked, this, [=](){
            int num = ui->widget->getNum();
            QString str = QString::number(num);
            qDebug() << str;
            ui->lineEdit_1->setText(str);
        });
    
        // click btn_set
        connect(ui->pushButton_set, &QPushButton::clicked, this, [=](){
            QString str = ui->lineEdit_2->text();
            ui->widget->setNum(str.toInt());
        });
    

    注意逻辑:我在自定义控件里提供了可以获取控件值的接口,然后再主界面里用 ui->widget->getNum() 就可以拿到自定义控件中的值了。

九.Qt中的鼠标事件

  1. 对鼠标事件进行处理时,通常要重新实现以下几个鼠标事件处理函数:

  2. 设置鼠标追踪

    // 鼠标追踪,默认为false,不会一进入就触发
    // true 一进入就会触发
    setMouseTracking(true);
    
    // 鼠标移动
    QWidget::mouseMoveEvent(QMouseEvent *e);
    // 鼠标按下
    QWidget::mousePressEvent(QMouseEvent *e);
    // 鼠标释放
    QWidget::mouseReleaseEvent(QMouseEvent *e);
    // 鼠标双击
    QWidget::mouseDoubleClickEvent(QMouseEvent *e);
    
    // 鼠标滚轮事件
    QWidget::QWheelEvent(QWheelEvent *e);
    

    注意:在小部件内按下或释放鼠标按钮或移动鼠标光标时,会发生鼠标事件。

    ​ 关于 小部件 :建个 .cpp 与 .h 文件,然后将需要的控件提升为一个小部件。

  3. 拉个label,新建 .cpp 和 .h 文件,将新建的文件继承的类与label继承的改为一致(.h .cpp 有三处需要修改)。

    1660553183914

    1660552913228

  4. myLabel.h

    public:
        // 重写鼠标事件
        // 鼠标移动
        void mouseMoveEvent(QMouseEvent *e);
        // 鼠标按下
        void mousePressEvent(QMouseEvent *e);
        // 鼠标释放
        void mouseReleaseEvent(QMouseEvent *e);
        // 鼠标双击
        void mouseDoubleClickEvent(QMouseEvent *e);
    
  5. myLabel.cpp

    // 鼠标移动
    void myLabel::mouseMoveEvent(QMouseEvent *e)
    {
        QString opt = QString("x = %1 , y = %2 , globalX = %3 , globalY = %4").arg(e->x()).arg(e->y()).arg(e->globalX()).arg(e->globalY());
        qDebug() << opt;
    }
    // 鼠标按下
    void myLabel::mousePressEvent(QMouseEvent *e)
    {
        qDebug() << "mousePressEvent";
    }
    // 鼠标释放
    void myLabel::mouseReleaseEvent(QMouseEvent *e)
    {
        qDebug() << "mouseReleaseEvent";
    }
    // 鼠标双击
    void myLabel::mouseDoubleClickEvent(QMouseEvent *e)
    {
        qDebug() << "mouseDoubleClickEvent";
    }
    

十. 定时器(两种方式)

  1. 利用事件 virtual void timerEvent ( QTimerEvent * e );
  2. 启动定时器 int id1 = startTimer ( 1000 ) 单位毫秒;
  3. 开启定时器的唯一标识id, 可以与事件对象e的 e->timerid 作比较;
// .h   定时器函数重写
void timerEvent(QTimerEvent *e);
int id1;
int id2;

// .cpp 定时器处理函数
void Widget::timerEvent(QTimerEvent *e)
{
    if(e->timerId() == id1)
    {
        static int num1 = 1;
        qDebug() << QString::number(num1++);
    }
    if(e->timerId() == id2)
    {
        static int num2 = 1;
        qDebug() << QString::number(num2++);
    }

}

// 调用
id1 = startTimer(1000);
id2 = startTimer(2000);
  1. 利用QTimer新建一个定时器对象; Qtimer timer = new Timer(this);*
  2. 启动定时器; timer -> start ( 1000 );
  3. 为启动的定时器添加响应函数,即信号 ( QTimer::timeout ) 与槽 ( lambda 表达式 );connect ( timer, &QTimer::timeout, [=] ( ) { } );
	#include <QTimer>

    QTimer* timer = new QTimer(this);				// 创建定时器对象
    timer->start(50);							   // 开启定时器
    connect(timer, &QTimer::timeout, [=](){			 // 定时器触发时做出响应
        static int num = 1;
        if(num<=100)
        {
            qDebug() << QString::number(num++);
        }else
        {
            timer->stop();
        }
    });

十一.事件分发器 与 事件过滤器

1660612698826

事件过滤器:

  1. 给小部件安装事件过滤器;
  2. 重写 eventFilter 事件
// 1.给label组件安装事件过滤器
ui->label->installEventFilter(this);

// 2.重写事件过滤函数
bool eventFilter(QObject* obj, QEvent* e);

bool Widget::eventFilter(QObject* obj, QEvent* e)
{
    // 拦截鼠标按下事件
    if(e->type() == QEvent::MouseButtonPress)
    {
        qDebug() << "filter mouse button press.";
        return true;
    }
    // 其余事件交给父亲QWidget处理
    return QWidget::eventFilter(obj, e);
}

补充:

// 若父类想往子类转
QEvent* e;
QMouseEvent* ev = static_cast<QMouseEvent *>(e);

QEvent *event;
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);

十二.绘图事件

// .h
void paintEvent(QPaintEvent* event);

// .cpp
#include <QPainter>

void Widget::paintEvent(QPaintEvent* event)
{
    // 实例化 画家对象
    QPainter painter(this);
    // 设置画笔颜色
    QPen pen(QColor)
    // 线
    painter.drawLine(QPoint(0, 0), QPoint(200, 200));
    // 圆和椭圆
    painter.drawEllipse(QPoint(100, 100), 80, 80);
    // 画矩形
    painter.drawRect(QRect(20, 20, 50, 50));
    // 画文字
    painter.drawText(QRect(10, 200, 150, 150), "hello world");
}


// 高级设置
// 1. 抗锯齿
painter.setRenderHint();
// 2. 利用画家画图片
painter.drawPixmap(x, y, QPixmap());

十三.文件读写

  1. 读文件

1660628701930

   	// 读取全部
	connect(ui->pushButton, &QPushButton::clicked, this, [=](){
        // open file dialog get file path
        QString path = QFileDialog::getOpenFileName(this,"open file", "C:\\Users\\王韩六六\\Desktop");
        // set edit text
        ui->lineEdit->setText(path);
        // read txt file
        
        QFile file(path);
        if(!file.open(QIODevice::ReadOnly))
        {
            return;
        }
        QByteArray fileArr = file.readAll();		
        
        ui->textEdit->setText(fileArr);
        file.close();
    });


	// 一行一行读取全部
        QByteArray fileArr;
        while(!file.atEnd())
        {
            fileArr += file.readLine();
        }


	// 对其他格式gbk读取
	QTextCodec* codec = QTextCodec::codecForName("gbk");
	ui->textEdit->setText( codec->toUnicode(array) );
  1. 写文件
        if(!file.open(QIODevice::Append))	// 追加的方式
        {
            return;
        }
        file.write("1111111111111111111111 \n");
        file.close();
  1. 文件信息获取 QFileInfo
QFileInfo  info(path);
qDebug() << "大小:" << info.size();	// 字节大小
qDebug() << "后缀:" << info.suffix();

注意:

  1. 在将QString写入csv文件时中文乱码

        // 获取路径
        QString path = QCoreApplication::applicationDirPath();
        path = path.section("/", 0, 3) + "/ini/info.csv";
        qDebug() << path;
        // 打开文件
        m_file.setFileName(path);
        if(!m_file.open(QIODevice::WriteOnly | QIODevice::Truncate))
        {
            qDebug() << "Open info.csv fail!";
            return;
        }
        else
        {
            QString data = "id,姓名,年龄,年级,班级,学号,电话,微信\n";
            //
            m_file.write(data.toLocal8Bit());
            //
        }
    

    解决方法:改为 file.write(data.toLocal8Bit()) 即可解决。

十四.数据库

  1. Sqllite数据库

    void xxxx::init()
    {
        // 设置数据库驱动类型
        db = QSqlDatabase::addDatabase("QSQLITE");
    
    #if 0
        // 设置动态数据库地址
        autho str = QCoreApplication::applicationDirPath();
        qDebug() << str;
    #endif
        
        // 设置数据库名称,加载数据库
        db.setDatabaseName("E:\\QT\\workspace\\data.db");
        // 打开数据库
        if(!db.open())
            qDebug() << "Unable to open database.";
    }
    
    // 构造数据库对象
    QSqlQuery sql(db);
    // 执行 sql 查询
    QString str = QString("delete from student where id = %1").arg(id);
    sql.exec(str);
    // 一个一个查询,需要用.next(),以轮询的方式查询下一个,有执行操作返回true,结束返回false
    while(sql.next())
    {
        
    }
    
    // 捕捉错误信息
    void xxxxx::errorInfo(QSqlQuery sql)
    {
        // 捕捉查询过程中发生的错误信息
        QSqlError e = sql.lastError();
        // e.isValid() 有错误信息返回true,isValid 是可用的
        if(e.isValid())
        {
            qDebug() << e.text();
        }
    }
    
  2. 单例模式--懒汉版

    实现要素:

    1. 定义一个单例类;
    2. 要使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例;
    3. 把构造函数定义为 protected 或 private;
    private:
        // 单例模式,一个类对外只有一个对象存在
        static StuSql* ptr_instance;    // 静态的 私有的
    public:
        static StuSql* GetInstance()
        {
            if(nullptr == ptr_instance)
            {
                ptr_instance = new StuSql();
            }
            return ptr_instance;
        }
    

    ​ 上述单例,没有释放 ptr_instance 指针,存在内存泄漏问题,改进:

        class del
        {
        public:
            ~del()
            {
                if(nullptr != StuSql::ptr_instance)
                {
                    delete StuSql::ptr_instance;
                    StuSql::ptr_instance = NULL;
                }
            }
        };
        static del d;   //  静态变量会在程序结束时调用它的析构函数
    

    该实现会在程序结束时调用静态变量的析构函数,从而 delete ptr_instance 指针;

    在使用这种方法释放单例对象有以下特征:

    1. 在单例内部定义有专有的嵌套类
    2. 在单例内部定义私有的专门用于释放单例对象的静态成员;
    3. 利用程序在结束时析构全局变量的特征,选择最终的释放时机。

    注意点:

    1. 类内声明的静态成员变量,必须要在类外初始化;
    // .h
    class person
    {
        public:
        	static int m_A;
        	static int *m_p;
        	static Person* p;
    };
    
    // .cpp 初始化
    int Person::m_A = 0;
    int *Person::m_p = new int;
    Person* Person::p = nullptr;
    

posted @ 2022-08-09 16:11  王韩六六  阅读(133)  评论(0编辑  收藏  举报