本文轉載自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(); }



