Qt之模仿QQ主面板界面

效果图

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

这里大家值得注意的是,QQ等级设置,已经列表展开搜索时的三角符的旋转效果

这里写图片描述
//QQMainWindow.cpp
#include "qqmainwindow.h"
#include "contactitem.h"
#include "rootcontatitem.h"
#include "skinwindow.h"

#include <QPainter>
#include <QMouseEvent>
#include <QSpacerItem>
#include <QProxyStyle>
#include <QTimer>

class CustomProxyStyle : public QProxyStyle
{
public:
    virtual void drawPrimitive(PrimitiveElement element, const QStyleOption * option,
        QPainter * painter, const QWidget * widget = 0) const
    {
        if (PE_FrameFocusRect == element)
        {
            return;
        }
        else
        {
            QProxyStyle::drawPrimitive(element, option, painter, widget);
        }
    }
};

QQMainWindow::QQMainWindow(QWidget *parent)
    : BasicWindow(parent)
{
    ui.setupUi(this);
    m_styleName = "MainWindow";
    loadStyleSheet("MainWindow");
    initControl();

    QTimer* timer = new QTimer(this);
    timer->setInterval(500);
    connect(timer, &QTimer::timeout, [this](){
        static int level = 0;
        if (level == 99)
        {
            level = 0;
        }
        setLevelPixmap(level);
        level++;
    });
    timer->start();
}

QQMainWindow::~QQMainWindow()
{

}

void QQMainWindow::initControl()
{
    ui.treeWidget->setStyle(new CustomProxyStyle);

    setLevelPixmap(0);
    setUserName(QString::fromLocal8Bit("雨田哥-工作号"));
    setHeadPixmap(":/QQMainWindow/Resources/MainWindow/yutiange.jpg");
    setStatusMenuIcon(":/QQMainWindow/Resources/MainWindow/StatusSucceeded.png");

    QHBoxLayout *flowLayout = new QHBoxLayout();
    flowLayout->setContentsMargins(0, 0, 0, 0);
    flowLayout->setSpacing(2);
    flowLayout->addWidget(addOtherAppExtension(":/QQMainWindow/Resources/MainWindow/app/app_7.png", "app_7"));
    flowLayout->addWidget(addOtherAppExtension(":/QQMainWindow/Resources/MainWindow/app/app_2.png", "app_2"));
    flowLayout->addWidget(addOtherAppExtension(":/QQMainWindow/Resources/MainWindow/app/app_3.png", "app_3"));
    flowLayout->addWidget(addOtherAppExtension(":/QQMainWindow/Resources/MainWindow/app/app_4.png", "app_4"));
    flowLayout->addWidget(addOtherAppExtension(":/QQMainWindow/Resources/MainWindow/app/app_5.png", "app_5"));
    flowLayout->addWidget(addOtherAppExtension(":/QQMainWindow/Resources/MainWindow/app/app_6.png", "app_6"));
    flowLayout->addStretch();
    flowLayout->addWidget(addOtherAppExtension(":/QQMainWindow/Resources/MainWindow/app/skin.png", "app_skin"));
    ui.appWidget->setLayout(flowLayout);

    ui.bottomLayout_up->addWidget(addOtherAppExtension(":/QQMainWindow/Resources/MainWindow/app/app_10.png", "app_10"));
    ui.bottomLayout_up->addWidget(addOtherAppExtension(":/QQMainWindow/Resources/MainWindow/app/app_8.png", "app_8"));
    ui.bottomLayout_up->addWidget(addOtherAppExtension(":/QQMainWindow/Resources/MainWindow/app/app_11.png", "app_11"));
    ui.bottomLayout_up->addWidget(addOtherAppExtension(":/QQMainWindow/Resources/MainWindow/app/app_9.png", "app_9"));
    ui.bottomLayout_up->addStretch();

    ui.lineEdit->installEventFilter(this);
    ui.searchLineEdit->installEventFilter(this);

    initContactTree();
    initStrangerTree();

    connect(ui.sysmin, SIGNAL(clicked(bool)), this, SLOT(onShowMin(bool)));
    connect(ui.sysclose, SIGNAL(clicked(bool)), this, SLOT(onShowClose(bool)));
}

void QQMainWindow::setHeadPixmap(const QString& headPath)
{
    ui.headLabel->setPixmap(getRoundImage(QPixmap(headPath), QPixmap(":/QQMainWindow/Resources/MainWindow/head_mask.png"), ui.headLabel->size()));
}

void QQMainWindow::setUserName(const QString& username)
{
    ui.nameLabel->adjustSize();
    QString name = ui.nameLabel->fontMetrics().elidedText(username, Qt::ElideRight, ui.nameLabel->width());
    ui.nameLabel->setText(name);
}

void QQMainWindow::setStatusMenuIcon(const QString& statusPath)
{
    QPixmap statusBtnPixmap(ui.statusBtn->size());
    statusBtnPixmap.fill(Qt::transparent);
    QPainter painter(&statusBtnPixmap);
    painter.drawPixmap(4, 4, QPixmap(statusPath));
    painter.drawPixmap(16, 3, QPixmap(":/QQMainWindow/Resources/MainWindow/arrow_normal.gft.png"));
    ui.statusBtn->setIcon(statusBtnPixmap);
    ui.statusBtn->setIconSize(ui.statusBtn->size());
}

void QQMainWindow::setLevelPixmap(int level)
{
    QPixmap levelPixmap(ui.levelBtn->size());
    levelPixmap.fill(Qt::transparent);
    QPainter painter(&levelPixmap);
    painter.drawPixmap(0, 4, QPixmap(":/QQMainWindow/Resources/MainWindow/lv.png"));
    int unitNum = level / 1 % 10;//取个位数字
    int tenNum = level / 10 % 10;//取十位数字
    //十位
    painter.drawPixmap(10, 4, QPixmap(":/QQMainWindow/Resources/MainWindow/levelvalue.png"), tenNum * 6, 0, 6, 7);
    //个位
    painter.drawPixmap(16, 4, QPixmap(":/QQMainWindow/Resources/MainWindow/levelvalue.png"), unitNum * 6, 0, 6, 7);
    ui.levelBtn->setIcon(levelPixmap);
    ui.levelBtn->setIconSize(ui.levelBtn->size());
}

QWidget* QQMainWindow::addOtherAppExtension(const QString& apppath, const QString& appName)
{
    QPushButton* btn = new QPushButton(this);
    btn->setFixedSize(20, 20);
    QPixmap pixmap(btn->size());
    pixmap.fill(Qt::transparent);
    QPainter painter(&pixmap);
    QPixmap appPixmap(apppath);
    painter.drawPixmap((btn->width() - appPixmap.width()) / 2, (btn->height() - appPixmap.height())/2, appPixmap);
    btn->setIcon(pixmap);
    btn->setIconSize(btn->size());
    btn->setProperty("hasborder", true);
    btn->setObjectName(appName);
    connect(btn, &QPushButton::clicked, this, &QQMainWindow::onAppIconCliked);
    return btn;
}

void QQMainWindow::onAppIconCliked()
{
    if (sender()->objectName() == "app_skin")
    {
        SkinWindow* skinWindow = new SkinWindow();
        skinWindow->show();
    }
}

void QQMainWindow::resizeEvent(QResizeEvent *event)
{
    setUserName(QString::fromLocal8Bit("雨田哥-工作号"));
    __super::resizeEvent(event);
}

bool QQMainWindow::eventFilter(QObject *obj, QEvent *event)
{
    if (ui.searchLineEdit == obj)
    {
        if (event->type() == QEvent::FocusIn)
        {
            ui.searchWidget->setStyleSheet("QWidget#searchWidget {background-color:rgb(255, 255, 255);}\
                                            QPushButton#searchBtn {border-image:url(:/QQMainWindow/Resources/MainWindow/search/main_search_deldown.png);}\
                                            QPushButton#searchBtn:hover {border-image:url(:/QQMainWindow/Resources/MainWindow/search/main_search_delhighlight.png);}\
                                            QPushButton#searchBtn:pressed{ border-image:url(:/QQMainWindow/Resources/MainWindow/search/main_search_delhighdown.png);}");
                                            
        }
        else if (event->type() == QEvent::FocusOut)
        {
            ui.searchWidget->setStyleSheet(QString("QWidget#searchWidget {background-color:rgba(%1,%2,%3,50);}\
                                                    QPushButton#searchBtn {border-image:url(:/QQMainWindow/Resources/MainWindow/search/search_icon.png);}").arg(m_colorBackGround.red()).arg(m_colorBackGround.green()).arg(m_colorBackGround.blue()));
        }
    }
    return __super::eventFilter(obj, event);
}

void QQMainWindow::mousePressEvent(QMouseEvent *event)
{
    if (qApp->widgetAt(event->pos()) != ui.lineEdit && ui.lineEdit->hasFocus())
    {
        ui.lineEdit->clearFocus();
    }
    else if (qApp->widgetAt(event->pos()) != ui.searchLineEdit && ui.searchLineEdit->hasFocus())
    {
        ui.searchLineEdit->clearFocus();
    }
    __super::mousePressEvent(event);
}

void QQMainWindow::initContactTree()
{
    //展开和收缩时信号,以达到变更我三角图片;
    connect(ui.treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(onItemClicked(QTreeWidgetItem *, int)));
    connect(ui.treeWidget, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(onItemExpanded(QTreeWidgetItem *)));
    connect(ui.treeWidget, SIGNAL(itemCollapsed(QTreeWidgetItem *)), this, SLOT(onItemCollapsed(QTreeWidgetItem *)));
    //分组节点
    QTreeWidgetItem *pRootFriendItem = new QTreeWidgetItem();
    pRootFriendItem->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
    //设置Data用于区分,Item是分组节点还是子节点,0代表分组节点,1代表子节点
    pRootFriendItem->setData(0, Qt::UserRole, 0);
    RootContatItem *pItemName = new RootContatItem(ui.treeWidget);
    
    int nMyFriendNum = 6;
    QString qsGroupName = QString::fromLocal8Bit("我的好友 %1/%2").arg(0).arg(nMyFriendNum);
    pItemName->setText(qsGroupName);
    //擦入分组节点
    ui.treeWidget->addTopLevelItem(pRootFriendItem);
    ui.treeWidget->setItemWidget(pRootFriendItem, 0, pItemName);

    for (int nIndex = 0; nIndex < nMyFriendNum; ++nIndex)
    {
        //添加子节点
        addMyFriendInfo(pRootFriendItem);
    }
}

void QQMainWindow::addMyFriendInfo(QTreeWidgetItem* pRootGroupItem)
{
    QTreeWidgetItem *pChild = new QTreeWidgetItem();
    //添加子节点
    pChild->setData(0, Qt::UserRole, 1);
    ContactItem* pContactItem = new ContactItem(ui.treeWidget);
    pContactItem->setHeadPixmap(getRoundImage(QPixmap(":/QQMainWindow/Resources/MainWindow/yutiange.jpg"), QPixmap(":/QQMainWindow/Resources/MainWindow/head_mask.png"), pContactItem->getHeadLabelSize()));
    pContactItem->setUserName(QString::fromLocal8Bit("雨田哥-工作号"));
    pContactItem->setSignName(QString::fromLocal8Bit("欢迎访问雨田哥工作号"));
    pRootGroupItem->addChild(pChild);
    ui.treeWidget->setItemWidget(pChild, 0, pContactItem);
}

void QQMainWindow::initStrangerTree()
{
    //分组节点
    QTreeWidgetItem *pRootFriendItem = new QTreeWidgetItem();
    pRootFriendItem->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
    //设置Data用于区分,Item是分组节点还是子节点,0代表分组节点,1代表子节点
    pRootFriendItem->setData(0, Qt::UserRole, 0);
    RootContatItem *pItemName = new RootContatItem(ui.treeWidget);

    int nMyFriendNum = 8;
    QString qsGroupName = QString::fromLocal8Bit("陌生人 %1/%2").arg(0).arg(nMyFriendNum);
    pItemName->setText(qsGroupName);
    //擦入分组节点
    ui.treeWidget->addTopLevelItem(pRootFriendItem);
    ui.treeWidget->setItemWidget(pRootFriendItem, 0, pItemName);

    for (int nIndex = 0; nIndex < nMyFriendNum; ++nIndex)
    {
        //添加子节点
        addStarngerInfo(pRootFriendItem);
    }
}

void QQMainWindow::addStarngerInfo(QTreeWidgetItem* pRootGroupItem)
{
    QTreeWidgetItem *pChild = new QTreeWidgetItem();
    //添加子节点
    pChild->setData(0, Qt::UserRole, 1);
    ContactItem* pContactItem = new ContactItem(ui.treeWidget);
    pContactItem->setHeadPixmap(getRoundImage(QPixmap(":/QQMainWindow/Resources/MainWindow/yutiange.jpg"), QPixmap(":/QQMainWindow/Resources/MainWindow/head_mask.png"), pContactItem->getHeadLabelSize()));
    pContactItem->setUserName(QString::fromLocal8Bit("雨田哥-工作号-陌生人"));
    pContactItem->setSignName(QString::fromLocal8Bit("欢迎访问雨田哥工作号-陌生人"));
    pRootGroupItem->addChild(pChild);
    ui.treeWidget->setItemWidget(pChild, 0, pContactItem);
}

void QQMainWindow::onItemClicked(QTreeWidgetItem * item, int column)
{
    bool bIsChild = item->data(0, Qt::UserRole).toBool();
    if (!bIsChild)
    {
        item->setExpanded(!item->isExpanded());
    }
}

void QQMainWindow::onItemExpanded(QTreeWidgetItem * item)
{
    bool bIsChild = item->data(0, Qt::UserRole).toBool();
    if (!bIsChild)
    {
        RootContatItem *prootItem = dynamic_cast<RootContatItem*>(ui.treeWidget->itemWidget(item, 0));
        if (prootItem)
        {
            prootItem->setExpanded(true);
        }
    }
}

void QQMainWindow::onItemCollapsed(QTreeWidgetItem * item)
{
    bool bIsChild = item->data(0, Qt::UserRole).toBool();
    if (!bIsChild)
    {
        RootContatItem *prootItem = dynamic_cast<RootContatItem*>(ui.treeWidget->itemWidget(item, 0));
        if (prootItem)
        {
            prootItem->setExpanded(false);
        }
    }
}
SkinWindow.cpp
#include "skinwindow.h"
#include "qclicklabel.h"
#include "notifymanager.h"

SkinWindow::SkinWindow(QWidget *parent)
    : BasicWindow(parent)
{
    ui.setupUi(this);
    m_styleName = "SkinWindow";
    initControl();
    setAttribute(Qt::WA_DeleteOnClose);
    loadStyleSheet("SkinWindow");
}

SkinWindow::~SkinWindow()
{

}

void SkinWindow::initControl()
{
    QList<QColor> colorLst = { QColor(22, 154, 218), QColor(40, 138, 221), QColor(49, 166, 107), QColor(218, 67, 68),
                               QColor(177, 99, 159), QColor(107, 81, 92), QColor(89, 92, 160), QColor(21, 156, 199) ,
                               QColor(79, 169, 172), QColor(155, 183, 154), QColor(128, 77, 77), QColor(240, 188, 189) };
    for (int row = 0; row < 3; row++)
    {
        for (int column = 0; column < 4; column++)
        {
            QClickLabel* label = new QClickLabel(this);
            label->setCursor(Qt::PointingHandCursor);
            connect(label, &QClickLabel::clicked, [row, column, colorLst, this](){
                NotifyManager::getInstance()->notifyOtherWindowChangeSkin(colorLst.at(4 * row + column));
            });
            label->setFixedSize(84, 84);
            QPalette palette;
            palette.setColor(QPalette::Background, colorLst.at(4 * row + column));
            label->setAutoFillBackground(true);
            label->setPalette(palette);

            ui.gridLayout->addWidget(label, row, column);
        }
    }

    connect(ui.sysmin, SIGNAL(clicked(bool)), this, SLOT(onShowMin(bool)));
    connect(ui.sysclose, SIGNAL(clicked(bool)), this, SLOT(onShowClose(bool)));
}
//父节点Item
#include "rootcontatitem.h"
#include <QPainter>
#include <QPropertyAnimation>
#include <QDebug>

RootContatItem::RootContatItem(QWidget *parent)
    : QLabel(parent)
    , m_rotation(0)
{
    setFixedHeight(32);
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    m_animation = new QPropertyAnimation(this, "rotation");
    m_animation->setDuration(50);
    m_animation->setEasingCurve(QEasingCurve::InQuad);
}

RootContatItem::~RootContatItem()
{

}

void RootContatItem::setExpanded(bool expand)
{
    if (expand)
    {
        m_animation->setEndValue(90);
    }
    else
    {
        m_animation->setEndValue(0);
    }
    m_animation->start();
}

int RootContatItem::rotation()
{
    return m_rotation;
}

void RootContatItem::setRotation(int rotation)
{
    m_rotation = rotation;
    update();
}

void RootContatItem::setText(const QString& title)
{
    m_titleText = title;
    update();
}

void RootContatItem::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    
    {  
        painter.setRenderHint(QPainter::TextAntialiasing, true);
        QFont font;
        font.setPointSize(10);
        painter.setFont(font);
        painter.drawText(24, 0, width() - 24, height(), Qt::AlignLeft | Qt::AlignVCenter, m_titleText);
    }

    {
        painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
        painter.save();
        //背景图
        QPixmap pixmap(":/QQMainWindow/Resources/MainWindow/arrow.png");
        QPixmap tmpPixmap(pixmap.size());
        tmpPixmap.fill(Qt::transparent);
        
        QPainter p(&tmpPixmap);
        p.setRenderHint(QPainter::SmoothPixmapTransform, true);
        //旋转m_rotation角度
        p.translate(pixmap.width() / 2, pixmap.height() / 2);
        p.rotate(m_rotation);
        p.drawPixmap(0 - pixmap.width() / 2, 0 - pixmap.height() / 2, pixmap);
        painter.drawPixmap(6, (height() - pixmap.height()) / 2, tmpPixmap);
        painter.restore();
    }

    __super::paintEvent(event);
}

工程文件

Qt交流大会 853086607 免费群中


在这里插入图片描述

结尾

不定期上传新作品,解答群中作品相关问题。相关外,能解答则解答。欢迎大家一起探索Qt世界!

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

推荐阅读更多精彩内容

  • 京剧这一国粹离孩子已经遥远了,班里没有一位孩子会唱京剧片段,这都没关系。语文课堂上,我们要利用国粹进行语言教学,我...
    而今起阅读 172评论 0 1
  • 1.迎来端午小长假,一早五点多,我们就起来收拾出发,感谢先生开车,一路畅通,7点多就到家了,简单吃了一个早饭,我们...
    小窗幽记_hj阅读 191评论 0 0
  • “这个秘密只有我柳萌一个人知道,你莫开阳真的不感兴趣?莫开阳,你真的不想我和你分享吗?要是真这样的话,那我柳萌就一...
    新观点读书阅读 184评论 0 0
  • 越来越反感原则,规则,规矩,规定,责任,义务……这些词语了。那么多人整天把这些东西挂在嘴边,不过是喜欢它们能带来的...
    山丘还殇阅读 781评论 0 2