Qt模仿QQ聊天窗口界面(一)

简述

最近利用业余时间,模仿QQ做了一个聊天窗口界面,功能还不全,准备分几个部分做出来,还是看空闲时间了,以及广大网友的支持了。

效果图

QQ的聊天窗口

这里写图片描述

我做的效果图

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

代码篇

//TalkWindowShell.cpp
TalkWindowShell::TalkWindowShell(QWidget *parent)
    : BasicWindow(parent)
{
    ui.setupUi(this);
    initControl();

    {
        TalkWindow* talkwindow1 = new TalkWindow(this);
        TalkWindowItem* talkwindowItem1 = new TalkWindowItem(talkwindow1);
        talkwindow1->setWindowName(QStringLiteral("雨田哥-工作号-1号"));
        talkwindowItem1->setMsgLabelContent(QStringLiteral("雨田哥-1号"));
        talkwindow1->setWindowSignName(QStringLiteral("欢迎吐槽、雨田哥-工作号-1号"));
        addTalkWindow(talkwindow1, talkwindowItem1);
    }

    {
        TalkWindow* talkwindow2 = new TalkWindow(this);
        TalkWindowItem* talkwindowItem2 = new TalkWindowItem(talkwindow2);
        talkwindow2->setWindowName(QStringLiteral("雨田哥-工作号-2号-2号"));
        talkwindowItem2->setMsgLabelContent(QStringLiteral("雨田哥-2号"));
        talkwindow2->setWindowSignName(QStringLiteral("欢迎吐槽、雨田哥-工作号-2号-2号"));
        addTalkWindow(talkwindow2, talkwindowItem2);
    }

    {
        TalkWindow* talkwindow3 = new TalkWindow(this);
        TalkWindowItem* talkwindowItem3 = new TalkWindowItem(talkwindow3);
        talkwindow3->setWindowName(QStringLiteral("雨田哥-工作号-3号-3号-3号"));
        talkwindowItem3->setMsgLabelContent(QStringLiteral("雨田哥-3号"));
        talkwindow3->setWindowSignName(QStringLiteral("欢迎吐槽、雨田哥-工作号-3号-3号-3号"));
        addTalkWindow(talkwindow3, talkwindowItem3);
    }
}

TalkWindowShell::~TalkWindowShell()
{
    delete m_emotionWindow;
    m_emotionWindow = nullptr;
}

void TalkWindowShell::initControl()
{
    loadStyleSheet("TalkWindow");

    m_emotionWindow = new EmotionWindow;
    m_emotionWindow->hide();

    QList<int> leftWidgetSize;
    leftWidgetSize << 154 << width() - 154;
    ui.splitter->setSizes(leftWidgetSize);

    ui.listWidget->setStyle(new CustomProxyStyle(this));

    connect(ui.listWidget, &QListWidget::itemClicked, this, &TalkWindowShell::onTalkWindowItemClicked);
    connect(m_emotionWindow, SIGNAL(signalEmotionItemClicked(int)), this, SLOT(onEmotionItemClicked(int)));
}

void TalkWindowShell::onEmotionBtnClicked(bool)
{
    m_emotionWindow->setVisible(!m_emotionWindow->isVisible());
    QPoint emotionPoint = this->mapToGlobal(QPoint(0, 0));
    emotionPoint.setX(emotionPoint.x() + 170);
    emotionPoint.setY(emotionPoint.y() + 220);
    m_emotionWindow->move(emotionPoint);
}

void TalkWindowShell::onTalkWindowItemClicked(QListWidgetItem *item)
{
    QWidget* talkwindowWidget = m_talkwindowItemMap.find(item).value();
    ui.rightStackedWidget->setCurrentWidget(talkwindowWidget);
}

void TalkWindowShell::addTalkWindow(TalkWindow* talkwindow, TalkWindowItem* talkwindowitem)
{
    ui.rightStackedWidget->addWidget(talkwindow);
    connect(m_emotionWindow, SIGNAL(signalEmotionWindowHide()), talkwindow, SLOT(onSetEmotionBtnStatus()));

    QListWidgetItem *aItem = new QListWidgetItem(ui.listWidget);
    m_talkwindowItemMap.insert(aItem, talkwindow);

    aItem->setSelected(true);
    talkwindowitem->setHeadPixmap("");
    ui.listWidget->addItem(aItem);
    ui.listWidget->setItemWidget(aItem, talkwindowitem);

    onTalkWindowItemClicked(aItem);

    connect(talkwindowitem, &TalkWindowItem::signalCloseClicked, [talkwindowitem, talkwindow, aItem, this](){
        m_talkwindowItemMap.remove(aItem);
        talkwindow->close();
        ui.listWidget->takeItem(ui.listWidget->row(aItem));
        delete talkwindowitem;
        ui.rightStackedWidget->removeWidget(talkwindow);
        if (ui.rightStackedWidget->count() < 1)
            close();
    });
}

void TalkWindowShell::onEmotionItemClicked(int emotionNum)
{
    TalkWindow* curTalkWindow = dynamic_cast<TalkWindow*>(ui.rightStackedWidget->currentWidget());
    if (curTalkWindow)
    {
        curTalkWindow->addEmotionImage(emotionNum);
    }
}
TalkWindow.cpp
TalkWindow::TalkWindow(QWidget *parent)
    : QWidget(parent)
{
    setAttribute(Qt::WA_DeleteOnClose);
    ui.setupUi(this);
    initControl();
}

TalkWindow::~TalkWindow()
{

}

void TalkWindow::initControl()
{
    setSendBtnMenu();
    QList<int> rightWidgetSize;
    rightWidgetSize << 600 << 138;
    ui.bodySpliter->setSizes(rightWidgetSize);

    connect(ui.sysmin, SIGNAL(clicked(bool)), parent(), SLOT(onShowMin(bool)));
    connect(ui.sysclose, SIGNAL(clicked(bool)), parent(), SLOT(onShowClose(bool)));
    connect(ui.closeBtn, SIGNAL(clicked(bool)), parent(), SLOT(onShowClose(bool)));

    connect(ui.faceBtn, SIGNAL(clicked(bool)), parent(), SLOT(onEmotionBtnClicked(bool)));
    connect(ui.sendBtn, SIGNAL(clicked(bool)), this, SLOT(onSendBtnClicked(bool)));

    ui.msgWidget->setShowSkinControl(ui.skinLabel);
}

void TalkWindow::onSetEmotionBtnStatus()
{
    ui.faceBtn->setChecked(false);
}

void TalkWindow::setWindowName(const QString& name)
{
    ui.nameLabel->setText(name);
}

void TalkWindow::setWindowSignName(const QString& name)
{
    ui.signLabel->setText(name);
}

void TalkWindow::onSignalWindowclosed()
{
    close();
}

void TalkWindow::onSendBtnClicked(bool)
{
    ui.textEdit->clear();
    ui.textEdit->delteAllEmotionImage();

    MsgWidgetItem* msgItem = new MsgWidgetItem(this);
    int count = ui.msgLayout->count();
    ui.msgLayout->insertWidget(count - 1, msgItem);
}

void TalkWindow::addEmotionImage(int emotionNum)
{
    ui.textEdit->setFocus();
    ui.textEdit->addEmotionUrl(emotionNum);
}

void TalkWindow::setSendBtnMenu()
{
    QMenu* menu = new QMenu(this);
    menu->setWindowFlags(menu->windowFlags() | Qt::FramelessWindowHint);
    menu->setAttribute(Qt::WA_TranslucentBackground);
    menu->setObjectName("senMenu");
    m_sendAction = menu->addAction(QStringLiteral("按Enter键,发送消息"), this, SLOT(onEnterAction()));
    m_ctrlSendAction = menu->addAction(QStringLiteral("按Enter+Ctrl键,发送消息"), this, SLOT(onEnterCtrlAction()));
    
    QActionGroup* actiongroup = new QActionGroup(this);
    m_sendAction->setCheckable(true);
    m_ctrlSendAction->setCheckable(true);
    m_sendAction->setChecked(true);
    actiongroup->addAction(m_sendAction);
    actiongroup->addAction(m_ctrlSendAction);
    ui.sendBtn->setMenu(menu);
}

void TalkWindow::onEnterAction()
{
    m_sendAction->setChecked(true);

}

void TalkWindow::onEnterCtrlAction()
{
    m_ctrlSendAction->setChecked(true);
}
EmotionWindow.cpp
const int emotionColumn = 14;
const int emotionRow = std::ceil(170 / 14);

EmotionWindow::EmotionWindow(QWidget *parent)
    : QWidget(parent)
{
    setWindowFlags(Qt::FramelessWindowHint | Qt::SubWindow);
    setAttribute(Qt::WA_TranslucentBackground);
    setAttribute(Qt::WA_DeleteOnClose);

    ui.setupUi(this);
    initControl();
}

EmotionWindow::~EmotionWindow()
{

}

void EmotionWindow::initControl()
{
    CommonUtils::loadStyleSheet(this, "EmotionWindow");
    for (int row = 0; row < emotionRow; row++)
    {
        for (int column = 0; column < emotionColumn; column++)
        {
            EmotionLabelItem* label = new EmotionLabelItem(this);
            label->setEmotionName(row * emotionColumn + column);
            connect(label, &EmotionLabelItem::emotionClicked, this, &EmotionWindow::addEmotion);
            ui.gridLayout->addWidget(label, row, column);
        }
    }
}

void EmotionWindow::addEmotion(int emotionNum)
{
    hide();
    emit signalEmotionWindowHide();
    emit signalEmotionItemClicked(emotionNum);
}

void EmotionWindow::paintEvent(QPaintEvent *event)
{
    QStyleOption opt;
    opt.init(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
    __super::paintEvent(event);
}

void EmotionWindow::showEvent(QShowEvent *event)
{
    setFocus();
    __super::showEvent(event);
}

void EmotionWindow::focusOutEvent(QFocusEvent *event)
{
    QWidget* widget = qApp->widgetAt(QCursor::pos());
    if (widget != this && widget != ui.emotionWidget && widget != ui.topWidget)
    {
        hide();
        emit signalEmotionWindowHide();
    }

    __super::focusOutEvent(event);
}
//QMsgTextEdit.cpp
QMsgTextEdit::QMsgTextEdit(QWidget *parent)
    : QTextEdit(parent)
{

}

QMsgTextEdit::~QMsgTextEdit()
{
    delteAllEmotionImage();
}

void QMsgTextEdit::delteAllEmotionImage()
{
    for (auto itor = m_emotionMap.constBegin(); itor != m_emotionMap.constEnd(); ++itor)
    {
        delete itor.key();
    }
    m_emotionMap.clear();
}

void QMsgTextEdit::addEmotionUrl(int emotionNum)
{
    const QString&& imageName = QString(":/TalkWindowShell/Resources/emotion/%1.png").arg(emotionNum);
    const QString&& flagName = QString("apng-%1-apng").arg(imageName);
    insertHtml(QString("<img src='%1'/>").arg(flagName));
    if (m_listEmotionUrl.contains(imageName))
        return;
    else
        m_listEmotionUrl.append(imageName);

    QMovie* apngMovie = new QMovie(imageName, "apng", this);
    apngMovie->setCacheMode(QMovie::CacheNone);
    m_emotionMap.insert(apngMovie, flagName);

    connect(apngMovie, SIGNAL(frameChanged(int)), this, SLOT(onEmotionImageFrameChange(int)));
    apngMovie->start();
}

void QMsgTextEdit::onEmotionImageFrameChange(int frame)
{
    QMovie* movie = qobject_cast<QMovie*>(sender());
    document()->addResource(QTextDocument::ImageResource, QUrl(m_emotionMap.value(movie)), movie->currentPixmap());
    setLineWrapColumnOrWidth(lineWrapColumnOrWidth());
}
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容