QT学习---------知识整理(进阶篇)

一、前言

本文主要讲讲qt的进阶知识,线程、通信、数据库、事件等知识。

二、正文

2.1 QT中的多线程

2.1.1 导言

主线程与子线程注意点:

  •  主线程:默认的线程,程序启动自带的,也叫窗口线程,UI线程负责窗口时间处理或窗口控件数据的更新
  • 子线程:子线程负责后台的业务逻辑处理,子线程中不能对窗口对象做任何操作,窗口的操作需要交给主线程操作
  • 主线程与子线程的关系:主线程与子线程之间如果要进行数据的传递,需要使用Qt中的信号槽机制

2.1.2 线程类QThread

创建子线程的类

2.1.2.1 常用的公共成员函数
  • 构造函数

  • 判断线程中的任务是不是处理完毕了

  • 判断子线程是不是在执行任务

优先级:

  • 得到当前线程的优先级

  • 设置优先级

  • 优先级排序,从低到高:

  • 退出线程,停止底层的事件循环,退出线程的工作函数

  • 调用线程退出函数后,线程不会马上退出,因为当前任务有可能还没有完成,等待任务完成,然后退出线程,一般在exit()后面调用wait()

2.1.2.2 信号槽
  • 和调用exit()效果是一样的,代用quit()后,再调用wait()函数

  • 启动子线程

  • 线程退出,可能是会马上终止线程,一般情况下不使用这个函数

  • 线程中执行的任务完成,发出该信号,任务函数中的处理逻辑执行完毕

  • 开始工作之前发出这个信号,一般不使用

2.1.2.3 静态函数
  • 返回一个指向管理当前执行线程的QThread的指针

  • 返回可以在系统上运行的理想线程数==和当前电脑CPU核心数相同

  • 线程休眠函数

2.1.2.4 任务处理函数
  • 子线程要处理什么任务,需要写到run()中

这个函数是一个受保护的成员函数,不能够在类的外部调用,如果想要让线程执行这个函数中的业务流程,需要通过当前线程对象调用槽函数start()启动子线程,当子线程被启动,这个run()函数也就在线程内部被调用了。

2.1.3 使用方法一

该方法简单,但是都写到run()里面,逻辑容易混乱,不利于维护

2.1.3.1 操作步骤
  • 创建子类,继承QThread

  • 重写父类run()方法,在该函数内部编写子线程要处理的具体业务

  • 在线程中创建子线程对象

  • 启动子线程,调用start()方法

不能在类的外部调用run()方法启动子线程,在外部调用start()相当于让run()开始运行

  • 释放资源

不释放资源会报错。

运行结果:

2.1.4 使用方法二

2.1.4.1 操作步骤
  • 创建一个新的类,让这个类从QObject派生

  • 在这个类中添加一个公共成员函数,函数体就是我们要在子线程中执行的业务逻辑

  • 在主线程中创建一个QThread对象,这就是子线程的对象

  • 在主线程中创建工作的类对象

  • 将MyThread2对象移动到创建的子线程对象中,需要调用QObject类提供的moveToThread()方法

  • 启动线程

  • 调用工作函数

  • 释放资源

  • 运行结果

2.1.5 线程池

2.1.5.1 线程池原理

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务,线程池线程都是后台线程。前面两种多线程的使用方法都要释放线程资源,加入线程池后可以不用释放,线程池会自动释放。

线程池包含三部分:

  • 任务队列,存储需要处理的任务,由工作的线程来处理这些任务
  • .工作的线程
  • 线程池管理器,负责管理线程池的创建、销毁和线程数量的控制
2.1.5.2 QRunnable

  • 在子类中必须要重写的函数,里面是任务的处理流程

  • 参数设置为true,这个任务对象在线程池中的线程处理完毕,这个任务对象就会自动销毁。  参数设置为false,这个任务对象在线程池中的线程处理完毕,对象需要手动销毁

  • 获取当前任务的析构方式,返回true---自动析构,返回false---手动析构

2.1.5.3 QThreadPool

常用的API函数

  • 获取和设置线程中的最大线程个数

  • 给线程池添加任务,任务是一个QRunnable类型的对象,如果线程池中没有空闲的线程了,任务会放到任务队列中,等待线程处理

  • 如果线程池中没有空闲的线程了,直接返回值,任务添加失败,任务不会添加到任务队列中

  • 线程池中被激活的线程个数(正在工作的线程个数)

  • 尝试性的将某一个任务从线程池的任务队列中删除,如果任务已经开始执行就无法删除

  • 在每个Qt应用程序中都有一个全局的线程池对象,通过这个函数直接访问这个对象

2.1.5.4 举例
  • 创建一个要添加到线程池中的任务类

  • 线程池启动与运行结果

2.2 通信

2.2.1 网络通信

2.2.1.1 基于TCP的Qt网络通信

network库中的套接字来实现,支持TCP和UDP,TCP安全可靠,UDP不怎么可靠

TCP:

UDP:

2.2.1.2 QTcpServer

  • 构造函数

  • 监听
  • 判断当前对象是否在监听,是返回true,没有监听返回false

  • 如果当前对象正在监听,返回监听的服务器地址信息,否则返回QHostAddress::NULL

  • 如果服务器正在监听连接,则返回服务器的端口,否则返回0

2.2.1.3 QTcpSocket

  • 构造函数

  • 连接主地址

  • 准备读取

  • 连接与不连接

2.2.1.4 通信流程
  • 创建套接字服务器QTcpServer对象
  • 通过QTcpServer对象设置监听,即QTcpServer::listen()
  • 基于QTcpServer::newConnection()信号检测是否有新的客户连接
  • 如果有新的客户端连接,调用QTcpSocket *QTcpServer::nextPendingConnection()得到通信的套接字对象
  • 使用通信套接字对象QTcpSocket和客户端进行通信
2.2.1.5 举例见文件传输

Server:

#include "acceptfile.h"

#include <QFile>


acceptfile::acceptfile(QTcpSocket* tcp, QThread *parent) : QThread(parent)
{
    m_tcp = tcp;
}

void acceptfile::run()
{
    //创建文件对象,保存客户端发送过来的文件内容
    QFile* file = new QFile("C:/Users/EDY/Desktop/QTtest/recv.txt");
    file->open(QFile::WriteOnly);
    //读取套接字socket的内容
    connect(m_tcp, &QTcpSocket::readyRead, this, [=](){
        static int total = 0;
        static int count = 0;
        if(count == 0)
        {
            m_tcp->read((char*)&total, 4);
        }
        //读出剩余数据
        QByteArray all = m_tcp->readAll();
        count += all.size();
        file->write(all);

        //判断是否接收完毕
        if(total == count)
        {
            m_tcp->close();
            m_tcp->deleteLater();
            file->close();
            file->deleteLater();
            emit over();
        }
    });
    //进入事件循环
    exec();
}
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "acceptfile.h"

#include <QMessageBox>

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

    //创建服务器对象
    m_server = new QTcpServer(this);
    //创建套接字
    connect(m_server, &QTcpServer::newConnection, this, [=](){
        QTcpSocket* tcp = m_server->nextPendingConnection();
        //创建子线程
        acceptfile* subthread = new acceptfile(tcp);
        subthread->start();

        //释放资源
        connect(subthread, &acceptfile::over, this, [=](){
            subthread->quit();
            subthread->wait();
            subthread->deleteLater();
            QMessageBox::information(this, "文件接收", "文件接收完毕!!!!!!!");
        });
    });
}

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


void MainWindow::on_pushButton_listen_clicked()
{
    m_server->listen(QHostAddress::Any, ui->lineEdit_port->text().toUShort());
}

client:

#include "sendfile.h"

#include <QFile>
#include <QFileInfo>

sendfile::sendfile(QObject *parent) : QObject(parent)
{

}

void sendfile::connectServer(unsigned short address, QString ip)
{
    m_tcp = new QTcpSocket;
    m_tcp->connectToHost(ip, address);
    connect(m_tcp, &QTcpSocket::connected, this, &sendfile::connectOK);
    connect(m_tcp, &QTcpSocket::disconnected, this, [=](){
        m_tcp->close();
        m_tcp->deleteLater();
        emit gameover();
    });
}

void sendfile::sendFile(QString path)
{
    QFile file(path);
    QFileInfo info(path);

    int filesize = info.size();

    file.open(QFile::ReadOnly);

    while (!file.atEnd())
    {
        static int num = 0;
        if(num == 0)
        {
            m_tcp->write((char*)&filesize, 4);
        }
        QByteArray line = file.readLine();
        num += line.size();
        int percent = (num * 100 / filesize);
        emit curpercent(percent);

        m_tcp->write(line);
    }
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "sendfile.h"

#include <QFileDialog>
#include <QMessageBox>

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

    ui->port->setText("8888");
    ui->IP->setText("192.168.2.24");
    ui->progressBar->setRange(0, 100);
    ui->progressBar->setValue(0);

    //创建线程对象
    QThread* thrd = new QThread;
    //创建任务
    sendfile* worker = new sendfile;
    //将work放入子线程
    worker->moveToThread(thrd);
    //连接服务器
    connect(this, &MainWindow::startconnect, worker, &sendfile::connectServer);
    //连接文件发送
    connect(this, &MainWindow::sendFile, worker, &sendfile::sendFile);
    //处理连接服务器的子线程数据
    connect(worker, &sendfile::connectOK, this, [=](){
        QMessageBox::information(this, "连接服务器",  "连接成功!!!!");
    });
    connect(worker, &sendfile::gameover, this, [=](){
        //资源释放
        thrd->quit();
        thrd->wait();
        worker->deleteLater();
        thrd->deleteLater();
    });
    //处理选择文件发送子线程数据
    connect(worker, &sendfile::curpercent, ui->progressBar, &QProgressBar::setValue);

    //线程开始
    thrd->start();
}

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


void MainWindow::on_connectserver_btn_clicked()
{
    QString ip = ui->IP->text();
    unsigned short port = ui->port->text().toUShort();
    emit startconnect(port, ip);
}

void MainWindow::on_file_btn_clicked()
{
    QString path = QFileDialog::getOpenFileName();
    if(path.isEmpty())
    {
        QMessageBox::warning(this, "打开文件", "选择的文件路径不能为空!!");
        return;
    }
    ui->file->setText(path);
}

void MainWindow::on_send_btn_clicked()
{
    emit sendFile(ui->file->text());
}

2.2.2 串口通信

2.2.2.1 Qt中的串口类

2.2.2.2 串口编写常用元素
  • 可获取端口

  • 串口名

COM1、COM2等串口名称,使用availablePorts()可以获取到可连接的串口名称,构造串口类对象时需要使用到串口名称。

  • 波特率

串口连接使用的参数,波特率相同的串口连接后才能通信。

  • 串口模式

需要给串口设置读写模式

  • 串口开启

  • 信号读取

信号读取时采用信号槽机制,以准备读取微信号,读取全部为槽函数。

  • 数据读取

2.2.2.3 通信步骤

1、设置串口名称与波特率

2、打开串口

3、读取数据

4、发送数据

#include "MySerial.h"

MySerial::MySerial(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);

	ui.label_serialportmsg->clear();//清空提示信息
	on_pushButton_Refresh_clicked();//刷新

    connect(ui.pushButton_Refresh, &QPushButton::clicked, this, &MySerial::on_pushButton_Refresh_clicked);//串口刷新
    connect(ui.pushButton_openserialport, &QPushButton::clicked, this, &MySerial::on_pushButton_openserialport_clicked);//串口打开
    connect(ui.pushButton_closeserialport, &QPushButton::clicked, this, &MySerial::on_pushButton_closeserialport_clicked);//串口关闭
    connect(ui.pushButton_clear, &QPushButton::clicked, this, &MySerial::on_pushButton_clear_clicked);//清空信息
    connect(ui.pushButton_send, &QPushButton::clicked, this, &MySerial::on_pushButton_send_clicked);//发送信息
}

MySerial::~MySerial()
{}

//刷新串口
void MySerial::on_pushButton_Refresh_clicked()
{
    //清空串口名
    ui.comboBox_serialportname->clear();
    //遍历串口信息
    foreach(const QSerialPortInfo & info, QSerialPortInfo::availablePorts())
    {
        ui.comboBox_serialportname->addItem(info.portName());
        int baud = m_serial.baudRate();
        ui.comboBox_baudrate->addItem(QString("%1").arg(baud));
    }
}

//打开串口
void MySerial::on_pushButton_openserialport_clicked()
{
    m_serial.setPortName(ui.comboBox_serialportname->currentText());
    m_serial.setBaudRate(ui.comboBox_baudrate->currentText().toInt());

    m_serial.open(QIODevice::ReadWrite);

    if (m_serial.isOpen())
    {
        ui.label_serialportmsg->setText("串口打开成功");
        connect(&m_serial, &QSerialPort::readyRead, this, [=]()
            {
                QByteArray array = m_serial.readAll();
                ui.textEdit_receiv->insertPlainText(array);
            });
    }
    else
    {
        ui.label_serialportmsg->setText("串口打开失败!!");
    }
}

//关闭串口
void MySerial::on_pushButton_closeserialport_clicked()
{
    m_serial.close();
    if (m_serial.isOpen())
    {
        ui.label_serialportmsg->setText("串口关闭失败!!");
    }
    else
    {
        ui.label_serialportmsg->setText("串口关闭成功");
    }
}

//清空信息
void MySerial::on_pushButton_clear_clicked()
{
    ui.textEdit_receiv->clear();
}

//发送信息
void MySerial::on_pushButton_send_clicked()
{
    QByteArray array = ui.textEdit_send->toPlainText().toUtf8();
    m_serial.write(array);
}
#pragma once
#pragma execution_character_set("utf-8")

#include <QMainWindow>
#include "ui_MySerial.h"

#include <QtSerialPort/qserialport.h>
#include <QtSerialPort/qserialportinfo.h>

class MySerial : public QMainWindow
{
	Q_OBJECT

public:
	MySerial(QWidget *parent = nullptr);
	~MySerial();

private:
	Ui::MySerialClass ui;

	QSerialPort m_serial;

	void on_pushButton_Refresh_clicked();
	void on_pushButton_openserialport_clicked();
	void on_pushButton_closeserialport_clicked();
	void on_pushButton_clear_clicked();
	void on_pushButton_send_clicked();
};

2.3 数据库

2.3.1 Qt中的数据库

2.3.1.1 模块

Qt的数据库调用需要添加SQL模块

2.3.1.2 常用的数据库类
  • QSqlDatabase:通过这个类添加/删除/复制/关闭数据库实例
  • QSqlQuery:数据库查询类
  • QSqlRecord:数据库记录(通常是数据库中表或视图中的一行)的功能和特征
  • QSqlField:数据库表或视图中单个列的特征,例如数据库类型的列名
  • QSqlQueryModel:执行SQL语句和遍历结果集的高级接口,它构建在底层QSqlQuery之上,可以用来为视图类(如QTableView)提供数据
  • QSqlError:数据操作失败可以通过这个类获取相关的错误信息
2.3.1.3 数据库操作流程
  • 创建数据库实例并初始化
  • 连接数据库
  • 对数据库进行一系列的添、删、查、改操作(编写并执行SQL语句)
  • 关闭数据库

一般使用的数据库类为:QSqlDatabase创建于链接数据库,QSqlQuery数据的添、删、查、改,QSqlError数据库写程序用的工具人,用来判断数据库的错误。

2.3.1.4 举例
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QSqlDatabase>
#include <QDebug>
#include <QSqlError>
#include <QSqlQuery>

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

    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    QStringList list = QSqlDatabase::drivers();
    qDebug() << list;
    db.setHostName("localhost");//localhost等同于本机IP的127.0.0.1
    db.setPort(3306);
    db.setDatabaseName("crops");
    db.setUserName("root");
    db.setPassword("**********");

    if(!db.open())
    {
        qDebug() << "失败原因" << db.lastError().text();
    }
    else
    {
        qDebug() << "连接成功";
    }
    QSqlQuery query;

    //创建表格的SQL语句
    QString createTableStatement = "CREATE TABLE IF NOT EXISTS crop ("
                "品种 TEXT,"
                "光补偿点 INT,"
                "最适光照 INT,"
                "光饱和点 INT,"
                "临界温度低 INT,"
                "最适合温度 INT,"
                "临界温度高 INT,"
                "水临界低 INT,"
                "水最适 INT,"
                "水临界高 INT,"
                "气(CO2) INT,"
                "肥(按照PH计算) INT)";

    //执行创建表格的SQL查询
    query.exec(createTableStatement);

    if (!query.exec())
    {
        qDebug() << "Error creating table: " << query.lastError().text();
    }
    else
    {
        qDebug() << "Table 'crop' created successfully.";
    }


    //往数据库添加数据需要建立一个事务
    QString sql = "insert into crop values(1,1,1,1,1,1,1,1,1,1,1,1)";
    db.transaction();
    bool flag = query.exec(sql);
    if(flag)
    {
        db.commit();
    }
    else
    {
        db.rollback();
    }

    sql = "select * from crop";
    query.exec(sql);
    while(query.next())
    {
        //打印数据库内容
        //value里面可以是数组对应的数字,也可以是具体某个字符
        qDebug() << query.value(0).toUInt()
               << query.value(1).toUInt();
    }
    db.close();
}

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

2.4 Qt中的事件

2.4.1 绘画事件QPaintEvent

2.4.1.1 简述

当窗口系统请求重绘窗口的某部分时发生,通常在paintEvent函数中处理,用于更新界面显示。

使用的时候需要重构函数!!!事件的使用都需要重构!!!

2.4.1.2 常用函数
2.4.1.2.1 构造函数

QPainter(QPaintDevice *device)

2.4.1.2.2 背景颜色设置

在Qt中,QPainter类本身并没有直接提供一个名为background()的方法来设置或获取背景。不过,你可以通过几种方式来控制绘图设备的背景色或背景画刷:

  • 使用QPaintDevice的背景

如果你在使用如QWidgetQPixmapQImage等作为绘图设备,可以在这些对象上设置背景。例如,在QWidget中,你可以重写paintEvent()并使用QPainter来填充整个 widget 的背景:

void MyWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QBrush background(Qt::blue); // 设置背景刷为蓝色
    painter.fillRect(rect(), background); // 使用画刷填充整个widget
    // ... 其他绘制代码 ...
}
  • 使用QPalette

对于基于widgets的绘图,可以通过设置QPalette来改变背景颜色

QPalette palette;
palette.setColor(QPalette::Window, Qt::red); // 设置窗口背景色为红色
this->setPalette(palette);
  • 自定义绘制背景

在需要更复杂背景时(如渐变色、图案或图像),可以在paintEvent中直接使用QPainter来绘制所需的背景

2.4.1.2.3 开始

2.4.1.2.4 刷子

2.4.1.2.5 设备

指定设备即可对该设备进行绘制操作

2.4.1.2.6 画点

2.4.1.2.7 画线

两点确定一条直线

2.4.1.2.8 画圆形

中心点+长+高,长和高一致为圆形,不一致为椭圆形

2.4.1.2.9 画矩形

四个点确定矩形

其余图形根据实际需求自己查询手册寻找。

2.4.1.3  实例

简单的实现绘画功能:

#include "MyEvent.h"

MyEvent::MyEvent(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);
}

MyEvent::~MyEvent()
{}

void MyEvent::paintEvent(QPaintEvent * event)
{
	QPainter* myPainter = new QPainter;
	myPainter->begin(this);
	myPainter->translate(100, 100);
	myPainter->setBrush(Qt::red);
	myPainter->drawRect(100, 100, 50, 50);
}
#pragma once

#include <QMainWindow>
#include "ui_MyEvent.h"

#include <QPainter>

class MyEvent : public QMainWindow
{
	Q_OBJECT

public:
	MyEvent(QWidget *parent = nullptr);
	~MyEvent();

private:
	Ui::MyEventClass ui;

protected:
	virtual void paintEvent(QPaintEvent* event);
};

复杂点的绘画:

#pragma once
#pragma execution_character_set("utf-8")

#include <QMainWindow>
#include "ui_MyEvent.h"

#include <QPainter>
#include <QDebug>
#include <QtMath>
#include <QDialog>
#include <QPaintEvent>
#include <QPainterPath>
#include <QRadialGradient>
#include <QTimer>
#include <QKeyEvent>

class MyEvent : public QMainWindow
{
	Q_OBJECT

public:
	MyEvent(QWidget *parent = nullptr);
	~MyEvent();
	QTimer* myTimer;

	int radius;//仪表盘的中心位置
	int direction;//指针运动的方向,1为前进,0为后退

private:
	Ui::MyEventClass ui;

protected:
	virtual void paintEvent(QPaintEvent* event);

private:
    int degRotate = 0;

private:
    void DrawPoint(QPainter&, int);
    void DrawDigital(QPainter&, int);
    void DrawCircle(QPainter&, int);
    void DrawSmallScale(QPainter&, int);
    void DrawUnit(QPainter&, int);
    void DrawNum(QPainter&, int);
    void DrawPointer(QPainter&, int);
    void DrawCircle_line(QPainter& painter, int radius);
    void DrawCircle_bom(QPainter& painter, int radius);
    void DrawCircle_bom_big(QPainter& painter, int radius);
    void DrawCircle_bom_shine(QPainter& painter, int radius);
    void DrawCircle_bom_small(QPainter& painter, int radius);
    void DrawCircle_arc(QPainter& painter, int radius);

    void keyPressEvent(QKeyEvent* event);
    void keyReleaseEvent(QKeyEvent* event);

private slots:
    void slot_speed_changed();
};
#include "MyEvent.h"

MyEvent::MyEvent(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);
	setFixedSize(1280, 800);

	//设置背景墙
	this->setStyleSheet("#MyEventClass{background-image:url(:/1)}");

	//定时器动态增加时速
	myTimer = new QTimer(this);
	connect(myTimer, SIGNAL(timeout()), this, SLOT(slot_speed_changed()));
}

MyEvent::~MyEvent()
{}

void MyEvent::paintEvent(QPaintEvent * event)
{
    QPainter painter(this);
    int width = this->width();
    int height = this->height() - 100;//移动仪表盘的高度
    int radius = ((width > height) ? height : width) / 2.0;//仪表盘的中心位置
    //移动画笔到中下方
    painter.translate(width / 2, height * 0.6);
    //启用反锯齿
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setPen(Qt::NoPen);
    //设置画刷颜色
    painter.setBrush(QColor(138, 43, 226));
    DrawSmallScale(painter, radius - 60);//刻度线
    DrawDigital(painter, radius - 90);//刻度数字
    /*所有形状绘画*/
//    DrawCircle_bom(painter,radius-40);  //扇形大圆
    DrawCircle(painter, radius - 35);      //渐变发光外扇形
    DrawCircle_arc(painter, radius - 40);//动态扇形环
    DrawPointer(painter, radius - 130);//指针
    DrawCircle_line(painter, radius - 35); //最外细圆线
    DrawCircle_bom_big(painter, radius - 150);//中间大圆
    DrawCircle_bom_shine(painter, radius - 230);//渐变发光内圈
    DrawCircle_bom_small(painter, radius - 200);//中间小圆

    DrawUnit(painter, radius - 390);//单位
    DrawNum(painter, radius - 300);//时速
}

//绘制外圈点
void MyEvent::DrawPoint(QPainter& painter, int radius)
{
    //组装点的路径图
    QPainterPath pointPath;
    pointPath.moveTo(-2, -2);
    pointPath.lineTo(2, -2);
    pointPath.lineTo(2, 2);
    pointPath.lineTo(0, 4);
    pointPath.lineTo(-2, 2);
    //绘制13个小点
    for (int i = 0; i < 13; ++i) {
        QPointF point(0, 0);
        painter.save();
        painter.setBrush(QColor(250, 252, 78));
        //计算并移动绘图对象中心点
        point.setX(radius * qCos(((210 - i * 20) * M_PI) / 180));
        point.setY(radius * qSin(((210 - i * 20) * M_PI) / 180));
        //计算并移动绘图对象的中心点
        painter.translate(point.x(), -point.y());
        //计算并选择绘图对象坐标
        painter.rotate(-120 + i * 20);
        //绘制路径
        painter.drawPath(pointPath);
        painter.restore();
    }
}

void MyEvent::DrawDigital(QPainter& painter, int radius)
{
    //设置画笔,画笔默认NOPEN
    painter.setPen(QColor(255, 255, 255));
    QFont font;
    font.setFamily("Arial");
    font.setPointSize(15);
    font.setBold(true);
    painter.setFont(font);
    for (int i = 0; i < 13; ++i) {
        QPointF point(0, 0);
        painter.save();
        point.setX(radius * qCos(((210 - i * 20) * M_PI) / 180));
        point.setY(radius * qSin(((210 - i * 20) * M_PI) / 180));
        painter.translate(point.x(), -point.y());
        painter.rotate(-120 + i * 20);
        painter.drawText(-25, 0, 50, 20, Qt::AlignCenter, QString::number(i * 20));
        painter.restore();
    }
    //还原画笔
    painter.setPen(Qt::NoPen);
}

void MyEvent::DrawCircle(QPainter& painter, int radius)
{
    //保存绘图对象
    painter.save();
    //计算大小圆路径
    QPainterPath outRing;
    QPainterPath inRing;
    outRing.moveTo(0, 0);
    inRing.moveTo(0, 0);
    outRing.arcTo(-radius, -radius, 2 * radius, 2 * radius, -30, 240);
    inRing.addEllipse(-radius + 50, -radius + 50, 2 * (radius - 50), 2 * (radius - 50));
    outRing.closeSubpath();
    //设置渐变色k
    QRadialGradient radialGradient(0, 0, radius, 0, 0);
    radialGradient.setColorAt(1, QColor(0, 82, 199));
    radialGradient.setColorAt(0.92, Qt::transparent);
    //设置画刷
    painter.setBrush(radialGradient);
    //大圆减小圆
    painter.drawPath(outRing.subtracted(inRing));
    painter.restore();
}

//绘制刻度
void MyEvent::DrawSmallScale(QPainter& painter, int radius)
{
    //组装点的路径图
    QPainterPath pointPath_small;
    pointPath_small.moveTo(-2, -2);
    pointPath_small.lineTo(2, -2);
    pointPath_small.lineTo(2, 8);
    pointPath_small.lineTo(-2, 8);


    QPainterPath pointPath_big;
    pointPath_big.moveTo(-2, -2);
    pointPath_big.lineTo(2, -2);
    pointPath_big.lineTo(2, 20);
    pointPath_big.lineTo(-2, 20);

    //绘制121个小点
    for (int i = 0; i < 121; i += 2) {
        QPointF point(0, 0);
        painter.save();
        point.setX(radius * qCos(((210 - i * 2) * M_PI) / 180));
        point.setY(radius * qSin(((210 - i * 2) * M_PI) / 180));
        painter.translate(point.x(), -point.y());
        painter.rotate(-120 + i * 2);

        if (i < 80)
        {
            painter.setBrush(QColor(255, 255, 255));
        }
        if (i >= 80)
        {
            painter.setBrush(QColor(235, 70, 70));
        }

        if (i % 5 == 0)
        {
            painter.drawPath(pointPath_big);//绘画大刻度
        }
        else
        {
            painter.drawPath(pointPath_small);//绘画小刻度
        }
        painter.restore();
    }
}

//绘制文字
void MyEvent::DrawUnit(QPainter& painter, int radius)
{
    painter.save();
    painter.setPen(QColor(255, 255, 255));
    QFont font;
    font.setFamily("Arial");
    font.setPointSize(16);
    font.setBold(true);
    painter.setFont(font);
    painter.drawText(-50, -radius, 100, 20, Qt::AlignCenter, QString("km/h"));
    painter.drawText(-60, -radius + 130, 120, 40, Qt::AlignCenter, QString("当前车速"));

    painter.setPen(QColor(255, 255, 255, 50));
    painter.drawText(-120, -radius + 280, 250, 40, Qt::AlignCenter, QString("-请按space键加速-"));
    painter.restore();
}

//绘制实时时速数字
void MyEvent::DrawNum(QPainter& painter, int radius)
{
    painter.save();
    painter.setPen(QColor(255, 255, 255));
    QFont font;
    font.setFamily("Arial");
    font.setPointSize(45);
    painter.setFont(font);
    painter.drawText(-75, -radius - 20, 150, 100, Qt::AlignCenter, QString::number(degRotate));
    painter.restore();
}

//绘制指针
void MyEvent::DrawPointer(QPainter& painter, int radius)
{
    //组装点的路径图
    QPainterPath pointPath;
    pointPath.moveTo(10, 0);
    pointPath.lineTo(1, -radius);
    pointPath.lineTo(-1, -radius);
    pointPath.lineTo(-10, 0);
    pointPath.arcTo(-10, 0, 20, 20, 180, 180);
    QPainterPath inRing;
    inRing.addEllipse(-5, -5, 10, 10);
    painter.save();

    //计算并选择绘图对象坐标
    painter.rotate(degRotate - 120);
    painter.setBrush(QColor(255, 255, 255));
    painter.drawPath(pointPath.subtracted(inRing));
    painter.restore();
}

void MyEvent::DrawCircle_line(QPainter& painter, int radius)
{
    //保存绘图对象
    painter.save();
    //计算大小圆路径
    QPainterPath outRing;
    QPainterPath inRing;
    outRing.moveTo(0, 0);
    inRing.moveTo(0, 0);
    outRing.arcTo(-radius, -radius, 2 * radius, 2 * radius, -30, 240);
    inRing.addEllipse(-radius + 2, -radius + 2, 2 * (radius - 2), 2 * (radius - 2));
    outRing.closeSubpath();

    //设置画刷
    painter.setBrush(QColor(5, 228, 255));
    //大圆减小圆
    painter.drawPath(outRing.subtracted(inRing));
    painter.restore();
}

void MyEvent::DrawCircle_bom(QPainter& painter, int radius)
{
    //保存绘图对象
    painter.save();
    //计算大小圆路径
    QPainterPath outRing;
    outRing.moveTo(0, 0);
    outRing.arcTo(-radius, -radius, 2 * radius, 2 * radius, -30, 240);
    outRing.closeSubpath();
    //设置画刷
    painter.setBrush(QColor(14, 15, 33));

    painter.drawPath(outRing);
    painter.restore();
}

void MyEvent::DrawCircle_bom_big(QPainter& painter, int radius)
{
    //保存绘图对象
    painter.save();
    //计算大小圆路径
    QPainterPath inRing;
    inRing.moveTo(0, 0);
    inRing.addEllipse(-radius + 50, -radius + 50, 2 * (radius - 50), 2 * (radius - 50));
    //设置画刷
    painter.setBrush(QColor(10, 20, 30));
    painter.drawPath(inRing);
    painter.restore();
}

void MyEvent::DrawCircle_bom_shine(QPainter& painter, int radius)
{
    painter.save();
    QRadialGradient radialGradient(0, 0, radius, 0, 0);
    //    radialGradient.setColorAt(0.5,QColor(8,77,197));
    radialGradient.setColorAt(0.5, QColor(10, 68, 185, 150));
    radialGradient.setColorAt(1.0, Qt::transparent);
    painter.setBrush(QBrush(radialGradient));
    painter.drawRect(-radius, -radius, 2 * (radius), 2 * (radius));
    painter.restore();
}

void MyEvent::DrawCircle_bom_small(QPainter& painter, int radius)
{
    //保存绘图对象
    painter.save();
    //计算大小圆路径
    QPainterPath inRing;
    inRing.moveTo(0, 0);
    inRing.addEllipse(-radius + 50, -radius + 50, 2 * (radius - 50), 2 * (radius - 50));
    //设置画刷
    painter.setBrush(QColor(10, 20, 30));
    painter.drawPath(inRing);

    painter.restore();
}

void MyEvent::DrawCircle_arc(QPainter& painter, int radius)
{
    QRect rect(-radius, -radius, 2 * radius, 2 * radius);
    QConicalGradient Conical(0, 0, -70);

    Conical.setColorAt(0.1, QColor(255, 88, 127, 200));//红色
    Conical.setColorAt(0.5, QColor(53, 179, 251, 150));//蓝色
    painter.setBrush(Conical);
    painter.drawPie(rect, 210 * 16, -(degRotate) * 16);
}

//按键按下事件
void MyEvent::keyPressEvent(QKeyEvent* event)
{
    if (event->key() == Qt::Key_Space)
    {
        if (direction == 0)
        {
            myTimer->start(1);
            direction = 1;
        }

    }
}

//按键释放事件
void MyEvent::keyReleaseEvent(QKeyEvent* event)
{
    if (event->key() == Qt::Key_Space)
    {
        direction = 0;
    }
}

//动态增加时速画面效果
void MyEvent::slot_speed_changed()
{
    if (direction == 1)//加速
    {
        degRotate++;
        if (degRotate > 240)
            degRotate = 240;
    }
    else if (direction == 0)//减速
    {
        degRotate--;
        if (degRotate < 0)
        {
            degRotate = 0;
            myTimer->stop();
        }
    }
    update();//刷新画面。很重要!
}

2.4.2 鼠标事件QMouseEvent

2.4.2.1 简述

鼠标事件相当于鼠标信号接收到后需要做的动作,其中需要注意的是坐标,鼠标经常遇到的情况就是坐标问题,比如点击一片区域的任何一个位置,有相同的动作。常用的事件是mousePressEvent、contextMenuEvent、mouseMoveEvent等,建议直接用这些封装好的函数,方便一些,不然还需要自己写一些东西。

2.4.2.2 举例子
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QInputEvent>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QMessageBox>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>

#include "weatherdata.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

protected:
    void contextMenuEvent(QContextMenuEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);


private:
    QMenu* mExitMenu;//右键退出的菜单
    QAction* mExitAct;//退出行为 - 菜单项

    QPoint mOffset;//窗口移动时,鼠标与窗口左上角的偏移

    //Http请求
    QNetworkAccessManager* mQNetworkAccessManager;

    Today mToday;
    Day mDay[6];

private:
    void getWeatherInfo(QString cityCode);//获取天气数据

    void parseJson(QByteArray& byteArray);//解析天气数据

private slots:
    void onReplied(QNetworkReply* reply);


};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"



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

    setFixedSize(800, 400);//固定窗口大小
    setWindowFlag(Qt::FramelessWindowHint);//设置窗口无边框

    //构建右键菜单
    mExitMenu = new QMenu(this);
    mExitAct = new QAction();

    mExitAct->setText("退出");
    mExitAct->setIcon(QIcon(":/res/close.png"));

    mExitMenu->addAction(mExitAct);

    connect(mExitAct, &QAction::triggered, this,[=]()
    {
        qApp->exit(0);
    });

    //数据请求与获取
    mQNetworkAccessManager = new QNetworkAccessManager(this);
    connect(mQNetworkAccessManager, &QNetworkAccessManager::finished, this, &MainWindow::onReplied);

    //直接在构造中,请求天气数据
    //"101010100"是北京的城市编码
    //"101270101"成都,"101020100"上海,"101280101"广州,"101280601"深圳,"101200101"武汉
    getWeatherInfo("101270101");//默认成都
    connect(ui->comboBox, &QComboBox::currentTextChanged,this,[=]()
    {
        if(ui->comboBox->currentText() == "成都")
        {
            getWeatherInfo("101270101");
        }
        else if(ui->comboBox->currentText() == "北京")
        {
            getWeatherInfo("101010100");
        }
        else if(ui->comboBox->currentText() == "上海")
        {
            getWeatherInfo("101020100");
        }
        else if(ui->comboBox->currentText() == "广州")
        {
            getWeatherInfo("101280101");
        }
        else if(ui->comboBox->currentText() == "深圳")
        {
            getWeatherInfo("101280601");
        }
        else if(ui->comboBox->currentText() == "武汉")
        {
            getWeatherInfo("101200101");
        }
    });

    //ui关联数据
    connect(mQNetworkAccessManager, &QNetworkAccessManager::finished, this, [=]()
    {
        ui->lineEdit_city->setText(mToday.city);
        ui->lineEdit_date->setText(mToday.date);
        ui->lineEdit_type->setText(mToday.type);
        ui->lineEdit_week->setText(mToday.week);
        ui->lineEdit_lowTemp->setText(QString("%1").arg(mToday.lowTemp));
        ui->lineEdit_highTemp->setText(QString("%1").arg(mToday.highTemp));
        ui->lineEdit_currentTemp->setText(QString("%1").arg(mToday.temperature));
        ui->lineEdit_PM25->setText(QString("%1").arg(mToday.pm25));
    });

    //初始化
    QStringList texts;
    texts << "成都" << "北京" << "上海" << "广州" << "深圳" << "武汉";
    ui->comboBox->addItems(texts);
}

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

void MainWindow::contextMenuEvent(QContextMenuEvent *event)
{
    //弹出右键菜单
    mExitMenu->exec(QCursor::pos());

    event->accept();
}

void MainWindow::mousePressEvent(QMouseEvent *event)
{
    mOffset = event->globalPos() - this->pos();
}

void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    if(!((event->x() <= (ui->comboBox->pos().x()+100))&&
      (event->x() >= (ui->comboBox->pos().x()))&&
      (event->y() >= (ui->comboBox->pos().y()))&&
      (event->y() <= (ui->comboBox->pos().y()+20))))
    {
        this->move(event->globalPos() - mOffset);
    }
}

void MainWindow::onReplied(QNetworkReply *reply)
{
//    qDebug() << "onReplied success!";

    //请求码,200表示成功
    int status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();

//    qDebug() << "operation:" << reply->operation();//请求的方法
//    qDebug() << "status_code:" << status_code;//请求码
//    qDebug() << "url:" << reply->url();//请求的url
//    qDebug() << "raw header:" << reply->rawHeaderList();//请求的响应头

    //错误提示
    if(reply->error() != QNetworkReply::NoError || status_code != 200)
    {
        qDebug("%s(%d) error: %s", __FUNCTION__,__LINE__,reply->errorString().toLatin1().data());
        QMessageBox::critical(this,"天气","请求天气数据失败!");
    }
    else
    {
        //获取响应信息
        QByteArray byteArray = reply->readAll();
//        qDebug() << "天气数据:" << byteArray.data();
        parseJson(byteArray);
    }

    reply->deleteLater();
}

void MainWindow::getWeatherInfo(QString cityCode)
{
    QUrl url("http://t.weather.itboy.net/api/weather/city/" + cityCode);
    mQNetworkAccessManager->get(QNetworkRequest(url));
}

void MainWindow::parseJson(QByteArray &byteArray)
{
    QJsonParseError err;
    QJsonDocument doc = QJsonDocument::fromJson(byteArray, &err);
    if(err.error != QJsonParseError::NoError)
    {
       return;
    }

    QJsonObject rootObj = doc.object();
//    qDebug() << rootObj.value("message").toString();

    //1.解析日期城市
    mToday.date = rootObj.value("date").toString();
    mToday.city = rootObj.value("cityInfo").toObject().value("city").toString();
//    qDebug() << mToday.date << " and " << mToday.city;

    //2.解析yesterday
    QJsonObject objData = rootObj.value("data").toObject();

    QJsonObject objYesterday = objData.value("yesterday").toObject();
    mDay[0].week = objYesterday.value("week").toString();
    mDay[0].date = objYesterday.value("ymd").toString();

    mDay[0].type = objYesterday.value("type").toString();

    QString s;
    //split是分割,“高温 15℃”中间是空格,所以用空格作为分割的依据
    //分成“高温”和“15℃”,at(1)为第二个即15℃
    s = objYesterday.value("high").toString().split(" ").at(1);
//    s.left(s.length()-1);//15
    mDay[0].highTemp = s.left(s.length()-1).toInt();

    s = objYesterday.value("low").toString().split(" ").at(1);
//    s.left(s.length()-1);//7
    mDay[0].lowTemp = s.left(s.length()-1).toInt();

    mDay[0].windScale = objYesterday.value("fl").toString();
    mDay[0].windDirection = objYesterday.value("fx").toString();

    mDay[0].aqi = objYesterday.value("api").toDouble();//空气质量指数

    //3.解析forcast中5天的数据
    QJsonArray forecastArr = objData.value("forecast").toArray();

    for (int i=0;i<5;i++)
    {
        QJsonObject objForecast = forecastArr[i].toObject();
        mDay[i+1].week = objForecast.value("week").toString();
        mDay[i+1].date = objForecast.value("ymd").toString();

        mDay[i+1].type = objForecast.value("type").toString();

        QString str;
        str = objForecast.value("high").toString().split(" ").at(1);
        mDay[i+1].highTemp = str.left(str.length()-1).toInt();

        str = objForecast.value("low").toString().split(" ").at(1);
        mDay[i+1].lowTemp = str.left(str.length()-1).toInt();

        mDay[i+1].windScale = objForecast.value("fl").toString();
        mDay[i+1].windDirection = objForecast.value("fx").toString();

        mDay[i+1].aqi = objForecast.value("api").toDouble();
    }


    //4.解析今天的数据
    mToday.coldIndex = objData.value("ganmao").toString();

    mToday.temperature = objData.value("wendu").toString().toInt();
    mToday.humidity = objData.value("shidu").toString();
    mToday.pm25 = objData.value("pm25").toDouble();
    mToday.quality = objData.value("quality").toString();

    //5.forecast中第一个数组元素,也是今天的数据
    mToday.type = mDay[1].type;

    mToday.windScale = mDay[1].windScale;
    mToday.windDirection = mDay[1].windDirection;

    mToday.highTemp = mDay[1].highTemp;
    mToday.lowTemp = mDay[1].lowTemp;
    mToday.week = mDay[1].week;
}


其余事件可自行查阅帮助手册,一般还有滚轮和键盘按键的事件用的多

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值