自定義QListWidget實現item被hover時改變圖標樣式(模仿網易雲音樂選項列表)(方法一)


本文轉載自https://blog.csdn.net/weixin_43742643/article/details/100187743

前半部分copy了博主的博文,后面是自己的實踐和疑惑。

效果圖:

 

這里需要說明一下:QListWidget是鼠標press時item就會被選中,自定義的TestListWidget類重寫了mousePressEvent和mouseReleaseEvent使得item在鼠標release時才會被選中。至於為什么這樣做,是因為Chrome瀏覽器的書簽欄以及網易雲音樂的選項列表都是在鼠標release時才會觸發選中…

本文中實現方法:

  • item被hover時改變圖標樣式:繼承QListWidgetItem + 繼承QListWidget
  • item在鼠標release時選中:繼承QListWidget

另一種實現方法:自定義QListWidget實現item被hover時改變圖標樣式(模仿網易雲音樂選項列表)(方法二)

(1)TestListWidgetItem類繼承自QListWidgetItem

  • TestListWidgetItem.h文件:

 

class TestListWidgetItem : public QListWidgetItem
{
    //Q_OBJECT  //由於QListWidgetItem沒有QObject屬性,所以Q_OBJECT需要注釋掉
public:
    explicit TestListWidgetItem(QListWidget *view = nullptr);
    void setUpIcon(const QIcon &icon, const QIcon &icon_hover);

    QIcon Img;
    QIcon Img_hover;
};

 

  • TestListWidgetItem.c文件:
TestListWidgetItem::TestListWidgetItem(QListWidget *view) :
    QListWidgetItem(view)
{
}

void TestListWidgetItem::setUpIcon(const QIcon &icon, const QIcon &icon_hover)
{
    Img = icon;
    Img_hover = icon_hover;
    setIcon(Img);
}

2)TestListWidget類繼承自QListWidget

  • TestListWidget.h文件:
class TestListWidget : public QListWidget
{
    Q_OBJECT
public:
    explicit TestListWidget(QWidget *parent = nullptr);

protected:
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void leaveEvent(QEvent *event) override;

private:
    QPoint startPos;
    TestListWidgetItem *theHighlightItem = nullptr;
    TestListWidgetItem *oldHighlightItem = nullptr;
    TestListWidgetItem *theSelectedItem = nullptr;
    TestListWidgetItem *oldSelectedItem = nullptr;

private slots:
    void updateSelectedIcon();
};
  • TestListWidget.c文件:
TestListWidget::TestListWidget(QWidget *parent) :
    QListWidget(parent)
{
    //mouseMoveEvent(QMouseEvent *event)需要該屬性
    setMouseTracking(true);
    //當前被選中項發生變化時,觸發item圖標的更新
    connect(this, &TestListWidget::itemSelectionChanged, this, &TestListWidget::updateSelectedIcon);
}

//處理鼠標hover時,item的圖標需要變成hover狀態
void TestListWidget::mouseMoveEvent(QMouseEvent *event)
{
    oldHighlightItem = theHighlightItem;
    theHighlightItem = static_cast<TestListWidgetItem *>(itemAt(event->pos()));

    //舊的hover的item圖標回復原狀(條件是該item沒有被選中)
    //新的hover的iten圖標變成hover狀態(條件是該item沒有被選中)
    if(oldHighlightItem != theHighlightItem){
        if(oldHighlightItem && !oldHighlightItem->isSelected()) oldHighlightItem->setIcon(oldHighlightItem->Img);
        if(theHighlightItem && !theHighlightItem->isSelected()) theHighlightItem->setIcon(theHighlightItem->Img_hover);
    }
}

//鼠標按下時,startPos記錄單擊位置
void TestListWidget::mousePressEvent(QMouseEvent *event)
{
    if(event->buttons() & Qt::LeftButton){
        startPos = event->pos();
    }
}

//釋放鼠標時,item才會被選中
void TestListWidget::mouseReleaseEvent(QMouseEvent *event)
{
    //如果鼠標釋放位置和單擊位置相距超過5像素,則不會觸發item選中
    if((event->pos() - startPos).manhattanLength() > 5) return;

    TestListWidgetItem *item = static_cast<TestListWidgetItem *>(itemAt(event->pos()));
    setCurrentItem(item);
}

//處理鼠標離開后,hover圖標回復正常狀態
void TestListWidget::leaveEvent(QEvent *event)
{
    Q_UNUSED(event);
    oldHighlightItem = theHighlightItem;
    if(oldHighlightItem && !oldHighlightItem->isSelected()) oldHighlightItem->setIcon(oldHighlightItem->Img);
    oldHighlightItem = theHighlightItem = nullptr;
}

//響應itemSelectionChanged()信號,處理item被選中后,圖標變成並保持hover狀態
void TestListWidget::updateSelectedIcon()
{
    oldSelectedItem = theSelectedItem;
    theSelectedItem = static_cast<TestListWidgetItem *>(currentItem());

    //之前被選中的item圖標回復原樣
    //新被選中的item圖標變成hover狀態
    if(oldSelectedItem != theSelectedItem){
        if(oldSelectedItem) oldSelectedItem->setIcon(oldSelectedItem->Img);
        if(theSelectedItem) theSelectedItem->setIcon(theSelectedItem->Img_hover);
    }
}

(3)使用TestListWidget和TestListWidgetItem

void test::initUi()
{
    TestListWidget *listwidget = new TestListWidget(this);
    listwidget->setIconSize(QSize(25, 25));  //設置item圖標大小
    listwidget->setFocusPolicy(Qt::NoFocus);  //這樣可禁用tab鍵和上下方向鍵並且除去復選框
    listwidget->setFixedHeight(320);
    listwidget->setFont(QFont("宋體", 10, QFont::DemiBold));
    listwidget->setStyleSheet(
                //"*{outline:0px;}"  //除去復選框
                "QListWidget{background:rgb(245, 245, 247); border:0px; margin:0px 0px 0px 0px;}"
                "QListWidget::Item{height:40px; border:0px; padding-left:14px; color:rgba(200, 40, 40, 255);}"
                "QListWidget::Item:hover{color:rgba(40, 40, 200, 255);}"
                "QListWidget::Item:selected{background:rgb(230, 231, 234); color:rgba(40, 40, 200, 255); border-left:4px solid rgb(180, 0, 0);}"
                //"QListWidget::Item:selected:active{background:rgb(230, 231, 234); color:rgba(40, 40, 200, 255); border-left:4px solid rgb(180, 0, 0);}");

    TestListWidgetItem *item1 = new TestListWidgetItem(listwidget);
    item1->setUpIcon(QIcon(":/listBar_Icon/1.png"), QIcon(":/listBar_Icon/1_hover.png"));
    item1->setText("發現音樂");

    TestListWidgetItem *item2 = new TestListWidgetItem(listwidget);
    item2->setUpIcon(QIcon(":/listBar_Icon/2.png"), QIcon(":/listBar_Icon/2_hover.png"));
    item2->setText("私人FM");

    TestListWidgetItem *item3 = new TestListWidgetItem(listwidget);
    item3->setUpIcon(QIcon(":/listBar_Icon/3.png"), QIcon(":/listBar_Icon/3_hover.png"));
    item3->setText("朋友");

    TestListWidgetItem *item4 = new TestListWidgetItem(listwidget);
    item4->setUpIcon(QIcon(":/listBar_Icon/4.png"), QIcon(":/listBar_Icon/4_hover.png"));
    item4->setText("MV");

    TestListWidgetItem *item5 = new TestListWidgetItem(listwidget);
    item5->setUpIcon(QIcon(":/listBar_Icon/5.png"), QIcon(":/listBar_Icon/5_hover.png"));
    item5->setText("本地音樂");

    TestListWidgetItem *item6 = new TestListWidgetItem(listwidget);
    item6->setUpIcon(QIcon(":/listBar_Icon/6.png"), QIcon(":/listBar_Icon/6_hover.png"));
    item6->setText("下載管理");

    TestListWidgetItem *item7 = new TestListWidgetItem(listwidget);
    item7->setUpIcon(QIcon(":/listBar_Icon/7.png"), QIcon(":/listBar_Icon/7_hover.png"));
    item7->setText("我的音樂雲盤");

    TestListWidgetItem *item8 = new TestListWidgetItem(listwidget);
    item8->setUpIcon(QIcon(":/listBar_Icon/8.png"), QIcon(":/listBar_Icon/8_hover.png"));
    item8->setText("我的收藏");

    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->setSpacing(0);
    layout->addWidget(listwidget);
    layout->setContentsMargins(0, 0, 0, 0);
    setLayout(layout);
}

(4)繼承QListWidget時其實也可以不需要重寫leaveEvent(QEvent *event)和mouseMoveEvent(QMouseEvent *event)這兩個函數,只需要重寫event(QEvent *event)。

  • TestListWidget.h文件:
class TestListWidget : public QListWidget
{
    Q_OBJECT
public:
    explicit TestListWidget(QWidget *parent = nullptr);

protected:
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    bool event(QEvent *event) override;

private:
    QPoint startPos;
    TestListWidgetItem *theHighlightItem = nullptr;
    TestListWidgetItem *oldHighlightItem = nullptr;
    TestListWidgetItem *theSelectedItem = nullptr;
    TestListWidgetItem *oldSelectedItem = nullptr;

private slots:
    void updateSelectedIcon();
};
  • TestListWidget.c文件:
TestListWidget::TestListWidget(QWidget *parent) :
    QListWidget(parent)
{
    //不重寫mouseMoveEvent(QMouseEvent *event)的話,該屬性可以不用設置
    //setMouseTracking(true);
    //當前被選中項發生變化時,觸發item圖標的更新
    connect(this, &TestListWidget::itemSelectionChanged, this, &TestListWidget::updateSelectedIcon);
}

//鼠標按下時,startPos記錄單擊位置
void TestListWidget::mousePressEvent(QMouseEvent *event)
{
    if(event->buttons() & Qt::LeftButton){
        startPos = event->pos();
    }
}

//釋放鼠標時,item才會被選中
void TestListWidget::mouseReleaseEvent(QMouseEvent *event)
{
    //如果鼠標釋放位置和單擊位置相距超過5像素,則不會觸發item選中
    if((event->pos() - startPos).manhattanLength() > 5) return;
    //由於TestListWidgetItem沒有QObject屬性,所以這里使用static_cast而非qobject_cast
    TestListWidgetItem *item = static_cast<TestListWidgetItem *>(itemAt(event->pos()));
    setCurrentItem(item);
}

bool TestListWidget::event(QEvent *event)
{
    switch (event->type()) {
    //處理鼠標hover時,item的圖標需要變成hover狀態
    case QEvent::HoverMove:
        oldHighlightItem = theHighlightItem;
        //由於TestListWidgetItem沒有QObject屬性,所以這里使用static_cast而非qobject_cast
        theHighlightItem = static_cast<TestListWidgetItem *>(itemAt(mapFromGlobal(QCursor::pos())));

        //舊的hover的item圖標回復原狀(條件是該item沒有被選中)
        //新的hover的iten圖標變成hover狀態(條件是該item沒有被選中)
        if(oldHighlightItem != theHighlightItem){
            if(oldHighlightItem && !oldHighlightItem->isSelected()) oldHighlightItem->setIcon(oldHighlightItem->Img);
            if(theHighlightItem && !theHighlightItem->isSelected()) theHighlightItem->setIcon(theHighlightItem->Img_hover);
        }
        break;
    //處理鼠標離開后,hover圖標回復正常狀態
    case QEvent::HoverLeave:
        oldHighlightItem = theHighlightItem;
        if(oldHighlightItem && !oldHighlightItem->isSelected()) oldHighlightItem->setIcon(oldHighlightItem->Img);
        oldHighlightItem = theHighlightItem = nullptr;
        break;
    default:
        break;
    }
    return QListWidget::event(event);
}

//響應itemSelectionChanged()信號,處理item被選中后,圖標變成並保持hover狀態
void TestListWidget::updateSelectedIcon()
{
    oldSelectedItem = theSelectedItem;
    //由於TestListWidgetItem沒有QObject屬性,所以這里使用static_cast而非qobject_cast
    theSelectedItem = static_cast<TestListWidgetItem *>(currentItem());

    //之前被選中的item圖標回復原樣
    //新被選中的item圖標變成hover狀態
    if(oldSelectedItem != theSelectedItem){
        if(oldSelectedItem) oldSelectedItem->setIcon(oldSelectedItem->Img);
        if(theSelectedItem) theSelectedItem->setIcon(theSelectedItem->Img_hover);
    }
}

這一部分是自己稍加改進的代碼,運行無誤。還是用的重寫事件的方法。

#include "testlistwidgetitem.h"
#include <QApplication>
#include "testlistwidgetitem.h"
#include "testlistwidget.h"
#include <QVBoxLayout>
#include  <QHBoxLayout>
#include <QWidget>
#include<QListWidget>
#include <QMainWindow>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TestListWidget *listwidget = new TestListWidget();
    listwidget->setIconSize(QSize(25, 25));  //設置item圖標大小
    listwidget->setFocusPolicy(Qt::NoFocus);  //這樣可禁用tab鍵和上下方向鍵並且除去復選框
    listwidget->setFixedHeight(320);
    listwidget->setFont(QFont("宋體", 10, QFont::DemiBold));
    listwidget->setStyleSheet(
        "*{outline:0px;}"  //除去復選框
        "QListWidget{background:rgb(245, 245, 247); border:0px; margin:0px 0px 0px 0px;}"
        "QListWidget::Item{height:40px; border:0px; padding-left:14px; color:rgba(200, 40, 40, 255);}"
        "QListWidget::Item:hover{color:rgba(40, 40, 200, 255);}"
        "QListWidget::Item:selected{background:rgb(230, 231, 234); color:rgba(40, 40, 200, 255); border-left:4px solid rgb(180, 0, 0);}"
        "QListWidget::Item:selected:active{background:rgb(230, 231, 234); color:rgba(40, 40, 200, 255); border-left:4px solid rgb(180, 0, 0);}");

    TestListWidgetItem *item1 = new TestListWidgetItem(listwidget);
    item1->setUpIcon(QIcon(":/normal.jpg"), QIcon(":/pressed.jpg"));
    item1->setText("數據查詢");

    TestListWidgetItem *item2 = new TestListWidgetItem(listwidget);
    item2->setUpIcon(QIcon(":/normal1.jpg"), QIcon(":/pressed1.jpg"));
    item2->setText("曲線查詢");

    QVBoxLayout *layout = new QVBoxLayout();
    //QHBoxLayout *layout = new QHBoxLayout();
    layout->setSpacing(0);
    layout->addWidget(listwidget);
    layout->setContentsMargins(0, 0, 0, 0);
    //listwidget->setLayout(layout);  //不注掉這一句的話不顯示窗體,很奇怪
    listwidget->show();

    return a.exec();
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM