Linux系统开发|QT制作聊天软件实验报告

一、实验目的
设计一款携带多种功能的聊天软件,不仅可以聊天,也能修改字体,计时,绘画等等。
二、实验内容:
1)ui设计(满分15分)
2)画图、timer、多线程(满分20分)
3)数据库操作(满分15分)
4)TCP网络通信(满分40分) (客户端15分,多线程服务器25分)

三、实验环境

Qt Creator 4.2.1 (Community)

四、实验过程与运行结果
1)ui设计
Linux系统开发|QT制作聊天软件实验报告_第1张图片
图表 1聊天界面

头像是在网络上找的,其余图标都是自己绘制的,但由于设计时间过早,在后面完成代码的时候发现没有信息的数据库,所以这里的了解成员要改为查找地址。
Linux系统开发|QT制作聊天软件实验报告_第2张图片
图表 2设计界面2
这是设计的第二个界面,但是本来想要管理员链接服务端,但是因为项目的问题一直链接不上,排查问题找了很久都没有找到问题,所以这里设计的按钮其实是无法跳转的,所以就改为第一次的设计。
Linux系统开发|QT制作聊天软件实验报告_第3张图片
图表 3绘画界面
Linux系统开发|QT制作聊天软件实验报告_第4张图片
图表 4客户端界面
Linux系统开发|QT制作聊天软件实验报告_第5张图片
图表 5服务端界面

2)画图、timer、多线程
Linux系统开发|QT制作聊天软件实验报告_第6张图片
图表 6多线程聊天
可进行多人聊天,只要打开窗口就能够聊天,但如果其他人要接收的话,也需要打开聊天框,不然就不能够接收到信息。
Linux系统开发|QT制作聊天软件实验报告_第7张图片
图表 7保存聊天记录
这里为了合理化也设计了字体样式的更改,保存,清空等等,但是颜色字号因为不太理解所以没有做成功,但加粗等成功了。
Linux系统开发|QT制作聊天软件实验报告_第8张图片
图表 8保存的聊天记录
Linux系统开发|QT制作聊天软件实验报告_第9张图片
图表 9修改字体样式
Linux系统开发|QT制作聊天软件实验报告_第10张图片
图表 10警告
为了不出现重复聊天框的BUG,所以要设置一个警告。
Linux系统开发|QT制作聊天软件实验报告_第11张图片

图表 11绘图界面
这里只绘制了一个圈和字体,但也可以实现其他的绘画,比如画直线等等,但需要在代码中加入,这里没有加是因为我觉得这样设计会比较好看。
Linux系统开发|QT制作聊天软件实验报告_第12张图片

图表 12绘画界面弹出
只要点击来画画吧,就会弹出绘画的这个窗口,这里本来想设计成后面的那些窗口都消失,但发现这样不太明显,就将那条代码删除了。
Linux系统开发|QT制作聊天软件实验报告_第13张图片

图表 13计时器
点击计时器按钮,就会跳转到计时器的这个页面来,点击开始就能够开始计时,停止就停止计时,重置就是重新开始。
Linux系统开发|QT制作聊天软件实验报告_第14张图片

图表 14点击重置后
点击重置后,时间就归零了。
3)数据库操作
Linux系统开发|QT制作聊天软件实验报告_第15张图片

图表 15数据库登录
这里是老师上课讲解的内容,这里进行了一个结合,只要点击右下方的按钮,就能够弹出这个页面,也就是“小蜜蜂数据”,接着就能够链接数据库查询信息了。
Linux系统开发|QT制作聊天软件实验报告_第16张图片

图表 16查询信息页面
因为没有数据库,所以这里用的是老师之前教课时所使用的,自己也尝试建立数据库,发现了很多问题,诸如数据的问题,要创建ID类但是一直无法设置好,导致程序出现故障等等。
Linux系统开发|QT制作聊天软件实验报告_第17张图片

图表 17重新建立数据库界面
因为原本数据库不太适合我的聊天软件,于是进行了更改,这样就能够查询好友的信息,比较匹配我的聊天软件。
4)TCP网络通信
Linux系统开发|QT制作聊天软件实验报告_第18张图片

图表 18同时打开服务端和客户端
能够运行服务端和客户端,界面上的LOGO是我设计的小蜜蜂。
Linux系统开发|QT制作聊天软件实验报告_第19张图片

图表 19多线程链接
两个客户端能够和服务端链接并且发送消息。
Linux系统开发|QT制作聊天软件实验报告_第20张图片

图表 20单线程链接
以下是主代码展示:【代码不全】

1)Widegt.cpp

#include "widget.h"
#include "ui_widget.h"
#include"draw.h"
#include "mytime.h"
#include "mydb.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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

    this->ppage2=new draw;//实例化绘画页面S
    connect(ui->drawBtn,&QPushButton::clicked,[=](){
        //this->hide();
        this->ppage2->show();//显示
    });

    this->ppage3=new mytime;
    connect(ui->timerBtn,&QPushButton::clicked,[=](){
        this->ppage3->show();
    });

    this->ppage4=new mydb;
    connect(ui->dataBtn,&QPushButton::clicked,[=](){
        this->ppage4->show();
    });



    this->port=0032;
    this->udpSocket=new QUdpSocket(this);

    udpSocket->bind(this->port,QUdpSocket::ShareAddress |QUdpSocket::ReuseAddressHint);

    connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::ReceiveMessage);
    connect(ui->sendBtn,&QPushButton::clicked,[=](){
        sndMsg(Msg);
    });
}

void Widget::sndMsg(Widget::Msgtype type)
{
    QByteArray array;
    QDataStream stream(&array,QIODevice::WriteOnly);
    stream<<type<<this->getName();

    switch(type){
    case Msg:
        if(ui->msgTxtEdit->toPlainText()=="")
        {
            QMessageBox::warning(this,"警告","发送的内容不能为空!");
            return;
        }
        stream<<this->getMsg();
        break;
    case UserEnter:
        break;
    case UserLeft:
        break;
    }
    udpSocket->writeDatagram(array.data(),array.size(),QHostAddress::Broadcast,this->port);
}

QString Widget::getName()
{
    return this->myname;
}

QString Widget::getMsg()
{
    QString msg=ui->msgTxtEdit->toHtml();
    ui->msgTxtEdit->clear();
    ui->msgTxtEdit->setFocus();
    return msg;
}



void Widget::ReceiveMessage()
{
    qint64 size=udpSocket->pendingDatagramSize();
    int mysize=static_cast<int>(size);
    QByteArray *array=new QByteArray(mysize,0);
    udpSocket->readDatagram((*array).data(),size);
    QDataStream stream(array,QIODevice::ReadOnly);
    int mytype;
    QString name,msg;//聊天内容 用户名
    QString time=QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
    stream>>mytype;
    switch(mytype){
    case Msg:
        stream>>name>>msg;
        ui->msgBrowser->setTextColor(QColor(Qt::blue));
        ui->msgBrowser->setCurrentFont(QFont("Times New Roman",10));
        ui->msgBrowser->append(("["+name+"]"+time));
        ui->msgBrowser->append(msg);
        break;
    case UserEnter:
        stream>>name;
//        userEnter(name);
        break;
    case UserLeft:
        stream>>name;
//        userLeft(name,time);
        break;
    }
    //字体改变
    connect(ui->fontCbx,&QFontComboBox::currentFontChanged,[=](const QFont &font){
        ui->msgTxtEdit->setFontFamily(font.toString());
        ui->msgTxtEdit->setFocus();
    });
    //字体大小
    void (QComboBox:: *sizebtn)(const QString &text)=&QComboBox::currentTextChanged;
    connect(ui->sizeCbx,sizebtn,[=](const QString &text){
       ui->msgTxtEdit->setFontPointSize(text.toDouble());
       ui->msgTxtEdit->setFocus();
    });
    //加粗
    connect(ui->boldTBtn,&QToolButton::clicked,this,[=](bool checked){
        if(checked)
        {
            ui->msgTxtEdit->setFontWeight(QFont::Bold);
        }
        else{
            ui->msgTxtEdit->setFontWeight(QFont::Normal);
        }
    });
    //倾斜
    connect(ui->italicTbtn,&QToolButton::clicked,this,[=](bool checked){
        ui->msgTxtEdit->setFontItalic(checked);
        ui->msgTxtEdit->setFocus();
    });
    //下划线
    connect(ui->underlineTBtn,&QToolButton::clicked,this,[=](bool checked){
        ui->msgTxtEdit->setFontUnderline(checked);
        ui->msgTxtEdit->setFocus();
    });
    //清除
    connect(ui->clearTBtn,&QToolButton::clicked,[=](){
        ui->msgBrowser->clear();
    });
    //颜色
    connect(ui->colorTBtn,&QToolButton::clicked,this,[=](){
        QColor color=QColorDialog::getColor(color,this);
        ui->msgBrowser->clear();
    });
    //保存
    connect(ui->saveTBtn,&QToolButton::clicked,[=](){
        if(ui->msgBrowser->document()->isEmpty())
        {
            QMessageBox::warning(this,"警告","保存内容不能为空!");
            return;
        }
        else{
            QString filename=QFileDialog::getSaveFileName(this,"保存聊天记录","聊天记录","(*.txt");
            if(!filename.isEmpty())
            {
                QFile file(filename);
                file.open(QIODevice::WriteOnly |QFile::Text);
                QTextStream stream(&file);
                stream<<ui->msgBrowser->toPlainText();
                file.close();
            }
        }
    });
}

void Widget::closeEvent(QCloseEvent *)
{
    emit this->closeWidget();
}

Widget::~Widget()
{
    delete ui;
}2)Wedget.h

#ifndef WIDGET_H
#define WIDGET_H
#include "draw.h"
#include 
#include 
#include "mytime.h"
#include "mydb.h"


namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    enum Msgtype{Msg,UserEnter,UserLeft};
    explicit Widget(QWidget *parent, QString name);

    void sndMsg(Msgtype type);
    QString getName();
    QString getMsg();
    QString getIcon();
    void userEnter(QString username);
    void userLeft(QString username,QString time);
    void ReceiveMessage();

    ~Widget();
    draw *ppage2=NULL;//用来保存绘画界面的实例化对象地址
    mytime *ppage3=NULL;//计时器
    mydb *ppage4=NULL;//数据库


signals:
    void closeWidget();
    void showmain();

private:
    Ui::Widget *ui;
    quint16 port;
    QString myname;
    QUdpSocket *udpSocket;
public:
    void closeEvent(QCloseEvent *);

};

#endif // WIDGET_H3)denglu.cpp

#include "denglu.h"
#include "ui_denglu.h"
#include 
#include 
#include 
#include 
#include 
#include 
denglu::denglu(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::denglu)
{
    ui->setupUi(this);
    //图标
    this->setWindowIcon(QIcon(":/image/bee.PNG"));
    //名称
    this->setWindowTitle("小蜜蜂聊天");
    QList<QString> namelist;
    namelist<<"喝饮料的小熊猫"<<"今天很开心"<<"保佑及格!"<<"天选锦鲤在哪里"<<"好好学习天天向上";
    QStringList iconNameList;
    iconNameList<<"1"<<"2"<<"3"<<"4"<<"5";
    QVector<QToolButton *> vector;
    for(int i=0;i<5;i++)
    {
        QToolButton *btn=new QToolButton(this);
        //头像
        btn->setIcon(QPixmap(QString(":/image/%1.png").arg(iconNameList[i])));
        btn->setIconSize(QPixmap((QString(":/image/%1.png").arg(iconNameList[i]))).size());
        btn->setText(QString("%1").arg(namelist[i]));
        btn->setAutoRaise(true);
        btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
        ui->vlayout_2->addWidget(btn);
        vector.push_back(btn);
        IsShow.push_back(false);
    }
    for(int i=0;i<5;i++)
    {
        connect(vector[i],&QToolButton::clicked,[=](){
            if(IsShow[i])
            {
                QMessageBox::warning(this,"警告","已有相关聊天框,请勿重复点击!");
                return;
            }
            IsShow[i]=true;
            Widget *widget=new Widget(nullptr,vector[i]->text());
            widget->setWindowIcon(vector[i]->icon());
            widget->setWindowTitle(vector[i]->text());
            widget->show();

            connect(widget,&Widget::closeWidget,this,[=](){
                IsShow[i]=false;
            });
        });
    }
}

denglu::~denglu()
{
    delete ui;
}4)denglu.h

#ifndef DENGLU_H
#define DENGLU_H

#include 

namespace Ui {
class denglu;
}

class denglu : public QDialog
{
    Q_OBJECT

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

private:
    Ui::denglu *ui;
    QVector<bool> IsShow;
};

#endif // DENGLU_H5)draw.cpp

#include "draw.h"
#include "ui_draw.h"
#include 
#include"qpainter.h"
draw::draw(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::draw)
{
    ui->setupUi(this);
    this->setWindowIcon(QIcon(":/image/bee.PNG"));
    this->setWindowTitle("小蜜蜂绘画");
}

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

void draw::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

       QPainter painter(this);
       // 设置画笔颜色
       painter.setPen(QColor(0, 160, 230));

       // 设置字体:微软雅黑、点大小50、斜体
       QFont font;
       font.setFamily("Microsoft YaHei");
       font.setPointSize(50);
       font.setItalic(true);
       painter.setFont(font);

       // 绘制文本
       painter.drawText(rect(), Qt::AlignCenter, "小蜜蜂");

       Q_UNUSED(event);
         painter.begin(this);
         QRectF rectangle(150.0, 100.0, 280.0, 240.0);//如果是一个正方形那就是一个圆
         painter.drawEllipse(rectangle);
         painter.end();



}6)mytime.cpp

#include "mytime.h"
#include "ui_mytime.h"
#include 
#include 
#include 

mytime::mytime(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::mytime)
{
    ui->setupUi(this);
    this->setWindowIcon(QIcon(":/image/bee.PNG"));
    this->setWindowTitle("小蜜蜂计时器");
    time.setHMS(0,0,0,0);//设置初始值
    timer = new QTimer(this);//创建一个定时器
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));
}

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

void mytime::update()
{
    static quint32 time_out=0;
            time_out++;
            time=time.addSecs(1);
            ui->label->setText(time.toString("hh:mm:ss"));

}

void mytime::on_startBtn_clicked()
{
    timer->start(1000);
}

void mytime::on_stopBtn_clicked()
{
   timer->stop();
}

void mytime::on_reBtn_clicked()
{
    timer->stop();
    time.setHMS(0,0,0,0);
    ui->label->setText(time.toString("hh:mm:ss"));
}7)mserver.cpp

#include "mserver.h"
#include "ui_mserver.h"

mServer::mServer(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::mServer)
{
    ui->setupUi(this);
    this->setWindowIcon(QIcon(":/image/bee.PNG"));
    this->setWindowTitle("小蜜蜂服务端");
    list_sock.clear();
    mserver=new QTcpServer();
    connect(mserver,SIGNAL(newConnection()),this,SLOT(acceptListen()));

    ui->tabClient->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    mtype=msize=0;
    mdata.clear();
}

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

void mServer::sendData()
{
    int rows=ui->tabClient->rowCount();
    if(rows<1) return;
    int row=ui->tabClient->currentIndex().row();
    list_sock[row]->write(mdata);
    mdata.clear();
}

void mServer::processData(int id)
{

}

void mServer::acceptListen()
{
    QTcpSocket *sock=mserver->nextPendingConnection();
    list_sock.append(sock);
    connect(sock,SIGNAL(readyRead()),this,SLOT(recvData()));

    int rows=ui->tabClient->rowCount();
    ui->tabClient->insertRow(rows);
    ui->tabClient->setItem(rows,0, new QTableWidgetItem(sock->peerAddress().toString()));
    ui->tabClient->setItem(rows,1, new QTableWidgetItem(QString::number(sock->peerPort())));
    QString str=QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
    ui->tabClient->setItem(rows,2, new QTableWidgetItem(str));
}

void mServer::recvData()
{
    QTcpSocket *sock = (QTcpSocket*)sender();
    for(int i=0;i<list_sock.length();i++)
    {
        if(sock == list_sock[i])
        {
            QString str=sock->peerAddress().toString()+"---"+QString::number(sock->peerPort())+":";
            QString str1 = QString::fromLocal8Bit(sock->readAll());
            ui->textEdit->append(str);
            ui->textEdit->append(str1);
        }
    }
}

void mServer::on_btn_listen_clicked()
{
    if(!mserver->listen(QHostAddress::Any,22233))
    {
        qDebug()<<mserver->errorString();//>>log.txt
        return;
    }
}

void mServer::on_btn_discon_clicked()
{
    mserver->close();
    for(int i=0;i<list_sock.length();i++)
    {
        list_sock[i]->abort();  //disconnect
    }
    list_sock.clear();

    exit(0);
}

void mServer::on_btn_sendMsg_clicked()
{
    QDataStream out(&mdata,QIODevice::WriteOnly); //memcpy
    out.setVersion(QDataStream::Qt_5_8);
    mtype=101; //101 102 103   201 202
    QString str=ui->text_send->text();
    QByteArray ba=str.toLocal8Bit();
    msize=ba.length();
    out<<mtype; out<<msize;
    mdata.append(ba);
    ba.clear();

    sendData();
}

void mServer::on_btn_sendFile_clicked()
{
    QString fpath=QFileDialog::getOpenFileName();
    if(fpath.isEmpty()) return;
    mtype= (fpath.endsWith("jpg"))?103:102;

    QFile mfile(fpath);
    QByteArray data;
    if(!mfile.isOpen())
    { mfile.open(QIODevice::ReadOnly);
        data = mfile.readAll();
    }
    mfile.close();
    msize=data.length();

    mdata.clear();
    QDataStream out(&mdata,QIODevice::WriteOnly); //memcpy
    out.setVersion(QDataStream::Qt_5_8);
    out<<mtype; out<<msize;
    mdata.append(data);
    qDebug()<<mtype<<msize;

    sendData();
}8)mclient.cpp

#include "mclient.h"
#include "ui_mclient.h"

mclient::mclient(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::mclient)
{
    ui->setupUi(this);
    this->setWindowIcon(QIcon(":/image/bee.PNG"));
    this->setWindowTitle("小蜜蜂客户端");
    sock=new QTcpSocket();
    connect(sock,SIGNAL(readyRead()),this,SLOT(recvData()));
    size=recvsize=totalsize=mtype=0;
    send_data.clear();

    devid=locid=0;
    list_random.clear();
}

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

void mclient::processData()
{
    if(mtype == 101)
    {
        ui->text_recv->clear();
        ui->text_recv->append(QString::fromLocal8Bit(mdata));
    }
    if(mtype == 102)
    {
        ui->text_recv->clear();
        ui->text_recv->append(QString::fromLocal8Bit(mdata));
    }
    if(mtype == 103)
    {
        QBuffer buffer(&mdata);
        buffer.open( QIODevice::ReadOnly);

        QImageReader reader(&buffer, "jpg");
        QImage image = reader.read();
        QImage img=image.scaled(ui->labImg->rect().width(),ui->labImg->rect().height(),Qt::IgnoreAspectRatio);
        ui->labImg->clear();
        ui->labImg->setPixmap(QPixmap::fromImage(img));
    }

}

void mclient::recvData()
{
    if(sock->bytesAvailable()<8) return;  //type  size

    if(totalsize < 1)
    {
        QDataStream in(sock);
        in.setVersion(QDataStream::Qt_5_8);
        in>>mtype; in>>totalsize; mdata.clear(); //cont  25k
        qDebug()<<mtype;
    }
    if(recvsize<totalsize)
    {
        quint32 len=(quint32)sock->bytesAvailable();
        recvsize += len;
        mdata.append(sock->readAll());
        if(recvsize == totalsize)
        {
            processData();
        }
    }
}

void mclient::on_btn_con_clicked()
{
    sock->connectToHost(ui->textIP->text(),(quint16)ui->textPort->text().toUInt());
}

void mclient::on_btn_send_clicked()
{
    QDataStream out(&send_data,QIODevice::WriteOnly); //memcpy
    out.setVersion(QDataStream::Qt_5_8);
    // mtype=201; //101 102 103   201 202
    QString str=ui->lineEdit->text();
    QByteArray ba=str.toLocal8Bit();
    //    size=ba.length();
    //  out<
    send_data.clear();
    send_data.append(ba);
    ba.clear();

    sock->write(send_data);
    send_data.clear();
}

void mclient::on_btn_discon_clicked()
{
    sock->abort();
    exit(0);
}

void mclient::on_btn_gendata_clicked()
{
    devid=(quint8)(ui->text_devid->text().toInt());
    locid=(quint8)(ui->text_locid->text().toInt());

    float a=1.0; float b=100.0; mtype=size=0;

    for(int i=0;i<7;i++)
    {
        QTime t= QTime::currentTime();
        qsrand(t.msec()+t.second()*100);
        list_random.append(qrand());
        QThread::msleep(30);
    }

    float v1=(float)((450+list_random[0]%100)*a);
    float v2=(float)((20+list_random[1]%50)*a);
    float v3=(float)((100+list_random[2]%400)*a);
    float v4=(float)((5+list_random[3]%25)*a);
    float v5=(float)((5+list_random[4]%20)*a);
    float v6=(float)((26+list_random[5]%7)*a);
    float v7=(float)((85+list_random[6]%7)/b);

    send_data.clear();
    mtype=222;
    QByteArray ba; ba.clear();
    QDataStream out1(&ba,QIODevice::WriteOnly);
    out1.setVersion(QDataStream::Qt_5_8);
    out1<<devid<<locid<<v1<<v2<<v3<<v4<<v5<<v6<<v7;
    size=(quint32)ba.length();

    QDataStream out(&send_data,QIODevice::WriteOnly);
    out1.setVersion(QDataStream::Qt_5_8);
    out<<mtype<<size;
    send_data.append(ba);

    sock->write(send_data);
//    qDebug()<
    send_data.clear();

}

五、实验总结
在本次实践学习中,我运用上课学习到的知识进行了自己的设计制作,会发现当跟着老师的时候会觉得很轻松的事情,自己上手了却变得困难了,比如多线程这一块内容,跟着老师走的时候就会知道下一步应该走向哪里,但是自己打开的时候却经常报错,而且因为地址的问题要找很多的资料,反复去回忆老师所讲过的内容,因为要复制一些程序到源地址中,而这几个程序我印象不深,所以也导致了在这个问题上面浪费太多时间,这也让我决定以后类似于这样的知识点应该记录到笔记中去。
还有就是手动建槽的时候也要注意规范,之前因为想测试某个功能,忘记在头文件中加入槽,等反应过来之后再去添加槽,却发现还是运行不了,代码也找了很多参考,但是都没有能够找出问题,所以这一个内容我全部删除重新再打一次代码,而后就运行成功了,所以我也认识到规范性也是很重要的。
在最初的UI设计中我是参考别人的设计,和同学一起讨论设计出来的,但发现功能太少了,远远不符合要求,所以再翻找以前的学习代码去找该怎么实现我想要的功能,然后再进行设计,所以在最初设计的基础上做了很多变动,比如因为没有数据库,所以和我最先设想的查找好友资料也无法做出来,只能参考原来上课时候的学习代码进行制作,还有原本想要管理员按钮连接服务端进行跳转,却发现服务端需要单独开设一个项目,那如何把项目放在另一个项目下面我也是找了很多资料,但也一直无法实现,这也是我的一个遗憾,就是无法在一个项目中同时做到多个功能,只能在聊天的基础上搭载绘画,计时器,天气数据查询等的功能。
最后就是代码,其实我参考学习了很多资料,包括之前上课所讲的代码,我都有做参考,在这个过程中也重新学到了很多知识,也算是查缺补漏了,刚开始觉得这个作业很难,但后来一步一步一个一个功能慢慢实现之后,就会发现其实还是能够完成的,每次遇到难题我都会去请教舍友或者网上搜索资料,一步一步试错也让我曾经很崩溃,有时候一整天都在完成一个功能,虽然最后结果出来之后不是很惊艳,但是我觉得我已经做到了我的最好,也学习到了很多!

能做出这个软件,也很感谢:
B站up主:性感的大飞侠-【QT实现群聊聊天系统-哔哩哔哩】 https://b23.tv/yrhCLWL
唔国旗u【04_QT_页面切换(两个ui界面)-哔哩哔哩】 https://b23.tv/wEWbfqW

以上指导很有用!

实验结果存在较多BUG,很不完善!欢迎讨论!

你可能感兴趣的:(经验,qt,开发语言)