Qt第四天

一个理解:

什么是头文件?

引入后,可以通过创建类对象,来使用功能

例如

#include <QTimer>

QTimer * timer = new

一、QT中的socket通信

Qt中提供的所有的Socket类都是非阻塞的。

Qt中常用的用于socket通信的套接字类:

  • QTcpServer
    • 用于TCP/IP通信, 作为服务器端套接字使用
  • QTcpSocket
    • 用于TCP/IP通信,作为客户端套接字使用。
  • QUdpSocket
    • 用于UDP通信,服务器,客户端均使用此套接字。

1.1 TCP/IP

在这里插入图片描述

  • QTcpServer
    • 用于TCP/IP通信, 作为服务器端套接字使用
  • QTcpSocket
    • 用于TCP/IP通信,作为客户端套接字使用。

备注:

由于QTcpserver 类属于network,首先要再.pro文件中加入模在这里插入图片描述

在这里插入图片描述

添加client的时候,要选Qt设计界面类

在这里插入图片描述

1.1.1 服务器端通信

在Qt中实现TCP/IP服务器端通信的流程:

  • 创建套接字
  • 将套接字设置为监听模式

①、在server.h中,创建两个公共成员 listenQTcpServer型)和 conn(QTcpSocket型)

#ifndef SERVER_H
#define SERVER_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class Server; }
QT_END_NAMESPACE

class Server : public QWidget
{
    Q_OBJECT

public:
    Server(QWidget *parent = nullptr);
    ~Server();
    QTcpServer * listen;
    QTcpSocket * conn;

private:
    Ui::Server *ui;
};
#endif // SERVER_H

②、在server.cpp中,先指定好默认端口和地址,然后创建监听套接字,并放到对象树中,将套接字设置成监听模式

#include "server.h"
#include "ui_server.h"

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

    //默认地址和端口
    ui->sIP->setText(QString("127.0.0.1"));
    ui->sPort->setText(QString::number(999));

    //创建监听套接字,并放到对象树中
    listen = new QTcpServer(this);
    //设置监听
    listen->listen(QHostAddress(ui->sIP->text()),ui->sPort->text().toInt());

}

Server::~Server()
{
    delete ui;
}
  • 等待并接受客户端请求

    可以通过QTcpServer提供的void newConnection()信号来检测是否有连接请求,如果有可以在对应的槽函数中调用nextPendingConnection()函数获取到客户端的Socket信息(返回值为QTcpSocket*类型指针),通过此套接字与客户端之间进行通信。

//有新的连接,会发送信号过来
connect(listen,&QTcpServer::newConnection,[=](){

    //通信套接字
    conn = listen->nextPendingConnection();

    ui->record->append("有新的连接!!!");
});
  • 接收或者向客户端发送数据

    • 接收数据:使用read()或者readAll()函数
    //有新的连接,会发送信号过来
    connect(listen,&QTcpServer::newConnection,[=](){
    
        //通信套接字
        conn = listen->nextPendingConnection();
    
        ui->record->append("有新的连接!!!");
    
        //保证conn是有效对象,连接发送过来的信号
        connect(conn,&QTcpSocket::readyRead,[=](){
    
            QByteArray array = conn->readAll();
    
            ui->record->append(array);
        });
    
    });
    
    • 发送数据:使用write()函数
//按下send按钮的话
connect(ui->send,&QPushButton::clicked,[=](){
    //发送数据
    conn->write(ui->Import->toPlainText().toUtf8());

    ui->record->append("My say:"+ui->Import->toPlainText());

    ui->Import->clear();
});

1.1.2 客户端通信

客户端通信流程:

  • 创建套接字

①、client.h中创建一个成员 client(QTcpSocket型)

QTcpSocket *  client;

②、client.cpp

//默认地址和端口
ui->sIP->setText(QString("127.0.0.1"));
ui->sPort->setText(QString::number(999));
//创建套接字
client = new QTcpSocket(this);
  • 连接服务器

    可以使用QTcpSocket类的**connectToHost()**函数来连接服务器。

//连接服务器
client->connectToHost(QHostAddress(ui->sIP->text()),ui->sPort->text().toInt());
  • 向服务器发送或者接受数据
//客户端发送数据
connect(ui->send,&QPushButton::clicked,[=](){

    client->write(ui->Import->toPlainText().toUtf8());

    ui->record->append("My say:"+ui->Import->toPlainText());

    ui->Import->clear();

});

//客户端接收数据
connect(client,&QTcpSocket::readyRead,[=](){

    QByteArray array = client->readAll();

    ui->record->append(array);

});

1.1.3 整体

①、main.cpp中要将client加入!!!

#include "server.h"
#include "client.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Server w;
    w.setWindowTitle("服务器");
    w.show();


    Client c;
    c.setWindowTitle("客户端");
    c.show();
    return a.exec();
}

②、server.cpp服务器端

#include "server.h"
#include "ui_server.h"

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

    //默认地址和端口
    ui->sIP->setText(QString("127.0.0.1"));
    ui->sPort->setText(QString::number(999));

    //创建监听套接字
    listen = new QTcpServer(this);
    //设置监听
    listen->listen(QHostAddress(ui->sIP->text()),ui->sPort->text().toInt());

    //有新的连接,会发送信号过来
    connect(listen,&QTcpServer::newConnection,[=](){

        //通信套接字
        conn = listen->nextPendingConnection();

        ui->record->append("有新的连接!!!");

        //保证conn是有效对象,连接发送过来的信号
        connect(conn,&QTcpSocket::readyRead,[=](){

            QByteArray array = conn->readAll();

            ui->record->append(array);
        });

    });

    connect(ui->send,&QPushButton::clicked,[=](){
        //发送数据
        conn->write(ui->Import->toPlainText().toUtf8());

        ui->record->append("My say:"+ui->Import->toPlainText());

        ui->Import->clear();
    });


}

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


③、client.cpp客户端

#include "client.h"
#include "ui_client.h"
#include <QHostAddress>


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

    //默认地址和端口
    ui->sIP->setText(QString("127.0.0.1"));
    ui->sPort->setText(QString::number(999));

    client = new QTcpSocket(this);
    //连接服务器
    client->connectToHost(QHostAddress(ui->sIP->text()),ui->sPort->text().toInt());

    //客户端发送数据
    connect(ui->send,&QPushButton::clicked,[=](){

        client->write(ui->Import->toPlainText().toUtf8());

        ui->record->append("My say:"+ui->Import->toPlainText());

        ui->Import->clear();

    });

    //客户端接收数据
    connect(client,&QTcpSocket::readyRead,[=](){

        QByteArray array = client->readAll();

        ui->record->append(array);


    });
}

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

④、效果图:

在这里插入图片描述

1.2 UDP

在这里插入图片描述

使用Qt提供的QUdpSocket进行UDP通信。在UDP方式下,客户端并不与服务器建立连接,它只负责调用发送函数向服务器发送数据。类似的服务器也不从客户端接收连接,只负责调用接收函数,等待来自客户端的数据的到达

1.2.1 UDP通信

在UDP通信中,服务器端和客户端的概念已经显得有些淡化,两部分做的工作都大致相同:

  • 创建套接字

  • 绑定套接字

    在UDP中如果需要接收数据需要对套接字进行绑定,只发送数据则不需要对套接字进行绑定。

    通过调用bind()函数将套接字绑定到指定端口上。

ui->sPort->setText("9999");
ui->cPort->setText("8888");
ui->cIp->setText("127.0.0.1");

//创建udp的对象
udp1 = new QUdpSocket(this);

//绑定自身端口
udp1->bind(ui->sPort->text().toInt());
  • 接收或者发送数据

    ①、接收数据:使用readDatagram()接收数据,函数声明如下:

qint64 readDatagram(char * data, qint64 maxSize,

QHostAddress * address = 0, quint16 * port = 0)

参数:

  • data: 接收数据的缓存地址
  • maxSize: 缓存接收的最大字节数
  • address: 数据发送方的地址(一般使用提供的默认值)
  • port: 数据发送方的端口号(一般使用提供的默认值)

使用**pendingDatagramSize()可以获取到将要接收的数据的大小**,根据该函数返回值来准备对应大小的内存空间存放将要接收的数据。

//接收数据
connect(udp1,&QUdpSocket::readyRead,[=](){
    //char * data = "adf";  不推荐,因为在栈区开辟空间还得去销毁
    qint64 size = udp1->pendingDatagramSize();
    QByteArray array = QByteArray(size,0);
    udp1->readDatagram(array.data(),size);

    //聊天记录追加数据
    //ui->record->append(QString(array));  QByteArray 会自动有一个隐式转换成QString,可以直接下面那样
    ui->record->append("udp2:"+array);

});

​ ②、发送数据: 使用writeDatagram()函数发送数据(传输报文),函数声明如下:

qint64 writeDatagram(const QByteArray & datagram,

const QHostAddress & host, quint16 port)

参数:

  • datagram:要发送的字符串
  • host:数据接收方的地址
  • port:数据接收方的端口号
//发送数据
connect(ui->send,&QPushButton::clicked,[=](){

    udp1->writeDatagram( ui->import_2->toPlainText().toUtf8(),QHostAddress(ui->cIp->text()),ui->cPort->text().toInt());

    ui->record->append("udp1:"+ui->import_2->toPlainText());

    ui->import_2->clear();
});

1.2.2 广播

在使用QUdpSocket类的writeDatagram()函数发送数据的时候,其中第二个参数host应该指定为广播地址:QHostAddress::Broadcast此设置相当于QHostAddress("255.255.255.255")

使用UDP广播的的特点:

  • 使用UDP进行广播,局域网内的其他的UDP用户全部可以收到广播的消息
  • UDP广播只能在局域网范围内使用

1.2.3 组播

我们再使用广播发送消息的时候会发送给所有用户,但是有些用户是不想接受消息的,这时候我们就应该使用组播,接收方只有先注册到组播地址中才能收到组播消息,否则则接受不到消息。另外组播是可以在Internet中使用的。

在使用QUdpSocket类的writeDatagram()函数发送数据的时候,其中第二个参数host应该指定为组播地址,关于组播地址的分类:

  • 224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
  • 224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
  • 224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
  • 239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。

注册加入到组播地址需要使用QUdpSocket类的成员函数:

bool joinMulticastGroup(const QHostAddress & groupAddress)

server.cpp

#include "server.h"
#include "ui_server.h"
#include <QHostAddress>

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

    //设置默认
    ui->sPort->setText("9999");
    ui->cPort->setText("8888");
    ui->cIp->setText("127.0.0.1");

    //创建套接字
    udp1 = new QUdpSocket(this);
    //绑定自身端口
    //udp1->bind(ui->sPort->text().toInt());
    udp1->bind(QHostAddress::AnyIPv4,ui->sPort->text().toInt());

    //发送数据
    connect(ui->pushButton,&QPushButton::clicked,[=](){

        //udp1->writeDatagram(ui->Import->toPlainText().toUtf8(),QHostAddress(ui->cIp->text()),ui->cPort->text().toInt());
        //广播
        //udp1->writeDatagram(ui->Import->toPlainText().toUtf8(),QHostAddress::Broadcast,ui->cPort->text().toInt());
        //组播
        udp1->writeDatagram(ui->Import->toPlainText().toUtf8(),QHostAddress("224.0.1.10"),ui->cPort->text().toInt());

        ui->record->append("udp1:"+ui->Import->toPlainText());

        ui->Import->clear();
    });


    //接收数据
    connect(udp1,&QUdpSocket::readyRead,[=](){

        qint16 size = udp1->pendingDatagramSize();

        QByteArray array = QByteArray(size,0);

        udp1->readDatagram(array.data(),size);

        ui->record->append("udp2:"+array);

    });


}

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


client.cpp

#include "client.h"
#include "ui_client.h"
#include <QUdpSocket>

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

    //设置默认
    ui->cPort->setText("8888");
    ui->sPort->setText("9999");
    ui->sIp->setText("127.0.0.1");

    //创建套接字
    udp2 = new QUdpSocket(this);
    //绑定自身端口
    //udp2->bind(ui->cPort->text().toInt());
    udp2->bind(QHostAddress::AnyIPv4,ui->cPort->text().toInt());

    //加入到组播中
    udp2->joinMulticastGroup(QHostAddress("224.0.1.10"));

    //发送数据
    connect(ui->pushButton,&QPushButton::clicked,[=](){

        udp2->writeDatagram(ui->Import->toPlainText().toUtf8(),QHostAddress(ui->sIp->text()),ui->sPort->text().toInt());

        ui->record->append("udp2:"+ui->Import->toPlainText());

        ui->Import->clear();
    });


    //接收数据
    connect(udp2,&QUdpSocket::readyRead,[=](){

        qint16 size = udp2->pendingDatagramSize();

        QByteArray array = QByteArray(size,0);

        udp2->readDatagram(array.data(),size);

        ui->record->append("udp1:"+array);

    });


}

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

1.3 TCP/IP 与UDP的对比

TCP/IP 效率比UDP低,因为前者有三次握手,但是可靠性前者高

TCP :可能会粘包,解决办法:延时发送 ,发送多少接收多少

UDP:可能会丢包,解决办法:外部加一层封装,验证是否接收到数据(理解为一次握手就好)

QQ、屏幕共享:用UDP,快;

二、UDP传输图片

2.1 方法一:数据流

2.1.1 ui布局

在这里插入图片描述

步骤:

  1. 首先选一个widget,放入一个line edit 和两个pushButton,对这个widget进行水平布局
  2. 再选一个label,以备显示图片。对整体进行一个垂直布局
  3. 第一个widget明显显示不行,可以将sizePolicy的垂直策略改为Fixed

在这里插入图片描述

​ label想要显示居中,可以再QLbel中的alignment把水平的改为center

在这里插入图片描述

​ 4.新加入一个Qt设计师界面,作为接收方的界面使用,直接放入一个Label,并且做居中处理

在这里插入图片描述

2.1.2 sender部分

首先要在.pro文件中把network模块加入

QT       += core gui network
2.1.2.1 sender.h

创建两个成员,sendpath

#ifndef SENDER_H
#define SENDER_H

#include <QWidget>
#include <QUdpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class Sender; }
QT_END_NAMESPACE

class Sender : public QWidget
{
    Q_OBJECT

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

private:
    Ui::Sender *ui;
    QUdpSocket * send;
    QString path; // 为了在类内可见
};
#endif // SENDER_H
2.1.2.2 sender.cpp

①、显示图片地址

#include "sender.h"
#include "ui_sender.h"
#include <QFileDialog>
#include <QPixmap>


Sender::Sender(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Sender)
{
    ui->setupUi(this);
        
    connect(ui->select,&QPushButton::clicked,[=](){

        path = QFileDialog::getOpenFileName(this,"选择文件","F:\\spj c++\\QT\\Day4\\07_UDPImage_shujuliu\\Image");

        if(!path.isEmpty())
        {
            //将文件名显示在框内
            ui->lineEdit->setText(path);

            //显示图片
            QPixmap pix(path);

            ui->pic->setPixmap(pix);
        }

    });

}

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

②、发送图片

//UDP套接字,放到对象树中
send = new QUdpSocket(this);

send->bind(9999);

connect(ui->send,&QPushButton::clicked,[=](){

    //准备容器
    QBuffer buf;
    buf.open(QIODevice::WriteOnly);

    QImage img(path);
    QDataStream stream(&buf);
    stream << img;

    qDebug()<<"发送数据长度:"<<buf.buffer().size();
    qint64 size = send->writeDatagram(buf.buffer(),QHostAddress::LocalHost,8888);
    qDebug()<<"发送报文长度:"<<size;


    //这是个IO设备,打开了应该关闭
    buf.close();

});

2.1.3 receiver部分

2.1.3.1 receiver.h
#ifndef RECEIVER_H
#define RECEIVER_H

#include <QWidget>
#include <QUdpSocket>

namespace Ui {
class Receiver;
}

class Receiver : public QWidget
{
    Q_OBJECT

public:
    explicit Receiver(QWidget *parent = nullptr);
    ~Receiver();

private:
    Ui::Receiver *ui;
    QUdpSocket * receive;
};

#endif // RECEIVER_H

2.1.3.2 receiver.cpp
#include "receiver.h"
#include "ui_receiver.h"
#include <QDebug>
#include <QBuffer>
#include <QIODevice>
#include <QDataStream>

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

    receive = new QUdpSocket(this);

    receive->bind(8888);


    connect(receive,&QUdpSocket::readyRead,[=](){

        qDebug()<<"收到了数据!";

        //报文大小
        qint64 size = receive->pendingDatagramSize();
        QByteArray array(size,0);

        receive->readDatagram(array.data(),size);

        //数据流
        QBuffer buf(&array);
        buf.open(QIODevice::ReadOnly);
        QDataStream stream(&buf);

        QImage img;
        stream >> img;
        //这是个IO设备,打开了应该关闭
        buf.close();
		//QImage 转 QPixmap
        QPixmap pix = QPixmap::fromImage(img);
        ui->label->setPixmap(pix);
    });

}

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

2.2 方法二:save方法

2.2.1 sender部分

#include "sender.h"
#include "ui_sender.h"
#include <QFileDialog>
#include <QPixmap>
#include <QBuffer>
#include <QDataStream>
#include <QDebug>
#include <QFileInfo>

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

    connect(ui->select,&QPushButton::clicked,[=](){

        path =  QFileDialog::getOpenFileName(this,"选择图片","F:\\spj c++\\QT\\Day4\\07_UDPImage\\Image");
        if(!path.isEmpty())
        {
            ui->lineEdit->setText(path);

            QPixmap pix(path);

            ui->pic->setPixmap(pix);
        }

    });

    //UDP的套接字
    send = new QUdpSocket(this);

    send->bind(9999);

    connect(ui->send,&QPushButton::clicked,[=](){

        //准备容器
        QBuffer buf;
        buf.open(QIODevice::WriteOnly);

        QImage img(path);

        //方法二:
        QFileInfo info(path);
        img.save(&buf,info.suffix().toUtf8().data());

        QByteArray newArray =  info.suffix().toUtf8().data() + buf.buffer();

        qDebug()<<"发送数据长度:"<<buf.buffer().size();

        qint64 size =  send->writeDatagram(newArray,QHostAddress::LocalHost,8888);
        qDebug()<<"发送报文长度:"<<size;

        //这是个IO设备,打开了应该关闭
        buf.close();

    });
}

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


2.2.2 receiver部分

#include "receiver.h"
#include "ui_receiver.h"
#include <QBuffer>
#include <QDataStream>
#include <QPixmap>
#include <QDebug>
#include <QFileInfo>

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

    receive = new QUdpSocket(this);

    receive->bind(8888);

    connect(receive,&QUdpSocket::readyRead,[=](){
        qDebug()<<"收到数据";

        //报文大小
        qint64 size = receive->pendingDatagramSize();
        QByteArray array(size,0);

        receive->readDatagram(array.data(),size);

        //方法二: save方法
        QImage img;
        QByteArray array1 = array.left(3);

        QByteArray array2 = array.right(array.size()-3);
        img.loadFromData(array2,array1);

        QPixmap pix = QPixmap::fromImage(img);
        ui->label->setPixmap(pix);

    });
}

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

三、多线程

在这里插入图片描述

3.1 Qt4.7版本以前

3.1.1 mythread

mythread.h

1.声明一个信号 2. 重写run方法 (注意是protected的)

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>

class MyThread : public QThread
{
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

signals:
    //信号值只需要声明
    void signalDone();

protected:
    //重写Protected Functions 里的 run()
    void run();

};

#endif // MYTHREAD_H

mythread.cpp

#include "mythread.h"
#include <QThread>

MyThread::MyThread(QObject *parent) : QThread(parent)
{

}

//对run实现
void MyThread::run()
{
    QThread::sleep(5);
    //发送信号
    emit signalDone();
}

3.1.2 widget

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();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QTimer>
#include <QThread>
#include "mythread.h"

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

    QTimer * timer = new QTimer(this);

    MyThread * thread = new MyThread(this);

    connect(ui->pushButton,&QPushButton::clicked,[=](){

        if(timer->isActive() == true) return;

//        //模拟复杂操作
//        QThread::sleep(3);
        thread->start();

        timer->start(1000);

    });

    //子线程做完复杂工作后,停止主线程里的lcd数字++
    connect(thread,&MyThread::signalDone,timer,&QTimer::stop);

    connect(timer,&QTimer::timeout,[=](){
        static int num = 0;
        ui->lcdNumber->display(++num);
    });
}

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

3.2 第二种方式

把复杂的业务逻辑封装到一个类中,声明一个方法,做复杂的事情

引用复杂的业务逻辑的类Work

创建一个业务逻辑对象

Work * worker = new Work();

创建一个子线程

QThread * thread = new Qthread(this);

将逻辑对象 放入到子线程中

work->moveToThread(thread);

启动子线程

thread->start;

3.2.1 问题警告!!!

//不能写在lambda表达式里面,要不然就和信号发送者 一个线程里了 也就是都放在主线程里面
connect(ui->start,&QPushButton::clicked,[=](){

    if(timer->isActive() == true) return;
    timer->start(1000);
    //启动子线程
    thread->start();

    worker->working();
});

打印一下线程,发现是同一个

在这里插入图片描述

3.2.2 MyWidget

MyWidget.h

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class MyWidget; }
QT_END_NAMESPACE

// 头文件
class MyWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MyWidget(QWidget *parent = 0);
    ~MyWidget();

protected:
    void paintEvent(QPaintEvent *);

private:
    Ui::MyWidget *ui;
    QImage m_image;
};
#endif // MYWIDGET_H

MyWidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"
#include "work.h"
#include <QThread>

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

    Work* pWork = new Work;
    connect(ui->draw, &QPushButton::clicked,
pWork, &Work::slotDrawImage);

    QThread * pthread = new QThread(this);
    // 将操作移入子线程中处理
    pWork->moveToThread(pthread);
    // 启动子线程
    pthread->start();

    connect(pWork, &Work::ImageDone, [=](QImage image)
    {
         // 保存图片
        m_image = image;
         // 刷新窗口
        update();
    });

    connect(this, &MyWidget::destroyed, [=]()
    {
        // 退出线程
        pthread->quit();
        pthread->wait();
        delete pWork;
    });
}

void MyWidget::paintEvent(QPaintEvent *e)
{
    QPainter p(this);
    p.drawImage(0, 0, m_image);
}

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


3.2.3 Work

#ifndef WORK_H
#define WORK_H

#include <QObject>
#include <QImage>
#include <QPainter>
#include <QPoint>

class Work : public QObject
{
    Q_OBJECT
public:
    Work(QObject *parent = 0) : QObject(parent)
    {

    }

public slots:
    void slotDrawImage()
    {
        QImage image(600, 600, QImage::Format_ARGB32);
        QPainter painter(&image);
        QPoint pt[] =
        {
            QPoint(qrand()%590, qrand()%590),
            QPoint(qrand()%590, qrand()%590),
            QPoint(qrand()%590, qrand()%590),
            QPoint(qrand()%590, qrand()%590),
            QPoint(qrand()%590, qrand()%590),
        };
        painter.drawPolygon(pt, 5);
         // 将画好的图片通过信号发送出去
        emit ImageDone(image);
    }
signals:
    void ImageDone(QImage image);
};

#endif // WORK_H

四、打包问题

(2条消息) 【qt】无法定位程序输入点 __gxx_personality_v0 于动态链接库_追寻生命的意义-CSDN博客

总结就是:没有设置环境变量

解决办法:

1.设置环境变量

2.用下面这个代替cmd

在这里插入图片描述

3.把

qt目录下的libstdc+±6.dll (D:\Qt\Qt5.10.0\5.10.0\mingw53_32\bin\libstdc+±6.dll),拷贝到exe所在文件夹

(2条消息) QT5.12 程序打包在win10发布(将QT5的工程项目打包成一个exe程序)_u014453443的博客-CSDN博客_qt打包生成exe

windeployqt+Engima Virtual Box

步骤:

1.先把QT里面debug设置成release,运行一次

在这里插入图片描述

2.找到F:\QT\Day4\build-09_QthreadDraw3-Desktop_Qt_5_9_9_MinGW_32bit-Release文件下

在这里插入图片描述

进入release文件,那个里面的cpp才是需要的,复制到一个空文件夹

3.在QT 5.9.9(MinGW 5.3.0 32-bit)里面运行windeployqt

在这里插入图片描述

4.点击文件夹里的demo.exe 看能否运行成功

5.使用Engima Virtual Box打包,对文件进行打包,生成不依赖QT库的exe程序

在这里插入图片描述

按照上图的示意,添加路径和文件如下图。文件可以托选,选择所有文件后,直接拖入界面框内。注意是要所有文件和文件夹全部拖进。示意图如下:

在这里插入图片描述

在右下角的【Files Options】中可以选择是否将文件压缩:

在这里插入图片描述

点击打包,即可

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值