目录
一、项目概述
本项目的目标是创建一个QQ聊天室应用,旨在作为学习基础QT语法和网络编程中Socket编程的入门练习。我们利用QT框架来设计并实现QQ的基础用户界面以及群聊窗口,同时,通过Socket编程技术,实现了用户之间的消息群发功能。
二、项目成果
1、QQ基础界面展示:
QQ基础界面如下图所示,页面中展示各个用户,通过点击用户,可以进入群聊界面。
2、群聊界面展示:
点击用户,即可进入群聊,群聊界面展示用户上线、下线、在线人数等信息。消息输入框中能够选择字体、加粗、颜色等功能。
3、聊天功能展示
用户在群聊中发送信息,可以显示用户名称和时间。
三、项目代码
在文章置顶中有zip文件
1、登录头文件(denglu.h)
#ifndef DENGLU_H
#define DENGLU_H
#include <QWidget>
namespace Ui {
class denglu;
}
class denglu : public QWidget
{
Q_OBJECT
public:
explicit denglu(QWidget *parent = nullptr);
~denglu();
private:
Ui::denglu *ui;
QVector<bool> IsShow;
};
#endif // DENGLU_H
2、登录源文件(denglu.cpp)
用于实现头文件中的函数,主要是添加用户和链接群聊界面。
#include "denglu.h"
#include "ui_denglu.h"
#include<QIcon>
#include<QToolButton>
#include<QMessageBox>
#include<widget.h>
denglu::denglu(QWidget *parent)
: QWidget(parent)
, ui(new Ui::denglu)
{
ui->setupUi(this);
//设置图标
//路径:“:+前缀+路径”
this->setWindowIcon(QIcon(":/images/QQ.jpg"));
//设置名称
this->setWindowTitle("QQ 2024");
QList<QString> nameList;
nameList << "绿巨人" << "钢铁侠" << "美国队长";
QStringList iconNameList;
iconNameList << "hulk01" << "gang01" << "mei01";
QVector<QToolButton *> vectorbtn;
for(int i = 0; i < 3; i++){
QToolButton *btn = new QToolButton(this);
// 加载图片
btn->setIcon(QIcon(QString(":/images/%1.jpg").arg(iconNameList[i])));
// 设置图片大小
btn->setIconSize(QSize(100, 100));
// 设置网名
btn->setText(QString("%1").arg(nameList[i]));
// 设置为透明
btn->setAutoRaise(true);
// 设置显示格式
btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
// 放到vlayout布局
ui->vlayout->addWidget(btn);
vectorbtn.push_back(btn);
IsShow.push_back(false);
}
// 将按钮进行连接聊天框
for(int i = 0; i < 3; i++){
connect(vectorbtn[i], &QToolButton::clicked, [=](){
if(IsShow[i]){
QMessageBox::warning(this, "警告", "该聊天框已被打开!");
return;
}
IsShow[i] = true;
Widget *widger = new Widget(nullptr, vectorbtn[i]->text());
widger->setWindowIcon(vectorbtn[i]->icon());
widger->setWindowTitle(vectorbtn[i]->text());
widger->show();
// 关闭聊天框时,将IsShow变为False
connect(widger, &Widget::closeWidget, this, [=]{
IsShow[i] = false;
});
});
}
}
denglu::~denglu()
{
delete ui;
}
3、聊天界面头文件(widget.h)
用于定义聊天界面功能的函数。
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr, QString name = "");
enum Msgtype{Msg, UserEnter, UserLeft};
void sndMsg(Msgtype type); //广播udp信息
QString getName(); //获取名字
QString getMsg(); //获取聊天信息
void userEnter(QString username); //处理用户进入
void userLeft(QString username, QString time); //处理用户离开
void ReceiveMessage(); // 接受UDP信息
//重写关闭事件
void closeEvent(QCloseEvent *);
~Widget();
signals:
void closeWidget();
private:
Ui::Widget *ui;
QString myname;
quint16 port; // 端口
QUdpSocket *udpSocket; // UDP套接字
};
#endif // WIDGET_H
4、聊天界面源文件(widget.cpp)
主要部分,用于实现聊天功能。
#include "widget.h"
#include "ui_widget.h"
#include<QMessageBox>
#include<QDateTime>
#include<QColorDialog>
#include<QFileDialog>
Widget::Widget(QWidget *parent, QString name)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
myname = name;
this->port = 9999;
this->udpSocket = new QUdpSocket(this);
udpSocket->bind(port, QUdpSocket::ShareAddress |QUdpSocket::ReuseAddressHint);
//监听信号
connect(udpSocket, &QUdpSocket::readyRead, this, &Widget::ReceiveMessage);
//连接发送按钮
connect(ui->sendTBtn, &QPushButton::clicked, [=](){
sndMsg(Msg);
});
//新用户进入
sndMsg(UserEnter);
//连接退出按钮
connect(ui->exitTBtn, &QPushButton::clicked, [=]{
this->close();
});
//连接字体
connect(ui->fontCbx, &QFontComboBox::currentFontChanged, [=](const QFont &font){
ui->msgTxtEdit->setFont(font);
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->colorTBtn, &QToolButton::clicked, this, [=](){
QColor color = QColorDialog::getColor(color, this);
ui->msgTxtEdit->setTextColor(color);
});
//清空聊天记录
connect(ui->clearTBtn, &QToolButton::clicked, [=](){
ui->msgBrowser->clear();
});
//保存聊天记录
connect(ui->saveTBtn, &QToolButton::clicked, [=](){
if(ui->msgBrowser->document()->isEmpty()){
QMessageBox::warning(this, "警告", "保存的信息不能为空!");
}
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::sndMsg(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::userEnter(QString username)
{
bool isEmpty = ui->usrTblWidget->findItems(username, Qt::MatchExactly).isEmpty();
if(isEmpty){
QTableWidgetItem *user = new QTableWidgetItem(username);
ui->usrTblWidget->insertRow(0);
ui->usrTblWidget->setItem(0,0,user);
ui->msgBrowser->setTextColor(Qt::gray);
ui->msgBrowser->append(username+"用户已上线");
ui->userNumLbl->setText(QString("在线人数:%1人").arg(ui->usrTblWidget->rowCount()));
sndMsg(UserEnter);
}
}
void Widget::userLeft(QString username, QString time)
{
bool isEmpty = ui->usrTblWidget->findItems(username, Qt::MatchExactly).isEmpty();
if(!isEmpty){
//寻找行
int row = ui->usrTblWidget->findItems(username, Qt::MatchExactly).first()->row();
//移除行
ui->usrTblWidget->removeRow(row);
//追加信息
ui->msgBrowser->setTextColor(Qt::gray);
ui->msgBrowser->append(username+"用户于"+time+"下线");
ui->userNumLbl->setText(QString("在线人数:%1").arg(ui->usrTblWidget->rowCount()));
}
}
void Widget::ReceiveMessage()
{
qint64 size = udpSocket->pendingDatagramSize();
int mysize = static_cast<int>(size);
QByteArray array = QByteArray(mysize, 0);
udpSocket->readDatagram(array.data(), size);
QDataStream stream(&array, QIODevice::ReadOnly);
int msgtype;
stream>>msgtype;
QString name,msg;
QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
switch(msgtype){
case Msg:
stream>>name>>msg;
//增加聊天记录
ui->msgBrowser->setTextColor(Qt::blue);
ui->msgBrowser->setCurrentFont(QFont("Times New Roman",12));
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;
}
}
Widget::~Widget()
{
delete ui;
}
void Widget::closeEvent(QCloseEvent *)
{
emit this->closeWidget();
sndMsg(UserLeft);
udpSocket->close();
udpSocket->destroyed();
}
四、项目优化
1、实现注册登录功能:与数据库进行连接。
2、界面美化:界面简单,未考虑美化问题。