Qt+QListWidget實現(xiàn)氣泡聊天界面(附源碼)
1、簡介
由于最近的項目需要,做了些相關(guān)IM的工作。所以聊天框也是必不可少的一部分。聊天框的制作分很多種,本文以QListWidget+QPainter繪制的Item做了一個Demo。該Demo只是做一個示例,代碼已公布如下,需要的拿去!
2、效果圖

3、實現(xiàn)原理
氣泡式聊天的顯示是由QListWidget作為控件,每個氣泡是由QListWidgetItem提升成QWidget來實現(xiàn)的。每個氣泡可以理解位是一個QWidget,這樣可以自由布置QWidget里面的內(nèi)容。每個Item保存聊天的對話、發(fā)送狀態(tài)、時間、種類等。
這個QWidget主要是顯示一個頭像+氣泡,氣泡里面是聊天的內(nèi)容等。 氣泡是在paintEvent事件中,采用QPainter來繪制的。
4、核心代碼
4.1、頭文件
#ifndef QNCHATMESSAGE_H
#define QNCHATMESSAGE_H
#include <QWidget>
class QPaintEvent;
class QPainter;
class QLabel;
class QMovie;
class QNChatMessage : public QWidget
{
Q_OBJECT
public:
explicit QNChatMessage(QWidget *parent = nullptr);
enum User_Type{
User_System,//系統(tǒng)
User_Me, //自己
User_She, //用戶
User_Time, //時間
};
void setTextSuccess();
void setText(QString text, QString time, QSize allSize, User_Type userType);
QSize getRealString(QString src);
QSize fontRect(QString str);
inline QString text() {
return m_msg;}
inline QString time() {
return m_time;}
inline User_Type userType() {
return m_userType;}
protected:
void paintEvent(QPaintEvent *event);
private:
QString m_msg;
QString m_time;
QString m_curTime;
QSize m_allSize;
User_Type m_userType = User_System;
int m_kuangWidth;
int m_textWidth;
int m_spaceWid;
int m_lineHeight;
QRect m_iconLeftRect;
QRect m_iconRightRect;
QRect m_sanjiaoLeftRect;
QRect m_sanjiaoRightRect;
QRect m_kuangLeftRect;
QRect m_kuangRightRect;
QRect m_textLeftRect;
QRect m_textRightRect;
QPixmap m_leftPixmap;
QPixmap m_rightPixmap;
QLabel* m_loading = Q_NULLPTR;
QMovie* m_loadingMovie = Q_NULLPTR;
bool m_isSending = false;
};
#endif // QNCHATMESSAGE_H4.2、源文件
#include "qnchatmessage.h"
#include <QFontMetrics>
#include <QPaintEvent>
#include <QDateTime>
#include <QPainter>
#include <QMovie>
#include <QLabel>
#include <QDebug>
QNChatMessage::QNChatMessage(QWidget *parent) : QWidget(parent)
{
QFont te_font = this->font();
te_font.setFamily("MicrosoftYaHei");
te_font.setPointSize(12);
// te_font.setWordSpacing(0);
// te_font.setLetterSpacing(QFont::PercentageSpacing,0);
// te_font.setLetterSpacing(QFont::PercentageSpacing, 100); //300%,100為默認(rèn) //設(shè)置字間距%
// te_font.setLetterSpacing(QFont::AbsoluteSpacing, 0); //設(shè)置字間距為3像素 //設(shè)置字間距像素值
this->setFont(te_font);
m_leftPixmap = QPixmap(":/img/Customer Copy.png");
m_rightPixmap = QPixmap(":/img/CustomerService.png");
m_loadingMovie = new QMovie(this);
m_loadingMovie->setFileName(":/img/loading4.gif");
m_loading = new QLabel(this);
m_loading->setMovie(m_loadingMovie);
m_loading->resize(16,16);
m_loading->setAttribute(Qt::WA_TranslucentBackground , true);
m_loading->setAutoFillBackground(false);
}
void QNChatMessage::setTextSuccess()
{
m_loading->hide();
m_loadingMovie->stop();
m_isSending = true;
}
void QNChatMessage::setText(QString text, QString time, QSize allSize, QNChatMessage::User_Type userType)
{
m_msg = text;
m_userType = userType;
m_time = time;
m_curTime = QDateTime::fromTime_t(time.toInt()).toString("hh:mm");
m_allSize = allSize;
if(userType == User_Me) {
if(!m_isSending) {
m_loading->move(m_kuangRightRect.x() - m_loading->width() - 10, m_kuangRightRect.y()+m_kuangRightRect.height()/2- m_loading->height()/2);
m_loading->show();
m_loadingMovie->start();
}
} else {
m_loading->hide();
}
this->update();
}
QSize QNChatMessage::fontRect(QString str)
{
m_msg = str;
int minHei = 30;
int iconWH = 40;
int iconSpaceW = 20;
int iconRectW = 5;
int iconTMPH = 10;
int sanJiaoW = 6;
int kuangTMP = 20;
int textSpaceRect = 12;
m_kuangWidth = this->width() - kuangTMP - 2*(iconWH+iconSpaceW+iconRectW);
m_textWidth = m_kuangWidth - 2*textSpaceRect;
m_spaceWid = this->width() - m_textWidth;
m_iconLeftRect = QRect(iconSpaceW, iconTMPH, iconWH, iconWH);
m_iconRightRect = QRect(this->width() - iconSpaceW - iconWH, iconTMPH, iconWH, iconWH);
QSize size = getRealString(m_msg); // 整個的size
qDebug() << "fontRect Size:" << size;
int hei = size.height() < minHei ? minHei : size.height();
m_sanjiaoLeftRect = QRect(iconWH+iconSpaceW+iconRectW, m_lineHeight/2, sanJiaoW, hei - m_lineHeight);
m_sanjiaoRightRect = QRect(this->width() - iconRectW - iconWH - iconSpaceW - sanJiaoW, m_lineHeight/2, sanJiaoW, hei - m_lineHeight);
if(size.width() < (m_textWidth+m_spaceWid)) {
m_kuangLeftRect.setRect(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), m_lineHeight/4*3, size.width()-m_spaceWid+2*textSpaceRect, hei-m_lineHeight);
m_kuangRightRect.setRect(this->width() - size.width() + m_spaceWid - 2*textSpaceRect - iconWH - iconSpaceW - iconRectW - sanJiaoW,
m_lineHeight/4*3, size.width()-m_spaceWid+2*textSpaceRect, hei-m_lineHeight);
} else {
m_kuangLeftRect.setRect(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), m_lineHeight/4*3, m_kuangWidth, hei-m_lineHeight);
m_kuangRightRect.setRect(iconWH + kuangTMP + iconSpaceW + iconRectW - sanJiaoW, m_lineHeight/4*3, m_kuangWidth, hei-m_lineHeight);
}
m_textLeftRect.setRect(m_kuangLeftRect.x()+textSpaceRect,m_kuangLeftRect.y()+iconTMPH,
m_kuangLeftRect.width()-2*textSpaceRect,m_kuangLeftRect.height()-2*iconTMPH);
m_textRightRect.setRect(m_kuangRightRect.x()+textSpaceRect,m_kuangRightRect.y()+iconTMPH,
m_kuangRightRect.width()-2*textSpaceRect,m_kuangRightRect.height()-2*iconTMPH);
return QSize(size.width(), hei);
}
QSize QNChatMessage::getRealString(QString src)
{
QFontMetricsF fm(this->font());
m_lineHeight = fm.lineSpacing();
int nCount = src.count("\n");
int nMaxWidth = 0;
if(nCount == 0) {
nMaxWidth = fm.width(src);
QString value = src;
if(nMaxWidth > m_textWidth) {
nMaxWidth = m_textWidth;
int size = m_textWidth / fm.width(" ");
int num = fm.width(value) / m_textWidth;
int ttmp = num*fm.width(" ");
num = ( fm.width(value) ) / m_textWidth;
nCount += num;
QString temp = "";
for(int i = 0; i < num; i++) {
temp += value.mid(i*size, (i+1)*size) + "\n";
}
src.replace(value, temp);
}
} else {
for(int i = 0; i < (nCount + 1); i++) {
QString value = src.split("\n").at(i);
nMaxWidth = fm.width(value) > nMaxWidth ? fm.width(value) : nMaxWidth;
if(fm.width(value) > m_textWidth) {
nMaxWidth = m_textWidth;
int size = m_textWidth / fm.width(" ");
int num = fm.width(value) / m_textWidth;
num = ((i+num)*fm.width(" ") + fm.width(value)) / m_textWidth;
nCount += num;
QString temp = "";
for(int i = 0; i < num; i++) {
temp += value.mid(i*size, (i+1)*size) + "\n";
}
src.replace(value, temp);
}
}
}
return QSize(nMaxWidth+m_spaceWid, (nCount + 1) * m_lineHeight+2*m_lineHeight);
}
void QNChatMessage::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);//消鋸齒
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(Qt::gray));
if(m_userType == User_Type::User_She) {
// 用戶
//頭像
// painter.drawRoundedRect(m_iconLeftRect,m_iconLeftRect.width(),m_iconLeftRect.height());
painter.drawPixmap(m_iconLeftRect, m_leftPixmap);
//框加邊
QColor col_KuangB(234, 234, 234);
painter.setBrush(QBrush(col_KuangB));
painter.drawRoundedRect(m_kuangLeftRect.x()-1,m_kuangLeftRect.y()-1,m_kuangLeftRect.width()+2,m_kuangLeftRect.height()+2,4,4);
//框
QColor col_Kuang(255,255,255);
painter.setBrush(QBrush(col_Kuang));
painter.drawRoundedRect(m_kuangLeftRect,4,4);
//三角
QPointF points[3] = {
QPointF(m_sanjiaoLeftRect.x(), 30),
QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 25),
QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 35),
};
QPen pen;
pen.setColor(col_Kuang);
painter.setPen(pen);
painter.drawPolygon(points, 3);
//三角加邊
QPen penSanJiaoBian;
penSanJiaoBian.setColor(col_KuangB);
painter.setPen(penSanJiaoBian);
painter.drawLine(QPointF(m_sanjiaoLeftRect.x() - 1, 30), QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 24));
painter.drawLine(QPointF(m_sanjiaoLeftRect.x() - 1, 30), QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 36));
//內(nèi)容
QPen penText;
penText.setColor(QColor(51,51,51));
painter.setPen(penText);
QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
painter.setFont(this->font());
painter.drawText(m_textLeftRect, m_msg,option);
} else if(m_userType == User_Type::User_Me) {
// 自己
//頭像
// painter.drawRoundedRect(m_iconRightRect,m_iconRightRect.width(),m_iconRightRect.height());
painter.drawPixmap(m_iconRightRect, m_rightPixmap);
//框
QColor col_Kuang(75,164,242);
painter.setBrush(QBrush(col_Kuang));
painter.drawRoundedRect(m_kuangRightRect,4,4);
//三角
QPointF points[3] = {
QPointF(m_sanjiaoRightRect.x()+m_sanjiaoRightRect.width(), 30),
QPointF(m_sanjiaoRightRect.x(), 25),
QPointF(m_sanjiaoRightRect.x(), 35),
};
QPen pen;
pen.setColor(col_Kuang);
painter.setPen(pen);
painter.drawPolygon(points, 3);
//內(nèi)容
QPen penText;
penText.setColor(Qt::white);
painter.setPen(penText);
QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
painter.setFont(this->font());
painter.drawText(m_textRightRect,m_msg,option);
} else if(m_userType == User_Type::User_Time) {
// 時間
QPen penText;
penText.setColor(QColor(153,153,153));
painter.setPen(penText);
QTextOption option(Qt::AlignCenter);
option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
QFont te_font = this->font();
te_font.setFamily("MicrosoftYaHei");
te_font.setPointSize(10);
painter.setFont(te_font);
painter.drawText(this->rect(),m_curTime,option);
}
}以上就是Qt+QListWidget實現(xiàn)氣泡聊天界面(附源碼)的詳細(xì)內(nèi)容,更多關(guān)于Qt QListWidget氣泡聊天界面的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C/C++中一次性執(zhí)行多個DOS命令的實現(xiàn)思路
在C語言中執(zhí)行DOS命令的方法很多,在這就不一給大家一一介紹了,本文重點給大家介紹C/C++中一次性執(zhí)行多個DOS命令的實現(xiàn)思路,需要的朋友參考下2017-12-12
仿現(xiàn)代C++智能指針實現(xiàn)引用計數(shù)
這篇文章主要為大家詳細(xì)介紹了如何仿現(xiàn)代C++智能指針實現(xiàn)引用計數(shù),文中的示例代碼講解詳細(xì),具有一定的借鑒價值,有需要的小伙伴可以了解下2024-03-03
深入解析C++的WNDCLASS結(jié)構(gòu)體及其在Windows中的應(yīng)用
這篇文章主要介紹了C++的WNDCLASS結(jié)構(gòu)體及其在Windows中的應(yīng)用,WNDCLASS被用來定義窗口,文中介紹了其諸多屬性,需要的朋友可以參考下2016-01-01
Win11+?VS2022編譯?FFmpeg6.0?靜態(tài)庫的詳細(xì)過程
這篇文章主要介紹了Win11+VS2022編譯FFmpeg6.0靜態(tài)庫的方法,本文通過圖文實例代碼相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08

