qt之菜單項定制


    qt實現菜單,簡單的界面QMenu+QAction完全可以實現,在加上qss的支持,可以定制出比較美觀的菜單,qt的菜單一般用在托盤、按鈕和工具欄上。

    當然啦,也有很多軟件有比較美觀的托盤菜單,比如360、電腦管家等軟件,效果圖如圖1所示,其實qt在4.2之后也提供了定制菜單的功能,使用QWidgetAction可以定制出自己想要的菜單來,接下來是我定制菜單欄的步驟。

圖1 360圖盤菜單

 

    實現效果如下圖2所示,菜單是由單個條目組成的,每一個條目又由左右兩部分組成,左邊是一個圖標,並伴有底色,右邊是一個label,上邊有文字描述,當有鼠標移動到項上時,項整個背景色變成紅色,並且圖標會替換,文字顏色也會有相應的變化。

圖2 定制菜單

 

    首先拿到這個功能,我們可以先考慮功能的拆分,既然qt支持菜單項的窗口定制功能,那我們不防把每一個項目定制成一個QWidget,這樣就問題就變成了一個窗口的定制,這樣看起來是不是簡單多了。

    首先我們來看下QSystemTrayIcon類,該類實現了windows托盤的功能,activated信號表示托盤圖標有事件,我們需要處理這個信號,當messageClicked信號觸發時,說明我們點擊了托盤提示信息。下面是我重寫的托盤類

 1 class CSystemTrayIcon : public QSystemTrayIcon
 2 {
 3     Q_OBJECT
 4 
 5 signals:
 6     void ShowMainWidget();
 7     void ShowMiniWidget();
 8     void AppQuit();
 9 
10 public:
11     CSystemTrayIcon(const QIcon & icon, QObject * parent = nullptr);
12     ~CSystemTrayIcon();
13 
14 public:
15     void SetWaverable(bool waver);//托盤圖標是否閃動
16     void ShowMessage(const QString & title
17         , const QString & message
18         , QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::Information
19         , int millisecondsTimeoutHint = 5000);//托盤彈出氣泡提示
20 
21     QAction * AddAction(const QString & actName, const QIcon & icon);
22 
23 protected:
24     virtual bool event(QEvent *) Q_DECL_OVERRIDE;
25     virtual void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE;
26     virtual bool eventFilter(QObject * watched, QEvent * event) Q_DECL_OVERRIDE;
27 
28 private slots:
29     void TrayActivateSlot(QSystemTrayIcon::ActivationReason);
30     void MessageClickedSlot();
31 
32 private:
33     void CreateMenu();
34 
35 private:
36     bool m_MouseLeave = true;
37     int m_TimeID = 0;
38     QIcon m_IconPath;
39     CTrayMenu * m_Menu = nullptr;
40 #ifdef CustomAction
41     //添加定制菜單項
42     CActionItem * mainAct = nullptr;
43     CActionItem * miniAct = nullptr;
44     CActionItem * exitAct = nullptr;
45 #else
46     QAction * mainAct = nullptr;
47     QAction * miniAct = nullptr;
48     QAction * exitAct = nullptr;
49 #endif
50 };
View Code

    通過查看CSystemTrayIcon類的接口,可以發現該類中有其他的接口,但是我在本文中不打算對其一一作出解釋,因為和菜單項定制無關,如果非要追問,那我只能接單的說明下,SetWaverable接口使用類設置托盤圖標是否閃動,就類似qq一樣的效果。

    接下來我們需要了解下QWidgetAction類,這個類繼承自QAction,他擁有QAction的所有信號和槽,因此我們可以把他當QAction一樣的用,不僅僅如此,我們還可以為其提供自定義的QWidget,實現代碼如下

 1 class CActionItem : public QWidgetAction
 2 {
 3     Q_OBJECT
 4     Q_PROPERTY(bool m_Hover READ IsMHover WRITE SetMHover)
 5 
 6 public:
 7     CActionItem(const QString & text = "", QWidget * parent = nullptr);
 8     ~CActionItem();
 9 
10 public:
11     void SetContentText(const QString & text);
12     void SetItemIcon(const QString & icon);
13     void SetItemIcon(const QString & icon, const QString & hover);
14     void SetItemIcon(const QString & icon, const QString & hover, const QString & press);
15 
16     QWidget * contentWidget() const;//獲取中心窗口
17     void SetToolTip(const QString & toolTip);
18 
19 public:
20     bool IsMHover(){ return m_Hover; }
21     void SetMHover(bool hover){ m_Hover = hover; }
22 
23 protected:
24     virtual QWidget * createWidget(QWidget * parent) Q_DECL_OVERRIDE;
25     virtual void deleteWidget(QWidget * widget) Q_DECL_OVERRIDE;
26     
27 private:
28     bool m_Hover = false;
29     CActionContentWidget * m_ContentWidget = nullptr;
30 };
View Code

    當有QWidgetAction被創建時,首先在構造函數中初始化我們定制的窗口,並將其設置為缺省的窗口

CActionItem::CActionItem(const QString & text, QWidget * parent /*= nullptr*/) : QWidgetAction(parent)
{
	setEnabled(true);

	m_ContentWidget = new CActionContentWidget();
	connect(m_ContentWidget, &CActionContentWidget::IconClicked, this, [this]{this->triggered(); });
	m_ContentWidget->SetContentText(text);

	setDefaultWidget(m_ContentWidget);
}

     createWidget接口會被自動調用,因此我們可以在此接口中創建我們自己定制的QWidget。代碼如下,記得把定制的窗口設置為參數所給窗口的子窗口

QWidget * CActionItem::createWidget(QWidget * parent)
{
	m_ContentWidget->setParent(parent);
	return m_ContentWidget;
}

     QWidgetActoin只是一個QAction,想要美觀的菜單項,還是需要我們自己去定制窗口的,接下來就是我自己定制的窗口

 1 class CActionContentWidget : public QWidget
 2 {
 3     Q_OBJECT
 4 signals:
 5     void IconClicked();
 6 
 7 public:
 8     CActionContentWidget(QWidget * parent = nullptr);
 9     ~CActionContentWidget();
10 
11 public:
12     void SetContentText(const QString & text);
13     void SetItemIcon(const QString & icon, const QString & hover);
14 
15 public:
16     void SetBackgroundRole(bool hover);
17 
18 protected:
19     virtual void enterEvent(QEvent *) Q_DECL_OVERRIDE;
20     virtual void leaveEvent(QEvent *) Q_DECL_OVERRIDE;
21     virtual bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE;
22 
23 private:
24     void InitializeUI();
25 
26 private:
27     QWidget * m_ContentWidget = nullptr;
28     QPushButton * m_ActIcon = nullptr;
29     QLabel * m_ActText = nullptr;
30     QString m_NormalIcon, m_HoverIcon, m_PressedIcon;
31 };
View Code

    最后就是菜單的定制啦,為什么要重寫菜單呢,因為我需要在指定時刻,修改菜單項的位置,因此菜單項的定制也比較簡單,就是在關鍵時刻跑出一個信號,表示需要修改菜單位置了,代碼如下:

 1 class CTrayMenu : public QMenu
 2 {
 3     Q_OBJECT
 4 
 5 signals:
 6     void FixedPostion();//移動菜單位置
 7 
 8 public:
 9     CTrayMenu(QWidget * parent = nullptr);
10     ~CTrayMenu();
11 
12 protected:
13     virtual bool event(QEvent *) Q_DECL_OVERRIDE;
14 };
View Code

    因為菜單是一個QWidget,在構造函數中,拿不到width和height,而在show的時候可以拿到相關信息,代碼如下:

bool CTrayMenu::event(QEvent * e)
{
	if (e->type() == QEvent::Show)
	{
		emit FixedPostion();
	}

	return QMenu::event(e);
}

     講到這兒,qt菜單定制功能就講完了,在菜單定制的過程中我自己也遇到了一些問題,在此記錄下,希望看到並知道原因的留下您的腳印。

問題:

1、定制的QWidget中的鼠標事件異常

2、qss中的屬性判斷異常,例如QLabel[IsCheck=true]{border:1 solid #ff0000;},這種方式設置的鼠標變化不起作用,為了實現這個功能,我是在CSystemTrayIcon類中把定制窗口事件都注冊到父類中,然后通過eventFilter來判斷鼠標位置,進一步重新設置qss來到達鼠標移動換背景色的功能。代碼如下:

bool CSystemTrayIcon::eventFilter(QObject * watched, QEvent * event)
{
	if (watched == this)
	{
		m_MouseLeave = false;
	}
	if (watched->inherits("QWidget") && event->type() == QEvent::Paint)
	{
		if (CActionContentWidget * actionItem = static_cast<CActionContentWidget *>(watched))
		{
			if (actionItem->rect().contains(actionItem->mapFromGlobal(QCursor::pos())))
			{
				actionItem->SetBackgroundRole(true);
			}
			else
			{
				actionItem->SetBackgroundRole(false);
			}
		}
	}
	return QSystemTrayIcon::eventFilter(watched, event);
}

 

如果您覺得文章不錯,不妨給個 打賞,寫作不易,感謝各位的支持。您的支持是我最大的動力,謝謝!!! 

 

  


很重要--轉載聲明

  1. 本站文章無特別說明,皆為原創,版權所有,轉載時請用鏈接的方式,給出原文出處。同時寫上原作者:朝十晚八 or Twowords
  2. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利於轉載者的目的。 


免責聲明!

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



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