Qt版聊天软件

来源:互联网 发布:php 没有soapclient 编辑:程序博客网 时间:2024/04/29 18:24
18.1 核心功能界面演示

drawer.h

#ifndef DRAWER_H
#define DRAWER_H
#include "widget.h"
#include <QToolBox>
#include <QToolButton>
class Drawer : public QToolBox
{
    Q_OBJECT
public:
    Drawer(QWidget *parent=0,Qt::WindowFlags f=0);
private:
    QToolButton *toolBtn1;
    QToolButton *toolBtn2;
    QToolButton *toolBtn3;
    QToolButton *toolBtn4;
    QToolButton *toolBtn5;
    QToolButton *toolBtn6;
    QToolButton *toolBtn7;
    QToolButton *toolBtn8;
    QToolButton *toolBtn9;
private slots:
    void showChatWidget1();
    void showChatWidget2();
    void showChatWidget3();
    void showChatWidget4();
    void showChatWidget5();
    void showChatWidget6();
    void showChatWidget7();
    void showChatWidget8();
    void showChatWidget9();
private:
    Widget *chatWidget1;
    Widget *chatWidget2;
    Widget *chatWidget3;
    Widget *chatWidget4;
    Widget *chatWidget5;
    Widget *chatWidget6;
    Widget *chatWidget7;
    Widget *chatWidget8;
    Widget *chatWidget9;
};
#endif // DRAWER_H
drawer.cpp
#include "drawer.h"
#include <QGroupBox>
#include <QVBoxLayout>
class QString rsrcPath = ":/images";
Drawer::Drawer(QWidget *parent, Qt::WindowFlags f)
    :QToolBox(parent,f)
{
    setWindowTitle (tr("Myself QQ 2015"));
    setWindowIcon (QPixmap(rsrcPath + "/qq.png"));
    toolBtn1 = new QToolButton;
    toolBtn1->setText (tr(" 苦涩沙漠"));
    toolBtn1->setIcon (QPixmap(rsrcPath + "/Cherry.png"));
    toolBtn1->setIconSize (QPixmap(rsrcPath + "/Cherry.png").size () * 0.58);
    toolBtn1->setAutoRaise (true);  //当鼠标离开时,按钮自动恢复成弹起状态
    toolBtn1->setToolButtonStyle (Qt::ToolButtonTextBesideIcon);
    connect(toolBtn1, SIGNAL(clicked(bool)), this, SLOT(showChatWidget1()));
    toolBtn2 = new QToolButton;
    toolBtn2->setText (tr(" 飘逝的云"));
    toolBtn2->setIcon (QPixmap(rsrcPath + "/dr.png"));
    toolBtn2->setIconSize (QPixmap(rsrcPath + "/dr.png").size () * 0.58);
    toolBtn2->setAutoRaise (true);  //当鼠标离开时,按钮自动恢复成弹起状态
    toolBtn2->setToolButtonStyle (Qt::ToolButtonTextBesideIcon);
    connect(toolBtn2, SIGNAL(clicked(bool)), this, SLOT(showChatWidget2()));
    toolBtn3 = new QToolButton;
    toolBtn3->setText (tr(" 会飞的狼"));
    toolBtn3->setIcon (QPixmap(rsrcPath + "/jj.png"));
    toolBtn3->setIconSize (QPixmap(rsrcPath + "/dr.png").size () * 0.58);
    toolBtn3->setAutoRaise (true);
    toolBtn3->setToolButtonStyle (Qt::ToolButtonTextBesideIcon);
    connect(toolBtn3, SIGNAL(clicked(bool)), this, SLOT(showChatWidget3()));
    toolBtn4 = new QToolButton;
    toolBtn4->setText (tr(" 金色的鱼"));
    toolBtn4->setIcon (QPixmap(rsrcPath + "/lswh.png"));
    toolBtn4->setIconSize (QPixmap(rsrcPath + "/lswh.png").size () * 0.58);
    toolBtn4->setAutoRaise (true);
    toolBtn4->setToolButtonStyle (Qt::ToolButtonTextBesideIcon);
    connect(toolBtn4, SIGNAL(clicked(bool)), this, SLOT(showChatWidget4()));
    toolBtn5 = new QToolButton;
    toolBtn5->setText (tr(" 一夜情柔"));
    toolBtn5->setIcon (QPixmap(rsrcPath + "/qmnn.png"));
    toolBtn5->setIconSize (QPixmap(rsrcPath + "/qmnn.png").size () * 0.58);
    toolBtn5->setAutoRaise (true);
    toolBtn5->setToolButtonStyle (Qt::ToolButtonTextBesideIcon);
    connect(toolBtn5, SIGNAL(clicked(bool)), this, SLOT(showChatWidget5()));
    toolBtn6 = new QToolButton;
    toolBtn6->setText (tr(" 天马行空"));
    toolBtn6->setIcon (QPixmap(rsrcPath + "/spqy.png"));
    toolBtn6->setIconSize (QPixmap(rsrcPath + "/spqy.png").size () * 0.58);
    toolBtn6->setAutoRaise (true);
    toolBtn6->setToolButtonStyle (Qt::ToolButtonTextBesideIcon);
    connect(toolBtn6, SIGNAL(clicked(bool)), this, SLOT(showChatWidget6()));
    toolBtn7 = new QToolButton;
    toolBtn7->setText (tr(" 飞奔时代"));
    toolBtn7->setIcon (QPixmap(rsrcPath + "/wy.png"));
    toolBtn7->setIconSize (QPixmap(rsrcPath + "/wy.png").size () * 0.58);
    toolBtn7->setAutoRaise (true);
    toolBtn7->setToolButtonStyle (Qt::ToolButtonTextBesideIcon);
    connect(toolBtn7, SIGNAL(clicked(bool)), this, SLOT(showChatWidget7()));
    toolBtn8 = new QToolButton;
    toolBtn8->setText (tr(" 一世情缘"));
    toolBtn8->setIcon (QPixmap(rsrcPath + "/qq.png"));
    toolBtn8->setIconSize (QPixmap(rsrcPath + "/qq.png").size () * 0.58);
    toolBtn8->setAutoRaise (true);
    toolBtn8->setToolButtonStyle (Qt::ToolButtonTextBesideIcon);
    connect(toolBtn8, SIGNAL(clicked(bool)), this, SLOT(showChatWidget8()));
    toolBtn9 = new QToolButton;
    toolBtn9->setText (tr(" 钟爱一生"));
    toolBtn9->setIcon (QPixmap(rsrcPath + "/ymrl.png"));
    toolBtn9->setIconSize (QPixmap(rsrcPath + "/ymrl.png").size () * 0.58);
    toolBtn9->setAutoRaise (true);
    toolBtn9->setToolButtonStyle (Qt::ToolButtonTextBesideIcon);
    connect(toolBtn9, SIGNAL(clicked(bool)), this, SLOT(showChatWidget9()));
    //创建一个QGroupBox类类实例,在文本例中对应抽屉“群成员”
    QGroupBox *groupBox = new QGroupBox;
    QVBoxLayout *layout = new QVBoxLayout(groupBox);
    layout->setMargin (6);          //设置边框距
    layout->setSpacing (2);
    layout->setAlignment (Qt::AlignLeft | Qt::AlignTop);
    layout->addWidget (toolBtn1);
    layout->addWidget (toolBtn2);
    layout->addWidget (toolBtn3);
    layout->addWidget (toolBtn4);
    layout->addWidget (toolBtn5);
    layout->addWidget (toolBtn6);
    layout->addWidget (toolBtn7);
    layout->addWidget (toolBtn8);
    layout->addWidget (toolBtn9);
    this->addItem ((QWidget*)groupBox, tr("群成员"));
}
void Drawer::showChatWidget1 ()
{
    //以按钮toolBtn1的文本为用户名创建一个Widget类的实例,对应于一个聊天窗口
    chatWidget1 = new Widget(0, toolBtn1->text ());
    //设置聊天的标题文字
    chatWidget1->setWindowTitle (toolBtn1->text ());
    //设置聊天窗口图标
    chatWidget1->setWindowIcon (toolBtn1->icon ());
    chatWidget1->show ();
}
void Drawer::showChatWidget2 ()
{
    chatWidget2 = new Widget(0, toolBtn2->text ());
    chatWidget2->setWindowTitle (toolBtn2->text ());
    chatWidget2->setWindowIcon (toolBtn2->icon ());
    chatWidget2->show ();
}
void Drawer::showChatWidget3 ()
{
    chatWidget3 = new Widget(0, toolBtn3->text ());
    chatWidget3->setWindowTitle (toolBtn3->text ());
    chatWidget3->setWindowIcon (toolBtn3->icon ());
    chatWidget3->show ();
}
void Drawer::showChatWidget4 ()
{
    chatWidget4 = new Widget(0, toolBtn4->text ());
    chatWidget4->setWindowTitle (toolBtn4->text ());
    chatWidget4->setWindowIcon (toolBtn4->icon ());
    chatWidget4->show ();
}
void Drawer::showChatWidget5 ()
{
    chatWidget5 = new Widget(0, toolBtn5->text ());
    chatWidget5->setWindowTitle (toolBtn5->text ());
    chatWidget5->setWindowIcon (toolBtn5->icon ());
    chatWidget5->show ();
}
void Drawer::showChatWidget6 ()
{
    chatWidget6 = new Widget(0, toolBtn6->text ());
    chatWidget6->setWindowTitle (toolBtn6->text ());
    chatWidget6->setWindowIcon (toolBtn6->icon ());
    chatWidget6->show ();
}
void Drawer::showChatWidget7 ()
{
    chatWidget7 = new Widget(0, toolBtn7->text ());
    chatWidget7->setWindowTitle (toolBtn7->text ());
    chatWidget7->setWindowIcon (toolBtn7->icon ());
    chatWidget7->show ();
}
void Drawer::showChatWidget8 ()
{
    chatWidget8 = new Widget(0, toolBtn8->text ());
    chatWidget8->setWindowTitle (toolBtn8->text ());
    chatWidget8->setWindowIcon (toolBtn8->icon ());
    chatWidget8->show ();
}
void Drawer::showChatWidget9 ()
{
    chatWidget9 = new Widget(0, toolBtn9->text ());
    chatWidget9->setWindowTitle (toolBtn9->text ());
    chatWidget9->setWindowIcon (toolBtn9->icon ());
    chatWidget9->show ();
}

client.h

#ifndef CLIENT_H
#define CLIENT_H
#include <QDialog>
#include <QHostAddress>
#include <QFile>
#include <QTime>
class QTcpSocket;
namespace Ui {
class Client;
}
class Client : public QDialog
{
    Q_OBJECT
public:
    explicit Client(QWidget *parent = 0);
    ~Client();
    void setHostAddr(QHostAddress addr);        //获取发送端IP地址
    void setFileName(QString name);             //获取文件保存路径
protected:
    void closeEvent(QCloseEvent *);
private:
    Ui::Client *ui;
    QTcpSocket *tClnt;                  //客户端套接字类
    quint16 blockSize;
    QHostAddress hostAddr;
    qint16 tPort;
    qint64 totalBytes;                  //总共需接收的字节数
    qint64 bytesReceived;               //已接收字节数
    qint64 fileNameSize;
    QString fileName;
    QFile *locFile;                     //待接收的文件
    QByteArray inBlock;                 //缓存一次接收的数据
    QTime time;
private slots:
    void newConn();                     //连接到服务器
    void readMsg();                     //读取文件数据
    void displayErr(QAbstractSocket::SocketError);  //显示错误信息
    void on_cCancleBtn_clicked();
    void on_cCloseBtn_clicked();
};
#endif // CLIENT_H

client.cpp

#include "client.h"
#include "ui_client.h"
#include <QTcpSocket>
#include <QDebug>
#include <QMessageBox>
Client::Client(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Client)
{
    ui->setupUi(this);
    setFixedSize (400, 190);
    totalBytes = 0;
    bytesReceived = 0;
    fileNameSize = 0;
    tClnt = new QTcpSocket(this);
    tPort = 5555;
    /**/
    connect (tClnt, SIGNAL(readyRead()), this, SLOT(readMsg()));
    /*接收文件的中途,一旦发生错误,则采用displayErr()槽函数输出错误信息*/
    connect (tClnt, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayErr(QAbstractSocket::SocketError)));
}
Client::~Client()
{
    delete ui;
}
/*
 * 获取文件保存路径
*/
void Client::setFileName (QString name)
{
    locFile = new QFile(name);
}
/*
 * 获取发送端IP地址
*/
void Client::setHostAddr (QHostAddress addr)
{
    hostAddr = addr;
    newConn ();
}
/*
 * 用来设置与服务器的连接
*/
void Client::newConn ()
{
    blockSize = 0;
    //中止当前的连接和重置套接字
    tClnt->abort ();
    tClnt->connectToHost (hostAddr, tPort);
    time.start ();
}
void Client::readMsg ()
{
    QDataStream in(tClnt);
    in.setVersion(QDataStream::Qt_4_7);
    float useTime = time.elapsed();
    if (bytesReceived <= sizeof(qint64)*2) {
        if ((tClnt->bytesAvailable() >= sizeof(qint64)*2) && (fileNameSize == 0))
        {
            in>>totalBytes>>fileNameSize;
            bytesReceived += sizeof(qint64)*2;
        }
        if((tClnt->bytesAvailable() >= fileNameSize) && (fileNameSize != 0)){
            in>>fileName;
            bytesReceived +=fileNameSize;
            if(!locFile->open(QFile::WriteOnly)){
                QMessageBox::warning(this,tr("应用程序"),tr("无法读取文件 %1:\n%2.").arg(fileName).arg(locFile->errorString()));
                return;
            }
        } else {
            return;
        }
    }
    if (bytesReceived < totalBytes) {
        bytesReceived += tClnt->bytesAvailable();
        inBlock = tClnt->readAll();
        locFile->write(inBlock);
        inBlock.resize(0);
    }
    ui->progressBar->setMaximum(totalBytes);
    ui->progressBar->setValue(bytesReceived);
    double speed = bytesReceived / useTime;
    ui->cStatusLbl->setText(tr("已接收 %1MB (%2MB/s) \n共%3MB 已用时:%4秒\n估计剩余时间:%5秒")
                                      .arg(bytesReceived / (1024*1024))
                                      .arg(speed*1000/(1024*1024),0,'f',2)
                                      .arg(totalBytes / (1024 * 1024))
                                      .arg(useTime/1000,0,'f',0)
                                      .arg(totalBytes/speed/1000 - useTime/1000,0,'f',0));
    if(bytesReceived == totalBytes)
    {
        locFile->close();
        tClnt->close();
        ui->cStatusLbl->setText(tr("接收文件 %1 完毕").arg(fileName));
    }
}
/*
 * 接收文件的中途,一旦发生错误,则采用displayErr()槽函数输出错误信息
*/
void Client::displayErr (QAbstractSocket::SocketError sockErr)
{
    switch(sockErr)
    {
    case QAbstractSocket::RemoteHostClosedError : break;
    default : qDebug() << tClnt->errorString();
    }
}
void Client::on_cCancleBtn_clicked()
{
    //中止当前的连接和重置套接字
    tClnt->abort ();
    if(locFile->isOpen ())
    {
        locFile->close ();
    }
}
void Client::on_cCloseBtn_clicked()
{
    //中止当前的连接和重置套接字
    tClnt->abort ();
    if(locFile->isOpen ())
    {
        locFile->close ();
    }
    close ();
}
void Client::closeEvent (QCloseEvent *)
{
    on_cCloseBtn_clicked ();
}

server.h

#ifndef SERVER_H
#define SERVER_H
#include <QDialog>
#include <QTime>
class QFile;
class QTcpServer;
class QTcpSocket;
namespace Ui {
class Server;
}
class Server : public QDialog
{
    Q_OBJECT
public:
    explicit Server(QWidget *parent = 0);
    ~Server();
    void initSrv();         //初始化服务器
    void refused();         //关闭服务器
protected:
    void closeEvent(QCloseEvent *);
private:
    Ui::Server *ui;
    qint16 tPort;
    QTcpServer *tSrv;
    QString fileName;
    QString theFileName;
    QFile *locFile;         //待发送的文件
    qint64 totalBytes;      //总共需要发送的字节
    qint64 bytesWritten;    //已经发送字节数
    qint64 bytesTobeWrite;  //待发送的字节数
    qint64 payloadSize;     //被初始化为一个常量
    QByteArray outBlock;    //缓存一次发送的数据
    QTcpSocket *clntConn;   //客户端连接的套接字
    QTime time;
private slots:
    void sndMsg();                          //发送数据
    void updClntProgress(qint64 numBytes);  //更新进度条
    void on_sSendBtn_clicked();
    void on_sOpenBtn_clicked();
    void on_sCloseBtn_clicked();
signals:
    void sndFileName(QString fileName);
};
#endif // SERVER_H

server.cpp

#include "server.h"
#include "ui_server.h"
#include <QFile>
#include <QTcpServer>
#include <QTcpSocket>
#include <QMessageBox>
#include <QFileDialog>
#include <QDebug>
Server::Server(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Server)
{
    ui->setupUi (this);
    setFixedSize (400, 207);
    tPort = 5555;
    tSrv = new QTcpServer(this);
    connect(tSrv, SIGNAL(newConnection()), this, SLOT(sndMsg()));
    initSrv ();
}
Server::~Server()
{
    delete ui;
}
/*
 * 初始化服务器
*/
void Server::initSrv ()
{
    payloadSize = 64 * 1024;
    totalBytes = 0;
    bytesWritten = 0;
    bytesTobeWrite = 0;
    ui->sStatusLbl->setText (tr("请选择要传送的文件"));
    //重新设置进度条
    ui->progressBar->reset ();
    ui->sOpenBtn->setEnabled (true);
    ui->sSendBtn->setEnabled (false);
    //关闭服务器
    tSrv->close ();
}
/*
 * 发送数据
*/
void Server::sndMsg ()
{
    ui->sSendBtn->setEnabled (false);
    clntConn = tSrv->nextPendingConnection ();  //获取下一个等待连接
    connect(clntConn, SIGNAL(bytesWritten(qint64)), this, SLOT(updClntProgress(qint64)));
    ui->sStatusLbl->setText (tr("开始传送文件%1 !").arg (theFileName));
    locFile = new QFile(fileName);
    //首先以只读方式打开选中的文件
    if(!locFile->open((QFile::ReadOnly)))
    {
        QMessageBox::warning(this, tr("应用程序"), tr("无法读取文件 %1:\n%2").arg(fileName).arg(locFile->errorString()));
        return;
    }
    /*通过QFile类的size()函数获取待发送文件的大小,并将该值暂存于totalBytes变量中*/
    totalBytes = locFile->size ();
    /*将发送缓冲区outBlock封装在一个QDataStream类型的变量中,这样做可以很方便地通过重载的"<<"操作符填写文件头指针*/
    QDataStream sendout(&outBlock, QIODevice::WriteOnly);
    sendout.setVersion (QDataStream::Qt_4_7);
    //time是QTime对象,用来统计传输所用的时间
    time.start ();
    /*通过QString类的right()函数去掉文件的路径部分,仅将文件部分保存在curFile变量中,最后位置-1,减去‘/’的长度*/
    QString curFile = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);
    /*构造一个临时的文件头,将该值追加到totalBytes字段,从而完成实际需要发送字节数的记录*/
    sendout << qint64(0) << qint64(0) << curFile;
    totalBytes += outBlock.size();
    /*将读写操作指向从头开始*/
    sendout.device ()->seek (0);
    /*填写实际的总长度和文件长度*/
    sendout << totalBytes << qint64((outBlock.size () - sizeof(qint64) * 2));
    /*将文件头发出,同时修改发送字节数bytesTobeWrite*/
    bytesTobeWrite = totalBytes - clntConn->write (outBlock);
    /*清空发送缓存区以备下次使用*/
    outBlock.resize (0);
}
/*
 * 更新进度条
*/
void Server::updClntProgress (qint64 numBytes)
{
    /*用于在传输大文件时使界面不会冻结*/
    qApp->processEvents ();
    bytesWritten += (int)numBytes;
    /*如果已发送的字节数大于0*/
    if(bytesWritten > 0)
    {
        /*将待发送的字节数读到发送缓冲区outBlock中*/
        outBlock = locFile->read (qMin(bytesTobeWrite, payloadSize));
        /*更新待发送的字节数*/
        bytesTobeWrite -= (int)clntConn->write (outBlock);
        /*将发送缓冲区清空*/
        outBlock.resize (0);
    }
    else
    {
        //关闭文件
        locFile->close ();
    }
    ui->progressBar->setMaximum (totalBytes);
    ui->progressBar->setValue (bytesWritten);
    /*time.elapsed ()函数来获取耗费的时间,这个是从time.start()开始计时的。通过获取的时间*/
    float useTime = time.elapsed ();
    double speed = bytesWritten / useTime;
    ui->sStatusLbl->setText(tr("已发送 %1MB (%2MB/s) \n共%3MB 已用时:%4秒\n估计剩余时间:%5秒")
                   .arg(bytesWritten / (1024*1024))
                   .arg(speed*1000 / (1024*1024), 0, 'f', 2)
                   .arg(totalBytes / (1024 * 1024))
                   .arg(useTime/1000, 0, 'f', 0)
                   .arg(totalBytes/speed/1000 - useTime/1000, 0, 'f', 0));
    if(bytesWritten == totalBytes)
    {
        locFile->close ();
        tSrv->close ();
        ui->sStatusLbl->setText (tr("传送文件 %1 成功").arg (theFileName));
    }
}
/*
 * 打开文件函数
*/
void Server::on_sOpenBtn_clicked()
{
    fileName = QFileDialog::getOpenFileName (this);
    if(!fileName.isEmpty ())
    {
        /*仅将文件部分保存在theFileName变量中*/
        theFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);
        ui->sStatusLbl->setText(tr("要传送的文件为:%1 ").arg(theFileName));
        ui->sSendBtn->setEnabled(true);
        ui->sOpenBtn->setEnabled(false);
    }
}
/*
 * 发送文件槽函数
*/
void Server::on_sSendBtn_clicked()
{
    /*开始监听*/
    if(!tSrv->listen(QHostAddress::Any,tPort))//开始监听
    {
        qDebug() << tSrv->errorString();
        close();
        return;
    }
    ui->sStatusLbl->setText(tr("等待对方接收... ..."));
    emit sndFileName(theFileName);
}
/*
 * 关闭监听槽函数
*/
void Server::on_sCloseBtn_clicked()
{
    /*关闭服务器*/
    if(tSrv->isListening ())
    {
        tSrv->close ();;
        if(locFile->isOpen ())
        {
            locFile->close ();
        }
        clntConn->abort ();
    }
    /*关闭对话框*/
    close ();
}
void Server::closeEvent (QCloseEvent *)
{
    on_sCloseBtn_clicked ();
}
/*
 * 如果接收端拒绝接收该文件,则关闭服务器
*/
void Server::refused ()
{
    tSrv->close ();
    ui->sStatusLbl->setText (tr("对方拒绝接收!"));
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QKeyEvent>
#include <QTextCharFormat>
class QUdpSocket;
class Server;
namespace Ui {
class Widget;
}
enum MsgType{Msg, UsrEnter, UsrLeft, FileName, Refuse};     //消息类型
class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent,QString usrname);
    ~Widget();
protected:
    void usrEnter(QString usename, QString ipaddr);         //处理新用户加入
    void usrLeft(QString usrname, QString time);            //处理用户离开
    void sndMsg(MsgType type, QString srvaddr="");          //获取UDP消息
    QString getIP();                                        //获取IP地址
    QString getUsr();                                       //获取用户名
    QString getMsg();                                       //获取聊天消息
    //用于在收到文件名UDP消息时判断是否接受该文件
    void hasPendingFile(QString usrname, QString srvaddr,QString clntaddr, QString filename);
    bool saveFile(const QString& filename);
    void closeEvent(QCloseEvent *);
private:
    Ui::Widget *ui;
    QUdpSocket *udpSocket;
    qint16 port;
    QString uName;
    QString fileName;
    Server *srv;
    QColor color;
private slots:
    void processPendingDatagrams();                         //接收UDP消息
    void on_sendBtn_clicked();
    void getFileName(QString);                              //获取服务器类sndFileName()信号发送过来的文件名
    void on_exitBtn_clicked();
    void on_sendTBtn_clicked();
    void on_fontCbx_activated(const QString &arg1);
    void on_sizeCbx_activated(const QString &arg1);
    void on_boldTBtn_clicked(bool checked);
    void on_italicTBtn_clicked(bool checked);
    void on_underlineTBtn_clicked(bool checked);
    void curFmtChanged(const QTextCharFormat &fmt);
    void on_colorTBtn_clicked();
    void on_saveTBtn_clicked();
    void on_clearTBtn_clicked();
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QUdpSocket>
#include <QMessageBox>
#include <QScrollBar>
#include <QDateTime>
#include <QNetworkInterface>
#include "server.h"
#include "client.h"
#include <QFileDialog>
#include <QColorDialog>
Widget::Widget(QWidget *parent,QString usrname) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    uName = usrname;
    udpSocket = new QUdpSocket(this);
    //设置UDP的端口号参数,服务器定向此端口发送广播信息
    port = 23232;
    //绑定指定的端口上
    udpSocket->bind (port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
    //connect()槽函数,随时接收来自其他用户的UDP广播消息
    connect(udpSocket, SIGNAL(readyRead()), this, SLOT(processPendingDatagrams()));
    //发送UDP广播消息则是通过调用sndMSg函数来实现的,该函数专门用于发送各类UDP数据
    sndMsg (UsrEnter);
    srv = new Server(this);
    connect(srv, SIGNAL(sndFileName(QString)), this, SLOT(getFileName(QString)));
    connect(ui->msgTxtEdit, SIGNAL(currentCharFormatChanged(QTextCharFormat)),this, SLOT(curFmtChanged(const QTextCharFormat)));
}
Widget::~Widget()
{
    delete ui;
}
/*
 * 广播UDP消息
*/
void Widget::sndMsg (MsgType type, QString srvaddr)
{
    QByteArray data;
    QDataStream out(&data, QIODevice::WriteOnly);
    QString address = getIP ();
    out << type << getUsr ();   //向要发送的数据中写入信息类型type、用户名
    switch(type)
    {
    case Msg :      //处理聊天信息
        if(ui->msgTxtEdit->toPlainText () == "")
        {
            QMessageBox::warning (0, tr("警告"), tr("发送内容不能为空"), QMessageBox::Ok);
            return;
        }
        out << address << getMsg ();
        ui->msgBrowser->verticalScrollBar ()->setValue (ui->msgBrowser->verticalScrollBar ()->maximum ());
        break;
    case UsrEnter:  //对于新用户加入UsrEnter,只是简单地向数据中写入IP地址
        out << address;
        break;
    case UsrLeft :
        break;
    case FileName:
    {
        int row = ui->usrTblWidget->currentRow();
        QString clntaddr = ui->usrTblWidget->item(row, 1)->text();
        out << address << clntaddr << fileName;
        break;
    }
    case Refuse:
        out << srvaddr;
        break;
    }
    //完成对消息的处理后,最后使用writeDatagram()函数进行UDP广播
    udpSocket->writeDatagram (data, data.length (), QHostAddress::Broadcast, port);
}
/*
 * 接收UDP消息
*/
void Widget::processPendingDatagrams ()
{
    //hasPendingDatagrams()判断是否有可提供读取的数据
    while(udpSocket->hasPendingDatagrams ())
    {
        QByteArray datagram;
        //如果有可读取的数据则通过pendingDatagramSize()获取当前可供读取的UDP报文大小,并据此大小分配接收缓冲区datagram,
        //最后使用QUdpSocket类的成员函数readDatagram读取相应数据
        datagram.resize (udpSocket->pendingDatagramSize ());
        udpSocket->readDatagram (datagram.data (), datagram.size ());
        QDataStream in(&datagram, QIODevice::ReadOnly);
        int msgType;
        in >> msgType;//首先获取信息的类型
        QString usrName,ipAddr,msg;
        QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
        switch(msgType)
        {
        case Msg:   //处理接收消息
            //如果是普通的聊天消息Msg,那么就获取其中的用户名,IP地址和内容信息等数据,然后用户名和聊天内容显示
            //在界面左上角的信息浏览器msgBrowser中,在显示信息的同时显示系统当前日期时间
            in >> usrName >> ipAddr >> msg;
            ui->msgBrowser->setTextColor(Qt::blue);
            ui->msgBrowser->setCurrentFont(QFont("Times New Roman",12));
            ui->msgBrowser->append("[ " +usrName+" ] "+ time);
            ui->msgBrowser->append(msg);
            break;
        case UsrEnter:  //如果是新用户加入UsrEnter,那么就获取其中的用户名和IP地址信息,然后使用usrEnter()函数进行新用户登录的处理
            in >> usrName >> ipAddr;
            usrEnter (usrName, ipAddr);
            break;
        case UsrLeft:   //如果是用户退出UsrLeft,那么获取其中的用户名,然后使用usrLeft()函数进行用户离开的处理
            in >>usrName;
            usrLeft(usrName,time);
            break;
        case FileName:
        {
            in >> usrName >> ipAddr;
            QString clntAddr, fileName;
            in >> clntAddr >> fileName;
            hasPendingFile(usrName, ipAddr, clntAddr, fileName);
            break;
        }
        case Refuse:
        {
            in >> usrName;
            QString srvAddr;
            in >> srvAddr;
            QString ipAddr = getIP();
            if(ipAddr == srvAddr)
            {
                srv->refused();
            }
            break;
        }
        }
    }
}
/*
 * 处理新用户加入
*/
void Widget::usrEnter (QString usrname, QString ipaddr)
{
    bool isEmpty = ui->usrTblWidget->findItems(usrname, Qt::MatchExactly).isEmpty();
    if (isEmpty) {
        QTableWidgetItem *usr = new QTableWidgetItem(usrname);
        QTableWidgetItem *ip = new QTableWidgetItem(ipaddr);
        ui->usrTblWidget->insertRow(0);
        ui->usrTblWidget->setItem(0,0,usr);
        ui->usrTblWidget->setItem(0,1,ip);
        ui->msgBrowser->setTextColor(Qt::gray);
        ui->msgBrowser->setCurrentFont(QFont("Times New Roman",10));
        ui->msgBrowser->append(tr("%1 在线!").arg(usrname));
        ui->usrNumLbl->setText(tr("在线人数:%1").arg(ui->usrTblWidget->rowCount()));
        sndMsg(UsrEnter);
    }
}
/*
 * 处理用户离开
*/
void Widget::usrLeft (QString usrname, QString time)
{
    int rowNum = ui->usrTblWidget->findItems(usrname, Qt::MatchExactly).first()->row();
    ui->usrTblWidget->removeRow(rowNum);
    ui->msgBrowser->setTextColor(Qt::gray);
    ui->msgBrowser->setCurrentFont(QFont("Times New Roman", 10));
    ui->msgBrowser->append(tr("%1  %2 离开!").arg(usrname).arg(time));
    ui->usrNumLbl->setText(tr("在线人数:%1").arg(ui->usrTblWidget->rowCount()));
}
/*
 * 获得IP地址
*/
QString Widget::getIP ()
{
    //QNetworkInterface类提供了一个主机IP地址和网络接口的列表
    QList<QHostAddress> list = QNetworkInterface::allAddresses ();
    foreach(QHostAddress addr, list)
    {
        //判断IP的协议
        if(addr.protocol () == QAbstractSocket::IPv4Protocol)
        {
            return addr.toString ();
        }
    }
    return 0;
}
/*
 * 获取用户名
*/
QString Widget::getUsr ()
{
    return uName;
}
/*
 * 获取聊天信息
*/
QString Widget::getMsg ()
{
    QString msg = ui->msgTxtEdit->toHtml ();
    ui->msgTxtEdit->clear ();
    ui->msgTxtEdit->setFocus ();
    return msg;
}
/*
 * 发送消息按钮
*/
void Widget::on_sendBtn_clicked()
{
   sndMsg (Msg);
}
/*
 * 退出聊天窗口按钮
*/
void Widget::on_exitBtn_clicked()
{
    close();
}
/*
 * 获取服务器类sndFileName()信号发送过来的文件名
*/
void Widget::getFileName (QString name)
{
    fileName = name;
    sndMsg (FileName);
}
/*
 * 传输文件
*/
void Widget::on_sendTBtn_clicked()
{
    if(ui->usrTblWidget->selectedItems().isEmpty())
    {
        QMessageBox::warning(0, tr("选择用户"),tr("请先选择目标用户!"), QMessageBox::Ok);
        return;
    }
    srv->show();
    srv->initSrv();
}
/*
 * 来判断是否要接收该文件,当发送过来拒绝信息时,需要判断改程序是否是发送端,如果是,则执行服务器的refused()函数
*/
void Widget::hasPendingFile (QString usrname, QString srvaddr, QString clntaddr, QString filename)
{
    QString ipAddr = getIP();
    if(ipAddr == clntaddr)
    {
        int btn = QMessageBox::information(this,tr("接受文件"),tr("来自%1(%2)的文件:%3,是否接收?").arg(usrname).arg(srvaddr).arg(filename),QMessageBox::Yes,QMessageBox::No);
        if (btn == QMessageBox::Yes)
        {
            QString name = QFileDialog::getSaveFileName(0,tr("保存文件"),filename);
            if(!name.isEmpty())
            {
                Client *clnt = new Client(this);
                clnt->setFileName(name);
                clnt->setHostAddr(QHostAddress(srvaddr));
                clnt->show();
            }
        }
        else {
            sndMsg(Refuse, srvaddr);
        }
    }
}
void Widget::closeEvent(QCloseEvent *e)
{
    sndMsg(UsrLeft);
    QWidget::closeEvent(e);
}
void Widget::on_fontCbx_activated(const QString &arg1)
{
    ui->msgTxtEdit->setCurrentFont (arg1);
    ui->msgTxtEdit->setFocus ();
}
void Widget::on_sizeCbx_activated(const QString &arg1)
{
    ui->msgTxtEdit->setFontPointSize (arg1.toDouble ());
    ui->msgTxtEdit->setFocus ();
}
void Widget::on_boldTBtn_clicked(bool checked)
{
    if(checked)
    {
        ui->msgTxtEdit->setFontWeight (QFont::Bold);
    }
    else
    {
        ui->msgTxtEdit->setFontWeight (QFont::Normal);
    }
    ui->msgTxtEdit->setFocus ();
}
void Widget::on_italicTBtn_clicked(bool checked)
{
    ui->msgTxtEdit->setFontItalic (checked);
    ui->msgTxtEdit->setFocus ();
}
void Widget::on_underlineTBtn_clicked(bool checked)
{
    ui->msgTxtEdit->setFontUnderline (checked);
    ui->msgTxtEdit->setFocus ();
}
void Widget::on_colorTBtn_clicked()
{
    color = QColorDialog::getColor (color, this);
    if(color.isValid ())
    {
        ui->msgTxtEdit->setTextColor (color);
        ui->msgTxtEdit->setFocus ();
    }
}
bool Widget::saveFile (const QString &filename)
{
    QFile file(filename);
    if(!file.open (QFile::WriteOnly | QFile::Text))
    {
        QMessageBox::warning (this, tr("保存文件"), tr("无法保存文件 %1:\n%2").arg (filename).arg (file.errorString ()));
        return false;
    }
    QTextStream out(&file);
    out << ui->msgBrowser->toPlainText ();
    return true;
}
void Widget::on_saveTBtn_clicked()
{
    if(ui->msgTxtEdit->document ()->isEmpty ())
    {
        QMessageBox::warning (0, tr("警告"), tr("聊天记录为空,无法保存!"), QMessageBox::Ok);
    }
    else
    {
        QString name = QFileDialog::getSaveFileName (this, tr("保存聊天记录"), tr("聊天记录"), tr("文本(*.txt);;所有文件(*.*)"));
        if(!name.isEmpty ())
        {
            saveFile(name);
        }
    }
}
void Widget::on_clearTBtn_clicked()
{
    ui->msgBrowser->clear ();
}
void Widget::curFmtChanged (const QTextCharFormat &fmt)
{
    ui->fontCbx->setCurrentFont(fmt.font());
    if (fmt.fontPointSize() < 8) {
        ui->sizeCbx->setCurrentIndex(4);
    } else {
        ui->sizeCbx->setCurrentIndex(ui->sizeCbx->findText(QString::number(fmt.fontPointSize())));
    }
    ui->boldTBtn->setChecked(fmt.font().bold());
    ui->italicTBtn->setChecked(fmt.font().italic());
    ui->underlineTBtn->setChecked(fmt.font().underline());
    color = fmt.foreground().color();
}

0 0