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_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為默認(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-01Win11+?VS2022編譯?FFmpeg6.0?靜態(tài)庫的詳細(xì)過程
這篇文章主要介紹了Win11+VS2022編譯FFmpeg6.0靜態(tài)庫的方法,本文通過圖文實例代碼相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08