Qt+QListWidget实现气泡聊天界面(附源码)
作者:音视频开发老舅
由于最近的项目需要,做了些相关IM的工作。所以聊天框也是必不可少的一部分。本文以QListWidget+QPainter绘制的Item做了一个Demo。该Demo只是做一个示例,感兴趣的可以了解一下
1、简介
由于最近的项目需要,做了些相关IM的工作。所以聊天框也是必不可少的一部分。聊天框的制作分很多种,本文以QListWidget+QPainter绘制的Item做了一个Demo。该Demo只是做一个示例,代码已公布如下,需要的拿去!
2、效果图
3、实现原理
气泡式聊天的显示是由QListWidget作为控件,每个气泡是由QListWidgetItem提升成QWidget来实现的。每个气泡可以理解位是一个QWidget,这样可以自由布置QWidget里面的内容。每个Item保存聊天的对话、发送状态、时间、种类等。
这个QWidget主要是显示一个头像+气泡,气泡里面是聊天的内容等。 气泡是在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,//系统 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_H
4.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为默认 //设置字间距% // te_font.setLetterSpacing(QFont::AbsoluteSpacing, 0); //设置字间距为3像素 //设置字间距像素值 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)); //内容 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); //内容 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实现气泡聊天界面(附源码)的详细内容,更多关于Qt QListWidget气泡聊天界面的资料请关注脚本之家其它相关文章!