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

本文涉及的产品
应用型负载均衡 ALB,每月750个小时 15LCU
传统型负载均衡 CLB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 连接服务器(上位机)广告图片播放模块日期显示模块天气显示模块信息提示模块
Qt广告机服务器(上位机)

功能

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

image.png

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

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

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

结构

image.png

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

image.png

效果

image.png

image.png
image.png
image.png

源码

Gitee:08Qt-Advertising-Machine Qt广告机

难点

  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();
    }
}
相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
Web App开发 安全 网络协议
Qt开发技术:QWebSocket客户端、服务端介绍与开发
Qt开发技术:QWebSocket客户端、服务端介绍与开发
Qt开发技术:QWebSocket客户端、服务端介绍与开发
|
4月前
|
网络协议
【qt】TCP客户端信息的接受和发送
【qt】TCP客户端信息的接受和发送
36 0
|
4月前
|
网络协议
【qt】TCP客户端如何断开连接?
【qt】TCP客户端如何断开连接?
76 0
|
4月前
【qt】客户端连接到服务器
【qt】客户端连接到服务器
73 0
|
4月前
|
网络协议
【qt】TCP服务端发消息给客户端
【qt】TCP服务端发消息给客户端
39 0
|
7月前
|
监控 网络安全 C++
Qt 5.14.2 网络编程揭秘:构建高效HTTP客户端与文件下载器
Qt 5.14.2 网络编程揭秘:构建高效HTTP客户端与文件下载器
495 0
《QT从基础到进阶·十六》QT实现客户端和服务端的简单交互
《QT从基础到进阶·十六》QT实现客户端和服务端的简单交互
171 0
|
网络协议 C++ Python
Qt+C++ TCP发送接收信息客户端与服务端窗体
Qt+C++ TCP发送接收信息客户端与服务端窗体
151 0
Qt+C++ TCP发送接收信息客户端与服务端窗体
STM32+ESP8266+QT客户端上位机显示DHT11温湿度与点灯
STM32+ESP8266+QT客户端上位机显示DHT11温湿度与点灯