Qt广告机客户端(下位机)

Qt广告机服务器(上位机)

功能

  1. 连接服务器(上位机)
  2. 广告图片播放模块
  3. 日期显示模块
  4. 天气显示模块
  5. 信息提示模块
    在这里插入图片描述

有两张以上图片,自动滚动

接收进度显示,删除提示(5s自动确认)

通过下发的地区获取天气信息

结构

在这里插入图片描述

adClient.pro

QT       += core gui network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

TARGET = adClient
TEMPLATE = app


SOURCES += main.cpp\
        adclient.cpp \
    addate.cpp \
    adsocket.cpp \
    weather.cpp \
    rollmassege.cpp

HEADERS  += adclient.h \
    addate.h \
    adsocket.h \
    tcp_MSG.h \
    weather.h \
    rollmassege.h

FORMS    += adclient.ui

RESOURCES += \
    res.qrc

main.cpp

#include "adclient.h"
#include <QApplication>
#include <QDebug>
#include <QDir>
int main(int argc, char *argv[])
{
    
    
    QApplication a(argc, argv);
    // 判断当前运行环境是否为Linux或Windows。
#ifdef Q_OS_LINUX
    qDebug() << "Current OS is Linux";
#elif defined(Q_OS_WIN)
    qDebug() << "Current OS is Windows";
#else
    qDebug() << "Unknown OS";
#endif

    QString folderName = "pic";
    //QString folderPath = QDir::currentPath() + "/" + folderName;
    QString folderPath = QApplication::applicationDirPath() + "/" + folderName;

    QDir folder(folderPath);
    if (!folder.exists())
    {
    
    

        bool success = folder.mkpath("."); // 创建文件夹
        if (success)
            qDebug() << "文件夹创建成功";
        else
            qDebug() << "文件夹创建失败";
    }
    else
         qDebug() << "文件夹已存在";

    QDir::setCurrent(folderPath);// 设置文件生成路径

    AdClient w;
    w.show();

    return a.exec();
}

tcp_MSG.h 共用Tcp传输信息

#ifndef TCP_MSG_H
#define TCP_MSG_H
#include<QMetaType>
#define tcp_MSG_txt_NUM 256
#define tcp_MSG_city_NUM 32
#define tcp_MSG_weather_NUM 128
#define tcp_MSG_path_NUM 128
#define tcp_MSG_photo_NUM 1280*800
#pragma pack(1)     //设置结构体为1字节对齐
typedef struct
{
    
    
    int type;// 消息类型
    char txt[tcp_MSG_txt_NUM];// 文字信息
    char city[tcp_MSG_city_NUM];// 城市
    char area[tcp_MSG_city_NUM];// 地区
    char weather[tcp_MSG_weather_NUM];// 天气
    char fileName[tcp_MSG_path_NUM];// 文件名
    int index; // 编号
    int allAd_Num;// 总数
    int fileSize;// 文件大小
}tcp_MSG;
#pragma pack()		//结束结构体对齐设置
Q_DECLARE_METATYPE(tcp_MSG)
// 实现对象的元编程功能。它可以用来定义宏,类型和函数,以支持将元数据与类型关联起来。它还可以用来实现类型安全性,类型转换和序列化功能。
//宏来注册tcp_MSG类型

enum MsgType{
    
    
    Init=0, // 初始化
    WEATHER, //天气
    MASSEGE,// 留言
    VIDEO, // 视频
    AD_add, // 添加广告
    AD_delete, // 删除广告
    Write_back,//回复
};

#pragma pack(1)     //设置结构体为1字节对齐
typedef struct
{
    
    
    int type;// 消息类型
    int state;// 状态  0不发送 1发送
    char id[32];// id
}tcp_backMSG;
#pragma pack()		//结束结构体对齐设置
Q_DECLARE_METATYPE(tcp_backMSG)
//宏来注册tcp_backMSG类型

#pragma pack(1)     //设置结构体为1字节对齐
typedef struct
{
    
    
    int type;// 消息类型
    char photo[tcp_MSG_photo_NUM];
}tcp_Image;
#pragma pack()		//结束结构体对齐设置
Q_DECLARE_METATYPE(tcp_Image)
//宏来注册tcp_Image类型

#endif // TCP_MSG_H

adclient.h 客户端

#ifndef ADCLIENT_H
#define ADCLIENT_H

#include <QMainWindow>
#include "addate.h"
#include "adsocket.h"
#include "weather.h"
#include "rollmassege.h"
#include <QLabel>
#include <QProgressDialog>
#include "stdlib.h"
#include <QThread>
#include <QTimer>
#include <QFile>
#include <QMessageBox>
#include <QNetworkInterface>
#include <QTcpSocket>
#include <QHostAddress>
#include <QAbstractSocket>
#include <QLineEdit>
#include <QPushButton>

namespace Ui {
    
    
class AdClient;
}

class AdClient : public QMainWindow
{
    
    
    Q_OBJECT

public:
    explicit AdClient(QWidget *parent = 0);
    ~AdClient();
    void saveFile();// 保存文件
    void InitStatusBar();// 初始化底部状态栏
    QString GetLocalIP();// 获取本地IP

public slots:
    void showImage(QImage);// 图片显示
    void showProgressBar(int currentProgress,int finish);//进度条显示
    void autoPlayTimeOut();//自动播放
    void GUI_WarningMsg(QString title,QString text,QString buttons,QString defaultButton);//设置警报
    void connectToServer();// 链接到服务器
private:
    Ui::AdClient *ui;
    AdDate *date;
    AdSocket *socket;
    Weather *weater;
    RollMassege *rollmsg;
    QProgressDialog *progress;// 创建进度条
    QTimer autoPlayTimer;
    int index;
    QLabel *mLocalIP;
    QLabel *serverIP_lb;
    QLineEdit *serverIP;
    QLabel *serverPort_lb;
    QLineEdit *serverPort;
    QPushButton *connectTcp;

};

#endif // ADCLIENT_H

adclient.cpp 客户端

#include "adclient.h"
#include "ui_adclient.h"
#include <QDebug>

QMap<QString,QImage> qMapPicturePath;// 图片路径

//QTcpSocket 的默认缓存区大小是 64KB(65536字节)
AdClient::AdClient(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::AdClient)
{
    
    
    ui->setupUi(this);
    this->setWindowIcon(QIcon(":/client.jpg"));
    date = new AdDate(ui->date_lb);//时间
    socket = new AdSocket;// TCP客户端
    weater = new Weather(ui->weather_lb);// 接收天气信息绘制
    rollmsg = new RollMassege(ui->msg_lb, ui->msg_lb);// 滚动信息

    connect(socket, SIGNAL(sig_showWeather(QString,QString,QString)), weater, SLOT(showWeather(QString,QString,QString)));
    connect(socket, SIGNAL(sig_showTxt(QString)), rollmsg, SLOT(showTxt(QString)));
    connect(socket, SIGNAL(sig_showImage(QImage)), this, SLOT(showImage(QImage)));
    connect(socket, SIGNAL(sig_showProgressBar(int,int)), this, SLOT(showProgressBar(int,int)),Qt::QueuedConnection);
    connect(socket, SIGNAL(GUI_WarningSignal(QString,QString,QString,QString)), this, SLOT(GUI_WarningMsg(QString,QString,QString,QString)));

    date->start();//更新时间
    progress = nullptr;
    autoPlayTimer.stop();
    connect(&autoPlayTimer, SIGNAL(timeout()), this, SLOT(autoPlayTimeOut()));

    InitStatusBar();// 初始化底部状态栏
}

AdClient::~AdClient()
{
    
    
    delete socket;
    delete ui;
}

// 保存文件
void AdClient::saveFile()
{
    
    
    QString fileName=qMapPicturePath.lastKey();
    qDebug()<<"lastKey:"<<fileName;
    QImage image=qMapPicturePath.last();
    image.save(fileName);
}

// 初始化底部状态栏
void AdClient::InitStatusBar()
{
    
    
    mLocalIP=new QLabel(this);
    mLocalIP->setMinimumWidth(230);
    QString ip = GetLocalIP();
    //    mLocalIP->setText("本地IP:"+ip);
    mLocalIP->setText("本地IP:"+tr("<font color=\"red\">%1</font>").arg(ip));

    serverIP_lb=new QLabel(this);
    serverIP_lb->setMinimumWidth(100);
    serverIP_lb->setText("服务器IP:");
    serverIP=new QLineEdit(this);
    serverIP->setMinimumWidth(200);
    serverIP->setInputMask("000.000.000.000");

    serverPort_lb=new QLabel(this);
    serverPort_lb->setMinimumWidth(60);
    serverPort_lb->setText("Port:");
    serverPort=new QLineEdit(this);
    serverPort->setMinimumWidth(60);
    serverPort->setPlaceholderText("8888");

    connectTcp=new QPushButton("链接",this);
    connectTcp->setMinimumWidth(100);

    ui->statusBar->addWidget(mLocalIP);
    ui->statusBar->addWidget(serverIP_lb);
    ui->statusBar->addWidget(serverIP);
    ui->statusBar->addWidget(serverPort_lb);
    ui->statusBar->addWidget(serverPort);
    ui->statusBar->addWidget(connectTcp);

    connect(connectTcp, SIGNAL(clicked()), this, SLOT(connectToServer()));
}


// 获取本地IP
QString AdClient::GetLocalIP()
{
    
    
    QList<QHostAddress> list=QNetworkInterface::allAddresses();
    foreach(QHostAddress address,list)
    {
    
    
        if(address.protocol()==QAbstractSocket::IPv4Protocol)
        {
    
    
            qDebug()<<address.toString();
            return address.toString();
        }
    }
    return "";
}


// 图片显示
void AdClient::showImage(QImage image)
{
    
    
    qDebug()<<"显示图片"<<__LINE__;
    QPixmap pixmap=QPixmap::fromImage(image);
    // 内容是否自动缩放,参数true自动缩放
    ui->video_lb->setScaledContents(true);//显示图片的全部
    // 将图片缩放到指定大小,参数ui->label->size()表示缩放的大小,Qt::KeepAspectRatio表示保持图片的宽高比,Qt::SmoothTransformation表示使用平滑缩放算法
    ui->video_lb->setPixmap(pixmap.scaled(ui->video_lb->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));

    qDebug()<<"图片数量:"<<qMapPicturePath.count();
    qDebug()<<"定时器状态:"<<autoPlayTimer.isActive();

    if(qMapPicturePath.count()>=2)
    {
    
    
        if(!autoPlayTimer.isActive()){
    
    
            // 两张以上图片开启播放
            autoPlayTimer.start(5000);
            index=0;
        }
    }
    else
    {
    
    
        autoPlayTimer.stop();
    }

    if(qMapPicturePath.count()>=1)
    {
    
    
        // 功能没问题,调试注释,避免多次保存
        //saveFile();// 保存文件
    }
}

//进度条显示
void AdClient::showProgressBar(int currentProgress, int finish)
{
    
    
    qDebug()<<"进度条显示"<<__LINE__;
    if (!progress)
    {
    
    
        //创建进度条
        progress = new QProgressDialog("接收中....", "取消", 0, 100, this);
        progress->setWindowModality(Qt::WindowModal);
        progress->setFixedSize(this->width()*0.3,this->height()*0.3);
        progress->setVisible(true);

    }
    //usleep(500);//在Windows环境下已被弃用
    QThread::msleep(50);//休眠50毫秒
    //更新进度条
    progress->setValue(currentProgress * 100 / finish);

    //如果任务已经完成,隐藏进度条
    if (currentProgress == finish)
    {
    
    
        qDebug()<<"进度条显示完成"<<__LINE__;
        progress->hide();
        delete progress;
        progress = nullptr;
    }
}

//自动播放
void AdClient::autoPlayTimeOut()
{
    
    
    QList<QString> keyList = qMapPicturePath.keys();//存放的就是QMap的key值

    if(index>=qMapPicturePath.count())// 删除广告时有可能 >
        index=0;

    QImage image=qMapPicturePath.value(keyList.at(index));
    qDebug()<<"自动播放:"<<keyList.at(index);
    showImage(image);

    index++;
}
//设置警报
void AdClient::GUI_WarningMsg(QString title, QString text, QString buttons, QString defaultButton)
{
    
    
    Q_UNUSED(buttons)//忽略编译器发出的警告,表明变量event未使用
    Q_UNUSED(defaultButton)
    QMessageBox *box=new QMessageBox(QMessageBox::Warning,title,text,QMessageBox::Cancel,this);
    QTimer::singleShot(5000,this,[=](){
    
    
        if(!box->isHidden()&&box->isVisible())
        {
    
    
            box->accept();
        }
    });// 5s后必执行
    box->exec();
    return;
}

// 链接到服务器
void AdClient::connectToServer()
{
    
    
    if(serverIP->text().isEmpty()||serverPort->text().isEmpty())
    {
    
    
        QMessageBox::warning(this,"提示","请输入IP或Port");
        return;
    }
    socket->id=GetLocalIP();
    socket->address=serverIP->text();
    qDebug()<<"服务器IP:"<<socket->address;
    socket->port=serverPort->text().toInt();
    qDebug()<<"服务器Port:"<<socket->port;

    qDebug()<<"是否已经连接:"<<socket->isValid();
    if(socket->isValid())// 如果套接字有效并且可以使用,则返回true;否则返回false。
    {
    
    
        socket->disconnectFromHost();// 与服务器断开连接
        // 无需再手动连接,只要更新IP和Port就行,AdSocket::conAgain会重新发起连接.

    }
    else
    {
    
    
        socket->connectToHost(QHostAddress(socket->address), socket->port);
    }
    qDebug()<<"是否已经连接:"<<socket->isValid();
}

addate.h 时间处理

#ifndef ADDATE_H
#define ADDATE_H

#include <QTime>
#include <QDate>
#include <QLabel>
#include <QTimer>

class AdDate : public QObject
{
    
    
    Q_OBJECT
public:
    AdDate(QLabel *_mlabel, QObject *parent = 0);
    ~AdDate();
    void start();// 定时器开启
public slots:
    void updateTime();// 定时器1s超时执行一次
private:
    QLabel *mlabel;
    QTimer *mtimer;
};

#endif // ADDATE_H

addate.cpp 时间处理

#include "addate.h"

AdDate::AdDate(QLabel *_mlabel, QObject *parent):
    QObject(parent)
{
    
    
    mlabel = _mlabel;
    mtimer = new QTimer;
    connect(mtimer, SIGNAL(timeout()), this, SLOT(updateTime()));
}

AdDate::~AdDate()
{
    
    
    delete mtimer;
}

void AdDate::start()
{
    
    
     mtimer->start(1000);
}

void AdDate::updateTime()
{
    
    
    QString time = QTime::currentTime().toString("hh:mm:ss")+"\n"
            +QDate::currentDate().toString("yy/MM/dd ddd");

    mlabel->setText(time);
}

adsocket.h 客户端Socket处理

#ifndef ADSOCKET_H
#define ADSOCKET_H

#include <QTcpSocket>
#include <QHostAddress>
#include <QAbstractSocket>
#include <QAbstractSocket>
#include <QImage>
#include <QLabel>
#include <QBuffer>
#include <QTime>
#include "string.h"
#include "tcp_MSG.h"



class AdSocket : public QTcpSocket
{
    
    
    Q_OBJECT
public:
    explicit AdSocket(QObject *parent = 0);
    ~AdSocket();

    QString id;
    QString address;
    int port;
    int readMsgType;

signals:
    void sig_showWeather(QString city,QString area,QString wt);// 发送天气信息
    void sig_showTxt(QString tcp_Txt);// 发送文字信息
    void sig_showImage(QImage tcp_image);// 发送文字信息
    void sig_showProgressBar(int currentProgress, int finish);// 发送进度条信息
    void GUI_WarningSignal(QString title,QString text,QString buttons,QString defaultButton);//设置警报

public slots:
    void readMsg();// 接收信息
    void conSuc(); // 成功建立连接
    void conAgain();// 重新连接
    void conAgain(QAbstractSocket::SocketError);// 重新连接


private:
    int needFileSize;
    int currentReceiveSize;
    QByteArray currentReceiveByte;
    int milsec;
    QString fileName;
};

#endif // ADSOCKET_H

adsocket.cpp 客户端Socket处理

#include "adsocket.h"
#include <QDebug>

extern QMap<QString,QImage> qMapPicturePath;// 图片路径

// QTcpSocket会自动处理大小端问题
AdSocket::AdSocket(QObject *parent) :
    QTcpSocket(parent)
{
    
    
    //注册tcp_MSG类型
    qRegisterMetaType<tcp_MSG>("tcp_MSG");

    id = "0011";
    /* 每当有新的输入数据时,就会发出这个信号。
    请记住,新传入的数据只报告一次;如果您不读取所有数据,这个类会缓冲数据,您可以稍后读取它,但是除非新数据到达,否则不会发出信号。*/
    connect(this, SIGNAL(readyRead()),this, SLOT(readMsg()));

    //该信号在调用connectToHost()并成功建立连接之后发出。
    connect(this, SIGNAL(connected()),this, SLOT(conSuc()));

    // 该信号在套接字断开连接时发出
    connect(this, SIGNAL(disconnected()),this, SLOT(conAgain()));

    address="127.0.0.1";
    port=8888;

    //connectToHost(QHostAddress(address), port);// 默认连接127.0.0.1 8888

    //该信号在错误发生后发出。socketError参数描述发生错误的类型
    connect(this, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(conAgain(QAbstractSocket::SocketError)));

    readMsgType=MsgType::Init;

    milsec=0;
}

AdSocket::~AdSocket()
{
    
    

}

// 接收信息
void AdSocket::readMsg()
{
    
    
    //读取缓冲区数据
    QByteArray  buffer = readAll();
    qDebug()<<"进来了"<<__LINE__;
    if(readMsgType==MsgType::Init)
    {
    
    
        tcp_MSG *msg=(tcp_MSG *)buffer.data();        //强转为结构体,需要用结构体指针接收
        qDebug()<<"进来了"<<__LINE__;
        qDebug()<<"类型"<<msg->type;
        switch (msg->type)
        {
    
    
        case WEATHER://天气
        {
    
    
            QString city=msg->city;
            QString area=msg->area;
            QString weather=msg->weather;
            //weather="\t\t"+city+"\t"+area+"\n"+weather;
            emit sig_showWeather(city,area,weather);// 发送天气信息
            break;
        }
        case MASSEGE:// 留言
        {
    
    
            QString tcp_Txt=msg->txt;
            qDebug()<<"文字信息:"<<tcp_Txt;
            emit sig_showTxt(tcp_Txt);// 发送文字信息
            break;
        }
        case VIDEO:// 视频
            qDebug()<<"发送视频信息:";
            break;
        case AD_add://添加广告
        {
    
    
            qDebug()<<"添加广告"<<__LINE__;
            qDebug()<<"文件名"<< QString(msg->fileName);
            qDebug()<<"编号"<< msg->index;
            qDebug()<<"总数"<< msg->allAd_Num;

            needFileSize=msg->fileSize;
            currentReceiveSize=0;
            currentReceiveByte.clear();
            QByteArray  sendTcpData;
            //使用字节数组,将结构体转为字符数组,发送的是字符数组(数据在传输过程中都是byte类型的)
            //直接sizeof(senddata)内存会变小,设置了对齐方式解决
            sendTcpData.resize(sizeof(tcp_backMSG));
            tcp_backMSG backMsg={
    
    };
            strcpy(backMsg.id,id.toUtf8().data());
            backMsg.state=1;
            backMsg.type=MsgType::Write_back;
            //将封装好的结构体转为QByteArray数组,因为传输都是Byte类型
            memcpy(sendTcpData.data(),&backMsg,sizeof(tcp_backMSG));

            this->write(sendTcpData);// 回复

            fileName.clear();
            fileName=QString(msg->fileName);

            readMsgType=MsgType::AD_add;
            break;
        }
        case AD_delete://删除广告
        {
    
    
            qDebug()<<"删除广告"<<__LINE__;
            qDebug()<<"文件名"<< QString(msg->fileName);
            int  success= qMapPicturePath.remove(QString(msg->fileName));
            if(success)
            {
    
    
                if(qMapPicturePath.count()>=1)
                {
    
    // 有图片
                    emit sig_showImage(qMapPicturePath.first());
                }
                else
                {
    
    // 没有图片
                    QImage image(100, 100, QImage::Format_RGBA8888);
                    image.fill(QColor(0, 0, 0, 0));// 纯透明图片
                    emit sig_showImage(image);
                }
                emit GUI_WarningSignal("提示","删除广告成功",NULL,NULL);
            }
            else
            {
    
    
                emit GUI_WarningSignal("提示","删除广告失败",NULL,NULL);
            }
            break;
        }
        default:
            qDebug()<<"";
            break;
        }
    }
    else if(readMsgType==MsgType::AD_add)
    {
    
    
        qDebug()<<"添加广告"<<__LINE__;
        qDebug()<<"需要接收大小:"<<needFileSize;
        // 记录开始时间
        QTime startTime = QTime::currentTime();

        currentReceiveSize+=buffer.size();
        currentReceiveByte+=buffer;
        qDebug()<<"当前接收大小:"<<currentReceiveSize;
        emit sig_showProgressBar(currentReceiveSize,needFileSize);
        // 记录结束时间
        QTime endTime = QTime::currentTime();
        // 计算运行时间
        milsec += startTime.msecsTo(endTime);

        if(needFileSize==currentReceiveSize)
        {
    
    
            qDebug()<<"图片接收完成";
            startTime = QTime::currentTime();

            QByteArray Ret_bytearray = QByteArray::fromBase64(currentReceiveByte);
            QBuffer buffer(&Ret_bytearray);
            buffer.open(QIODevice::WriteOnly);
            QPixmap imageresult;
            imageresult.loadFromData(Ret_bytearray);

            qMapPicturePath.insert(fileName,imageresult.toImage());// 先插入键值
            emit sig_showImage(imageresult.toImage());

            readMsgType=MsgType::Init;

            endTime = QTime::currentTime();
            milsec += startTime.msecsTo(endTime);
            qDebug()<<"接收消耗时间"<<milsec<<"毫秒";
        }
    }
}
// 成功建立连接
void AdSocket::conSuc()
{
    
    
    QByteArray  sendTcpData;
    //使用字节数组,将结构体转为字符数组,发送的是字符数组(数据在传输过程中都是byte类型的)
    //直接sizeof(senddata)内存会变小,设置了对齐方式解决
    sendTcpData.resize(sizeof(tcp_backMSG));
    tcp_backMSG msg={
    
    };
    strcpy(msg.id,id.toUtf8().data());
    msg.state=0;
    msg.type=MsgType::Init;
    //将封装好的结构体转为QByteArray数组,因为传输都是Byte类型
    memcpy(sendTcpData.data(),&msg,sizeof(tcp_backMSG));

    this->write(sendTcpData);
    qDebug()<<"connect success";
}

// 重新连接
void AdSocket::conAgain()
{
    
    
    qDebug()<<"套接字断开连接时重新连接";
    abort();// 终止当前连接并重置套接字
    connectToHost(QHostAddress(address), port);
}
// 重新连接
void AdSocket::conAgain(QAbstractSocket::SocketError error)
{
    
    
    qDebug()<<"连接失败:"<<error;
    readMsgType=MsgType::Init;
    abort();// 终止当前连接并重置套接字

    if(error==QAbstractSocket::ConnectionRefusedError)
    {
    
    
        connectToHost(QHostAddress(address), port);
    }
}

weather.h 天气信息处理

#ifndef WEATHER_H
#define WEATHER_H

#include <QObject>
#include <QLabel>
#include <QDebug>
class Weather : public QObject
{
    
    
    Q_OBJECT
public:
    explicit Weather( QLabel *_label,QObject *parent = 0);

signals:

public slots:
    void showWeather(QString city,QString area,QString weather);

private:
    QLabel *label;
    QString _city;
    QString _area;
};

#endif // WEATHER_H

weather.cpp 天气信息处理

#include "weather.h"

Weather::Weather( QLabel *_label, QObject *parent) :
    QObject(parent)
{
    
    
    label = _label;
}

void Weather::showWeather(QString city,QString area,QString weather)
{
    
    
    _city=city;
    qDebug()<<"城市:"<<_city;
    _area=area;
    qDebug()<<"地区:"<<_area;
    weather="\t\t"+_city+"\t"+_area+"\n\t"+weather;
    label->setText(weather);
}

rollmassege.h 滚动信息处理

#ifndef ROLLMASSEGE_H
#define ROLLMASSEGE_H

#include <QLabel>
#include <QEvent>
#include <QTimer>
#include <QRect>
#include <QPainter>
#include <QFont>

class RollMassege : public QLabel
{
    
    
    Q_OBJECT
public:
    explicit RollMassege(QWidget *parent = 0);
    explicit RollMassege(QLabel *_label, QWidget *parent = 0);

    void paintEvent(QPaintEvent *event);
signals:

public slots:
    void updateMsg();
    void showTxt(QString tcp_Txt);
private:
    QString txt;
    QRect rect;
    int offset;//偏移量
    QTimer *timer;
};

#endif // ROLLMASSEGE_H

rollmassege.cpp 滚动信息处理

#include "rollmassege.h"
#include <QDebug>

RollMassege::RollMassege(QWidget *parent) :
    QLabel(parent)
{
    
    
}

RollMassege::RollMassege(QLabel *_label, QWidget *parent) :
    QLabel(parent)
{
    
    
    rect = _label->geometry();
    rect.setWidth(798);
    rect.setHeight(80);
    setGeometry(rect);
    show();
    qDebug()<<rect.width()<<" "<<rect.height();

    offset = rect.width();//偏移量
    txt = "暂无信息";
    timer = new QTimer;
    connect(timer, SIGNAL(timeout()), this, SLOT(updateMsg()));
    timer->start(50);
}
// 绘画事件
void RollMassege::paintEvent(QPaintEvent *event)
{
    
    
    Q_UNUSED(event); //忽略编译器发出的警告,表明变量event未使用
    QPainter painter(this);
    QFont font;
    font.setPointSize(16);
    painter.setFont(font);
    painter.drawText(rect.x()+offset, rect.y()+30, txt);// 绘制文字,x+偏移量
}


void RollMassege::updateMsg()
{
    
    
    offset--;
    if(offset<0) offset = rect.width();
    update();// 刷新,触发绘画事件paintEvent
}

void RollMassege::showTxt(QString tcp_Txt)
{
    
    
    txt =tcp_Txt;
    update();
}

ui

在这里插入图片描述

效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

源码

有道云:

难点

  1. QTcpSocket发送和接收使用 自定义 信息结构体,

结构体需要1字节对齐 ,参考Qt 利用TCP/IP socket通信 发送与接收结构体(简单通信协议解析)

  • 发送
QByteArray  sendTcpData;
//使用字节数组,将结构体转为字符数组,发送的是字符数组(数据在传输过程中都是byte类型的)
//直接sizeof(senddata)内存会变小,设置了对齐方式解决
sendTcpData.resize(sizeof(tcp_MSG));

//将封装好的结构体转为QByteArray数组,因为传输都是Byte类型
memcpy(sendTcpData.data(),&msg,sizeof(tcp_MSG));

socket->write(sendTcpData);
  • 接收
//读取缓冲区数据
QByteArray  buffer = readAll();

tcp_MSG *msg=(tcp_MSG *)buffer.data();        //强转为结构体,需要用结构体指针接收
  1. 发送图片

QTcpSocket 的默认缓存区大小是 64KB(65536字节)

图片一般比较大,需要循环接收,校验发送长度和接收长度

因为QTcpSocket是一个基于字节流的套接字,它只能传输二进制数据。而图片文件是一种二进制文件,不能直接传输。因此,需要将图片文件转换为一种可传输的文本格式,如Base64编码。

Base64编码是一种将二进制数据转换为ASCII字符的编码方式。它将每3个字节转换为4个字符,因此可以将任何二进制数据转换为一种文本格式,方便传输

本项目发送图片,使用 服务器下发消息类型,客户端回复并开启图片接收; 服务器 把图片发给 回复的客户端;

  • 发送
*在adtcp.cpp的Ad_SendAction()中先下发消息类型
{
    
    
	QFileInfo file(path);
	QImage image(path);
	QByteArray byteArray;
	QBuffer buffer(&byteArray);
	buffer.open(QIODevice::WriteOnly);
	
	//获取文件的后缀名,并将其转换为大写字母
	image.save(&buffer,file.suffix().toUpper().toStdString().c_str()); //将图片保存为PNG/JPG等格式
	
	sendImage = byteArray.toBase64();
	
	msg.fileSize=sendImage.size();
	
	buffer.close();
}

*在adtcp.cpp的read_back()中先下发消息类型
{
    
    
	// 返回此信号的 发送对象
    QTcpSocket *getSocket=qobject_cast<QTcpSocket *>(sender());
    //读取缓冲区数据
    QByteArray  buffer = getSocket->readAll();//client_list->last()->msocket->readAll();// 读取最后客户端(也就是最新的)
    tcp_backMSG *msg=(tcp_backMSG *)buffer.data();        //强转为结构体,需要用结构体指针接收
    
	else if(msg->type==MsgType::Write_back&&msg->state==1)
    {
    
    
        getSocket->write(sendImage);
        // 用于等待发送的数据被写入到网络套接字描述符中,这样就可以确保数据完全被发送出去,
        //该函数会阻塞程序继续执行,直到数据被完全发送出去
        if (getSocket->waitForBytesWritten())
        {
    
    
            getSocket->flush(); //释放socket缓存
        }
    }
 }
  • 接收
* 在adsocket.cpp的readMsg()先回复并开启图片接收
{
    
    
    //读取缓冲区数据
    QByteArray  buffer = readAll();
    tcp_MSG *msg=(tcp_MSG *)buffer.data();        //强转为结构体,需要用结构体指针接收
    needFileSize=msg->fileSize;// 需要接收图片大小
    
	QByteArray  sendTcpData;
	//使用字节数组,将结构体转为字符数组,发送的是字符数组(数据在传输过程中都是byte类型的)
	//直接sizeof(senddata)内存会变小,设置了对齐方式解决
	sendTcpData.resize(sizeof(tcp_backMSG));
	tcp_backMSG backMsg={
    
    };
	strcpy(backMsg.id,id.toUtf8().data());
	backMsg.state=1;
	backMsg.type=MsgType::Write_back;
	//将封装好的结构体转为QByteArray数组,因为传输都是Byte类型
	memcpy(sendTcpData.data(),&backMsg,sizeof(tcp_backMSG));
	
	this->write(sendTcpData);// 回复
}

* 在adsocket.cpp的readMsg()图片接收
{
    
    
	QByteArray  buffer = readAll();
	qDebug()<<"需要接收大小:"<<needFileSize;
	currentReceiveSize+=buffer.size();
	currentReceiveByte+=buffer;//当前累计接收大小
	
	if(needFileSize==currentReceiveSize)
	{
    
    qDebug()<<"图片接收完成";
	   QByteArray Ret_bytearray = QByteArray::fromBase64(currentReceiveByte);
	   QBuffer buffer(&Ret_bytearray);
	   buffer.open(QIODevice::WriteOnly);
	   QPixmap imageresult;
	   imageresult.loadFromData(Ret_bytearray);

	   QImage pic=imageresult.toImage();
	}
}

猜你喜欢

转载自blog.csdn.net/qq_47355554/article/details/129378136