一、使用工具
本次整个项目的开发基于Qt Creator 5.14.2进行的开发与调试,主要通信功能采用UDPSocket完成,是我学习Qt过程中联系的小案例,过程中遇到的bug和完整代码会尽量展现出来,关于Qt以及其安装流程在优快云里面有很多大佬写的教程,十分详细!!!
二、软件实例效果图
三、完整代码解读:
dialoglist.h
#ifndef DIALOGLIST_H
#define DIALOGLIST_H
#include <QVector>
#include <QWidget>
namespace Ui {
class DialogList;
}
class DialogList : public QWidget
{
Q_OBJECT
public:
explicit DialogList(QWidget *parent = nullptr);
~DialogList();
QVector<bool> isshow; //判断当前窗口是否已被显示出
private:
Ui::DialogList *ui;
};
#endif // DIALOGLIST_H
dialoglist.cpp
#include "dialoglist.h"
#include "ui_dialoglist.h"
#include <QToolButton>
#include <widget.h>
#include <QString>
#include <QList>
#include <QMessageBox>
DialogList::DialogList(QWidget *parent) :
QWidget(parent),
ui(new Ui::DialogList)
{
ui->setupUi(this);
setWindowIcon(QPixmap(":/images/校标.png"));
//容器保存10个toolbutton
QVector<QToolButton*> toolvector;
//记录头像图名加入泛型集合
QList<QString> name;
name<<"copy.Image"<<"cut.Image"<<"Hearts"<<"new.Image"<<"open.Image"<<"paste.Image"<<"print.Image"
<<"printPreview.Image"<<"save.Image"<<"校标";
//在toolbox容器中动态生成toolbutton按钮
for (int i=0;i<name.size();i++)
{
QToolButton *btn = new QToolButton;
btn->setText(name[i]);
QString str = QString(":/images/"+name[i]+".png");
btn->setIcon(QPixmap(str));
btn->setIconSize(QSize(72,72));
btn->setAutoRaise(true);
btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
ui->vLayout->addWidget(btn);
//将创建的10个toolbutton全部放入容器保存
toolvector.push_back (btn);
isshow.push_back(false);
}
//对10个toolbutton添加信号和槽函数
for (int i=0;i<toolvector.size();i++)
{
connect (toolvector[i],&QToolButton::clicked,[=](){
if(isshow[i]==true)
{
QMessageBox::warning(this,"警告","当前窗口已被打开");
return;
}
else
{
isshow[i]=true;
Widget *widget = new Widget(0,toolvector[i]->text());
widget->setWindowTitle(toolvector[i]->text());
widget->setWindowIcon(toolvector[i]->icon());
widget->show();
connect (widget,&Widget::closeWidget,[=](){
isshow[i]=false;
});
}
});
}
}
DialogList::~DialogList()
{
delete ui;
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include<QString>
#include <QWidget>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent,QString name);
~Widget();
private:
Ui::Widget *ui;
signals:
//关闭窗口发送关闭信号
void closeWidget();
public:
//窗口关闭事件
void closeEvent(QCloseEvent *);
public:
enum MsgType{Msg,UsrEnter,UsrLeft};
void sendMsg(MsgType type); //广播UDP消息
void usrEnter(QString usrname); //新用户进入
void usrLeft(QString usrname,QString time); //处理用户离开
QString getUsr(); //获取用户名
QString getMsg(); //获取聊天信息
private:
QUdpSocket *udpSocket; //UDP套接字
qint16 port; //端口
QString Uname; //用户名
void ReceiveMsg(); //接收UDP消息
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QByteArray>
#include <QDataStream>
#include <QMessageBox>
#include <QDateTime>
#include <QColorDialog>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
Widget::Widget(QWidget *parent,QString name)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
udpSocket = new QUdpSocket(this);
//用户名获取
Uname=name;
//端口号获取
this->port=9999;
//绑定端口号 共享地址 和 断线重连
udpSocket->bind(port,QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
//发送新用户进入
sendMsg(UsrEnter);
//点击按钮发送信息
connect (ui->sendbtn,&QPushButton::clicked,[=](){
sendMsg(Msg);
});
//监听别人发送的数据
connect (udpSocket,&QUdpSocket::readyRead,this,&Widget::ReceiveMsg);
//退出程序
connect (ui->quitbtn,&QPushButton::clicked,[=](){
this->close();
});
/辅助功能///
//设置字体
connect(ui->fontComboBox,&QFontComboBox::currentFontChanged,[=](const QFont &font){
ui->msgtextEdit->setFont(font);
});
//设置字号[应用函数指针]
void(QComboBox:: *p)(const QString &text) =&QComboBox::currentIndexChanged;
connect(ui->sizecomboBox, p ,[=](const QString &text){
ui->msgtextEdit->setFontPointSize(text.toDouble());
ui->msgtextEdit->setFocus();
});
//加粗
connect(ui->Boldbtn,&QToolButton::clicked,[=](bool checked){
if (checked)
{
ui->msgtextEdit->setFontWeight(QFont::Bold);
}
else
{
ui->msgtextEdit->setFontWeight(QFont::Normal);
}
});
//倾斜
connect(ui->Itabtn,&QToolButton::clicked,[=](bool checked){
if (checked)
{
ui->msgtextEdit->setFontItalic(true);
}
else
{
ui->msgtextEdit->setFontItalic(false);
}
});
//下划线
connect(ui->underlinebtn,&QToolButton::clicked,[=](bool checked){
if (checked)
{
ui->msgtextEdit->setFontUnderline(true);
}
else
{
ui->msgtextEdit->setFontUnderline(false);
}
});
//字体颜色
connect(ui->colorbtn,&QToolButton::clicked,[=](){
QColor color = QColorDialog::getColor(Qt::red);
ui->msgtextEdit->setTextColor(color);
});
//清空删除聊天记录
connect(ui->deletebtn,&QToolButton::clicked,[=](){
ui->msgBrowser->clear();
});
//保存聊天记录
connect(ui->savebtn,&QToolButton::clicked,[=](){
if (ui->msgBrowser->document ()->isEmpty())
{
QMessageBox::warning(this,"警告","聊天记录不能为空");
return;
}
else
{
QString savepath = QFileDialog::getSaveFileName (this,"save","聊天记录","(*.txt)");
if (savepath.isEmpty())
{
QMessageBox::warning(this,"警告","路径不能为空");
return;
}
else
{
QFile file(savepath);
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&file);
stream << ui->msgBrowser->toPlainText();
file.close();
QMessageBox::information (this,"Tip","保存成功");
}
}
});
}
Widget::~Widget()
{
delete ui;
}
//窗口关闭事件
void Widget::closeEvent(QCloseEvent *)
{
emit this->closeWidget();
//发送用户离开
sendMsg(UsrLeft);
//关闭套接字
udpSocket->close ();
udpSocket->destroyed ();
}
//广播UDP消息
void Widget::sendMsg(MsgType type)
{
QByteArray array=QByteArray(10000,0);
QDataStream stream(&array,QIODevice::WriteOnly);
//第一段 获取信息类型 第二段 用户名
stream<<type<<getUsr();
switch (type)
{
case Msg:
//如果输入框为空,则提示警告
if (ui->msgtextEdit->toPlainText()=="") {
QMessageBox::warning(this,"Tip Message","输入框不允许为空",QMessageBox::Close);
return;
}
//第三段 合并获取到的消息
stream<<getMsg();
break;
case UsrEnter: //用户进入信息
break;
case UsrLeft: //用户离开信息
break;
default:
break;
}
//书写报文 广播发送
udpSocket->writeDatagram(array,QHostAddress::Broadcast,port);
}
//新用户进入
void Widget::usrEnter(QString usrname)
{
bool isempty = ui->usrtableWidget->findItems (usrname,Qt::MatchExactly).isEmpty();
if (isempty)
{
ui->msgBrowser->setTextColor (Qt::gray);
ui->msgBrowser->append (usrname+" 上线了!");
QTableWidgetItem *usr = new QTableWidgetItem(usrname);
ui->usrtableWidget->insertRow(0);
ui->usrtableWidget->setItem(0,0,usr);
QString online = QString("在线人数为:%1").arg(ui->usrtableWidget->rowCount());
ui->label->setText (online);
//把自身信息广播出去
sendMsg (UsrEnter);
}
}
//处理用户离开
void Widget::usrLeft(QString usrname,QString time)
{
bool isempty = ui->usrtableWidget->findItems (usrname,Qt::MatchExactly).isEmpty();
if (!isempty)
{
int r = ui->usrtableWidget->findItems(usrname,Qt::MatchExactly).first()->row();
ui->usrtableWidget->removeRow(r);
//追加聊天记录
ui->msgBrowser->setTextColor(Qt::gray);
ui->msgBrowser->append (QString("%1 at %2 离开").arg (usrname).arg(time));
//在线人数更新
ui->label->setText (QString("在线人数为:%1").arg(ui->usrtableWidget->rowCount()));
}
}
//获取用户名
QString Widget::getUsr()
{
return this->Uname;
}
//获取聊天信息
QString Widget::getMsg()
{
QString str = ui->msgtextEdit->toHtml();
ui->msgtextEdit->clear();
ui->msgtextEdit->setFocus();
return str;
}
//接收UDP消息
void Widget::ReceiveMsg()
{
//拿到数据报文
qint64 size = udpSocket->pendingDatagramSize();
QByteArray array=QByteArray(10000,0);
udpSocket->readDatagram(array.data(),size);
//解析数据
QDataStream stream(&array,QIODevice::ReadOnly);
int msgType;
QString usrName;
QString msg;
QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
stream>>msgType;
switch (msgType)
{
case Msg: //普通聊天
{
stream>>usrName>>msg;
//追加聊天记录
ui->msgBrowser->setTextColor(Qt::blue);
ui->msgBrowser->append("["+usrName+"]"+time);
ui->msgBrowser->append(msg);
break;
}
case UsrEnter:
{
stream>>usrName;
usrEnter(usrName);
break;
}
case UsrLeft:
{
stream>>usrName;
QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
usrLeft(usrName,time);
break;
}
default:
break;
}
}
★UDP通信使用流程如下:
关键核心易出错部分,解析:
1.针对有重载的信号或槽函数,使用时要注意用到函数指针 void(作用域::*p)(int a) =&...
//设置字号[应用函数指针]
void(QComboBox:: *p)(const QString &text) =&QComboBox::currentIndexChanged;
connect(ui->sizecomboBox, p ,[=](const QString &text){
ui->msgtextEdit->setFontPointSize(text.toDouble());
ui->msgtextEdit->setFocus();
});
2.写文本文件到本地,将指定文本保存到本地:
QString savepath = QFileDialog::getSaveFileName (this,"save","聊天记录","(*.txt)");
if (savepath.isEmpty())
{
QMessageBox::warning(this,"警告","路径不能为空");
return;
}
else
{
QFile file(savepath);
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&file);
stream << ui->msgBrowser->toPlainText();
file.close();
QMessageBox::information (this,"Tip","保存成功");
}
3. 广播UDP信息:
//广播UDP消息
void Widget::sendMsg(MsgType type)
{
QByteArray array=QByteArray(10000,0);
QDataStream stream(&array,QIODevice::WriteOnly);
//第一段 获取信息类型 第二段 用户名
stream<<type<<getUsr();
switch (type)
{
case Msg:
//如果输入框为空,则提示警告
if (ui->msgtextEdit->toPlainText()=="") {
QMessageBox::warning(this,"Tip Message","输入框不允许为空",QMessageBox::Close);
return;
}
//第三段 合并获取到的消息
stream<<getMsg();
break;
case UsrEnter: //用户进入信息
break;
case UsrLeft: //用户离开信息
break;
default:
break;
}
//书写报文 广播发送
udpSocket->writeDatagram(array,QHostAddress::Broadcast,port);
}
4. 接收DUP信息:
//接收UDP消息
void Widget::ReceiveMsg()
{
//拿到数据报文
qint64 size = udpSocket->pendingDatagramSize();
QByteArray array=QByteArray(10000,0);
udpSocket->readDatagram(array.data(),size);
//解析数据
QDataStream stream(&array,QIODevice::ReadOnly);
int msgType;
QString usrName;
QString msg;
QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
stream>>msgType;
switch (msgType)
{
case Msg: //普通聊天
{
stream>>usrName>>msg;
//追加聊天记录
ui->msgBrowser->setTextColor(Qt::blue);
ui->msgBrowser->append("["+usrName+"]"+time);
ui->msgBrowser->append(msg);
break;
}
case UsrEnter:
{
stream>>usrName;
usrEnter(usrName);
break;
}
case UsrLeft:
{
stream>>usrName;
QString time = QDateTime::currentDateTime().toString ("yyyy-MM-dd hh:mm:ss");
usrLeft(usrName,time);
break;
}
default:
break;
}
}
5. 向控件TableWidget中插入内容(以下例子为插入指定的usrname):
bool isempty = ui->usrtableWidget->findItems(usrname,Qt::MatchExactly).isEmpty();
if (isempty)
{
QTableWidgetItem *usr = new QTableWidgetItem(usrname);
ui->usrtableWidget->insertRow(0);
ui->usrtableWidget->setItem(0,0,usr);
}
后续其他练习实例同步更新,完结!