跟着视频学习很多年前的一个小qt项目,简单还原的qq的群聊功能,过程中也是踩了一些小坑坑.
结果展示
本demo实现了基本的群聊功能和设置了聊天框的下划线,加粗,字体,颜色等等,最后还结合了文件的操作,可以将聊天记录进行保存.
群聊的用户
聊天界面及功能
实现过程
首先进行ui设计,在ui设计模式下面进行界面设计.
然后进行代码设计.
先对群聊部分进行代码设计,明确需求,这个界面需要显示可以参与到群聊里面的用户,以及实现点击用户,就进入弹出群聊界面,并且要传递一些参数进入界面,方便界面显示,代码中采用for循环放入用户,采用QVector容器保存按钮进行处理,isShow是标识,处理点击同一个用户按钮弹出多个相同用户框的情况,代码如下:
DialogList::DialogList(QWidget *parent) :
QWidget(parent),
ui(new Ui::DialogList)
{
ui->setupUi(this);
this->resize(250,700);
this->setWindowIcon(QPixmap(":/images/qq.png"));
this->setWindowTitle("MyselfQQ");
//创建9个按钮存放到QVector容器中
QVector<QToolButton *> vToolBtn; //声明存放QtoolButton的容器
//准备图标
QList<QString>nameList;
nameList << "斧头帮主" << "忆梦如澜" <<"北京出版人"<<"Cherry"<<"淡然"
<<"娇娇girl"<<"落水无痕"<<"青墨暖暖"<<"无语";
QStringList iconNameList; //图标资源列表
iconNameList << "ymrl"<< "ymrl" <<"qq" <<"Cherry"<< "qq"
<<"jj"<<"lswh"<<"qmnn"<<"qq";
//循环放入按钮
for(int i = 0;i<8;i++){
QToolButton * btn = new QToolButton;
btn->setText(nameList[i]);
//设置头像
QString str = QString(":/images/%1.png").arg(iconNameList.at(i));
btn->setIcon(QPixmap(str));
//设置头像大小
btn->setIconSize(QPixmap(str).size());
//设置按钮风格 透明
btn->setAutoRaise(true);
//设置文字和图片一起显示
btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
//加到 垂直布局中
ui->verticalLayout->addWidget(btn);
//容器保存住9个按钮,方便以后再次操作
vToolBtn.push_back(btn);
//9个标示默认初始化 防止点击同一个人出现多个窗口
isShow.push_back(false);
}
//将聊天的列表窗口和具体聊天的信息窗口进行关联 点击按钮弹出窗口
for(int i = 0;i<vToolBtn.size();i++){ //这里体现了上诉 使用容器保存九个按钮的作用
connect(vToolBtn[i],&QToolButton::clicked,this,[=](){
if(isShow[i]){
QMessageBox::warning(this," 警告",QString("用户1%的窗口已经弹出").arg(vToolBtn[i]->text()));
return;
}
isShow[i] = true;
//这是直接打开 , 尝试创建窗口是传入用户名
Widget * chatWidget = new Widget(0,vToolBtn[i]->text());
//设置窗口的标题
chatWidget->setWindowTitle(vToolBtn[i]->text());
//设置ICON
chatWidget->setWindowIcon(vToolBtn[i]->icon());
chatWidget->show();
connect(chatWidget,&Widget::closeWidget,[=](){
isShow[i] = false;
});
});
}
}
群聊部分实现过后,进行聊天界面部分的设计,这里也是采用udp通信,可以参考我之前写的udp demo进行研究,只不过群聊使用的是广播模式.这里进行设计的时候要一个功能一个功能的完成.
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);
ui->label->setText(QString("在线用户:1%人").arg(ui->tableWidget->rowCount()));
//点击发送按钮发送消息
connect(ui->send,&QPushButton::clicked,[=](){
sendMsg(Msg);
});
connect(ui->exit,&QPushButton::clicked,this,[=]()
{
this->close();
});
//监听发送的数据
connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::ReceiveMessage);
//字体设置
connect(ui->fontComboBox,&QFontComboBox::currentFontChanged,[=](const QFont &font){
ui->textEdit->setCurrentFont(font);
ui->textEdit->setFocus();
});
//字号
void(QComboBox::* cbxsignal)(const QString &text) = &QComboBox::currentIndexChanged;
connect(ui->comboBox,cbxsignal,[=](const QString &text){
ui->textEdit->setFontPointSize(text.toDouble());
ui->textEdit->setFocus();
});
//加粗
connect(ui->l1,&QToolButton::clicked,this,[=](){
static int x = 1;
// qDebug()<<"ddd";
/*if(isCheck)
{
qDebug()<<"选择加粗";
ui->textEdit->setFontWeight(100);
}
else
{
ui->textEdit->setFontWeight(25);
qDebug()<<"选择不加粗";
}*/
//上诉方法不晓得为什么不行 换成toggled点击没有反应 所以考虑使用奇偶次数来判断是否加粗
//看了一下帮助文档好像是点击默认里面的checked是false
if(x%2 == 1){
// qDebug()<<"选择加粗";
ui->textEdit->setFontWeight(99);
x++;
}
else{
ui->textEdit->setFontWeight(25);
//qDebug()<<"选择不加粗";
x++;
}
});
//倾斜
connect(ui->toolButton_2,&QToolButton::clicked,[=](){
/*qDebug()<<"ddd";
ui->textEdit->setFontItalic(check);*/
static int x = 1;
if(x%2 == 1){
ui->textEdit->setFontItalic(true);
x++;
}
else{
ui->textEdit->setFontItalic(false);
x++;
}
});
//这个按钮加上就不是默认的false了
ui->toolButton_3->setChecked(true);
//下划线
connect(ui->toolButton_3,&QToolButton::clicked,[=](bool check){
//clicked默认里面是是false 但是设置toggled点击了根本没进入
//已经解决
//原因 没有选中设置toolButton的checked 和 checkable属性
ui->textEdit->setFontUnderline(check);
});
//字体颜色
connect(ui->toolButton_4,&QToolButton::clicked,[=](){
QColor color = QColorDialog::getColor(Qt::red);
ui->textEdit->setTextColor(color);
});
connect(ui->toolButton_5,&QToolButton::clicked,this,[=](){
if(ui->textBrowser->document()->isEmpty())
{
QMessageBox::warning(this,"警告","聊天记录为空,无法保存!",QMessageBox::Ok);
}
else
{
QString fName = QFileDialog::getSaveFileName(this,"保存聊天记录","聊天记录","(*.txt)");
if(!fName.isEmpty())
{
//保存名称不为空再做保存操作
QFile file(fName);
file.open(QIODevice::WriteOnly | QFile::Text);
QTextStream stream(&file);
stream << ui->textBrowser->toPlainText();
file.close();
}
}
});
connect(ui->toolButton_6,&QToolButton::clicked,[=](){
ui->textBrowser->clear();
});
}
void Widget:: sendMsg(MsgType type){//广播UDP消息
//发送的消息分为三种类型
//发送的数据做分段处理 第一段是类型 第二段用户名 第三段是具体内容
//使用数据流 往array中写数据
QByteArray array;
QDataStream stream(&array,QIODevice::WriteOnly);
//第一段内容添加到流
stream<<type<<getUsr();
switch (type) {
case Msg://普通消息发送
if(ui->textEdit->toPlainText() == ""){
QMessageBox::warning(0,"警告","发送的内容不能为空",QMessageBox::Ok);
return;
}
stream<<getMsg();
break;
case UsrEnter:
break;
case UserLeft:
break;
default:
break;
}
//书写报文 广播发送
udpSocket->writeDatagram(array,QHostAddress::Broadcast,port);
}
//获取聊天信息
QString Widget:: getMsg(){
QString str = ui->textEdit->toHtml();
//清空输入框
ui->textEdit->clear();
ui->textEdit->setFocus();
return str;
}
//接收信息
void Widget:: ReceiveMessage(){
qint64 size = udpSocket->pendingDatagramSize();
//拿到数据报文
QByteArray array = QByteArray(size,0);
udpSocket->readDatagram(array.data(),size);
//解析数据
//第一段类型
QDataStream stram(&array,QIODevice::ReadOnly);
int magType;
QString usrName;
QString msg;
//获取当前时间
QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
//读取到类型
stram >>magType;
switch (magType) {
case Msg://普通聊天
stram>>usrName>>msg;
//追加聊天记录
ui->textBrowser->setTextColor(Qt::blue);
ui->textBrowser->append("[" +usrName +"]" + time);
ui->textBrowser->append(msg);
break;
case UsrEnter:
stram>>usrName;
usrEnter(usrName);
break;
case UserLeft:
stram>>usrName;
usrLeft(usrName,time);
break;
default:
break;
}
}
void Widget:: closeEvent(QCloseEvent * e){
//跨指窗口 进行操作
emit this->closeWidget(); //发送自定义的信号
sendMsg(UserLeft);
//断开套接字
udpSocket->close();
udpSocket->destroyed();
QWidget::closeEvent(e);
}
QString Widget::getUsr(){
return this->uName;
}
void Widget:: usrEnter(QString username){
//更新右侧的TableWidget
//stram>>usrName;
if(ui->tableWidget->findItems(username,Qt::MatchExactly).isEmpty()){
QTableWidgetItem * usr = new QTableWidgetItem(username);
//插入行
ui->tableWidget->insertRow(0);
ui->tableWidget->setItem(0,0,usr);
//追加聊天记录
ui->textBrowser->setTextColor(Qt::gray);
ui->textBrowser->append(QString("%1 上线了").arg(username));
//在线人数更新
ui->label->setText(QString("在线用户:%1人").arg(ui->tableWidget->rowCount()));
//把自己的信息广播出去
sendMsg(UsrEnter);
}
}
void Widget:: usrLeft(QString username, QString time){
if(!ui->tableWidget->findItems(username,Qt::MatchExactly).isEmpty()){
int rowNum = ui->tableWidget->findItems(username, Qt::MatchExactly).first()->row();
ui->tableWidget->removeRow(rowNum);
ui->textBrowser->setTextColor(Qt::gray);
ui->textBrowser->setCurrentFont(QFont("Times New Roman", 10));
ui->textBrowser->append(QString("%1 于 %2 离开!").arg(username).arg(time));
ui->label->setText(QString("在线人数:%1").arg(ui->tableWidget->rowCount()));
}
}
最后对这次踩的最无语的坑进行总结,在上诉代码之中的注释我也有说明,就是关于按钮点击的clicked信号.
这里看到帮助文档之中,信号后面的bool值默认的是false.
要想取消这个默认值,而是由我们点击判断状态值,需要设计按钮的checked属性.
或者还可以再ui界面中进行设置
上图的checked勾上即可 ,toggled设置的便是checkable.