一个理解:
什么是头文件?
引入后,可以通过创建类对象,来使用功能
例如
#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
中,创建两个公共成员 listen
(QTcpServer型)和 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布局
步骤:
- 首先选一个widget,放入一个line edit 和两个pushButton,对这个widget进行水平布局
- 再选一个label,以备显示图片。对整体进行一个垂直布局
- 第一个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
创建两个成员,send
和path
#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 于动态链接库_追寻生命的意义-优快云博客
总结就是:没有设置环境变量
解决办法:
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的博客-优快云博客_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】中可以选择是否将文件压缩:
点击打包,即可