Qt之懸浮球菜單

原文鏈接:Qt之懸浮球菜單

一、概述

最近想做一個炫酷的懸浮式菜單,考慮到菜單展開和美觀,所以考慮學習下Qt的動畫系統和狀態機內容,打開QtCreator的示例教程瀏覽了下,大致發現教程中2D Painting程序和Animated Tiles程序有所幫助,如下圖所示,這兩個demo講述了怎麼做一個展開動畫,感興趣的同學也可以直接參考

有了這兩個demo之後,就可以開始動工寫咱們自己的程序。

二、效果展示

如下兩幅圖就是作者失效的兩個懸浮菜單效果圖,展示圖1代碼已上傳至CSDN,不需要積分即可下載,效果圖2代碼暫時不開源,有需要的朋友可以進一步諮詢

基礎圓形菜單功能,代碼已上傳CSDN - Qt 失效的 PC 端環形菜單、懸浮球菜單、展開動畫

高級懸浮球菜單、支持二級菜單打開

三、實現代碼

實現文件比較簡單,只有頭文件和實現文件,這裏先主要放出頭文件,然後講解實現思路,具體實現細節可以通過下載源碼進行具體瞭解

1、菜單項

PopRingItem爲菜單展開項、可以通過綁定外部QAction實現與普通菜單相同功能

class PopRingItem : public QLabel
{
	Q_OBJECT

public:
	PopRingItem(QWidget *parent = 0);
	~PopRingItem();

	void SetRadius(int radius);
	int GetRadius() const;

	void BindAction(QAction * action);

signals:
	void MouseEvent(bool);

protected:
	virtual void enterEvent(QEvent * event) override;
	virtual void leaveEvent(QEvent * event) override;

	virtual void paintEvent(QPaintEvent * event) override;

protected:
	int m_iRadius = 50;
	QAction * m_actAction = nullptr;
};

2、懸浮球

懸浮球爲菜單入口,繼承自菜單項,與菜單項有相似功能

class QVariantAnimation;
class QPropertyAnimation;
class PopRingMenu : public PopRingItem
{
	Q_OBJECT

public:
	PopRingMenu(QWidget *parent = 0);
	~PopRingMenu();

signals:
	void DoubleClicked();

public:
	void SetActions(const QVector<QAction *> & acts);
	void SetIcons(const QVector<QString> & icons);

	void SetAnimationEnabled(bool enabled);
	bool IsAnimationEnabled() const;

	void SetSlowlyFade(bool enabled);
	bool IsSlowlyFade() const;

	void SetDistanced(int distance);
	int GetDistanced() const;

	void SetStartAngle(int angle);
	int GetStartAngle() const;

	void SetStepAngle(int angle);
	int GetStepAngle() const;

	void SetNormalMenuSize(int size);
	int GetNormalMenuSize() const;
	void SetNormalItemSize(int size);
	int GetNormalItemSize() const;

protected:
	virtual void enterEvent(QEvent * event) override;
	virtual void leaveEvent(QEvent * event) override;
	virtual void mouseDoubleClickEvent(QMouseEvent * event) override;

	virtual void timerEvent(QTimerEvent * event) override;
	virtual bool event(QEvent * event) override;

private slots:
	void OnMouseEvent(bool);

private:
	void UpdateActions(int msecond);

	void ExpandMenu();
	void CollapseMenu();

	void SlowlyFade();
	void QuicklyLighter();

	bool IsUnderMouse() const;

	void TryCollapseMenu();
	void KillHideTimer();

private:
	int m_iDistance = 70;
	int m_iStartAngle = 0;
	int m_iStepAngle = 60;

	int m_iMenuSize = 70;
	int m_iItemSize = 60;

	int m_iTimerID = -1;

	QPropertyAnimation * m_pOpacityAnimation = nullptr;
	QVariantAnimation * m_pItemAnimation = nullptr;
	QVector<PopRingItem *> m_items;
};

3、關鍵點

初始化動畫對象,指定動畫時長和動畫起始、終止值

動畫具體實現函數未UpdateAction,根據當前動畫進度值在動畫起始值和終止值所佔比例,進行計算當前動畫時刻菜單項的位置和大小

m_pItemAnimation = new QVariantAnimation(this);

m_pItemAnimation->setEasingCurve(QEasingCurve::InCubic);
m_pItemAnimation->setStartValue(ShowMenuStartValue);
m_pItemAnimation->setEndValue(ShowMenuEndValue);
m_pItemAnimation->setDuration(ShowMenuDuration);

connect(m_pItemAnimation, &QVariantAnimation::valueChanged, this, [this](const QVariant & v){
	UpdateActions(v.toInt());
});

鼠標進入懸浮球時,執行展開動畫

void PopRingMenu::ExpandMenu()
{
	if (m_pItemAnimation)
	{
		if (m_pItemAnimation->state() != QAbstractAnimation::Running
			&& m_pItemAnimation->currentValue().toInt() != ShowMenuEndValue)
		{
			m_pItemAnimation->setDirection(QVariantAnimation::Forward);
			m_pItemAnimation->start();
		}
	}
	else
	{
		UpdateActions(ShowMenuEndValue);
	}

	KillHideTimer();
	QuicklyLighter();
}
  1. 鼠標離開懸浮球時,執行收起動畫,與展開動畫相反方向
  2. 收起動畫時有一個細節點,那就是鼠標hover在菜單項上時,也不能收起
void PopRingMenu::CollapseMenu()
{
	if (false == IsUnderMouse())
	{
		if (m_pItemAnimation)
		{
			m_pItemAnimation->setDirection(QVariantAnimation::Backward);
			m_pItemAnimation->start();
		}
		else
		{
			UpdateActions(ShowMenuStartValue);
		}

		KillHideTimer();
		SlowlyFade();
	}
}

展開和收起動畫實現細節,根據動畫指定幀數,按比例進行縮放和移動菜單項

void PopRingMenu::UpdateActions(int msecond)
{
	int curDistance = msecond * m_iDistance / ShowMenuEndValue;
	for (int i = 0; i < m_items.size(); ++i)
	{
		PopRingItem * item = m_items.at(i);
		
		double radians = qDegreesToRadians(m_iStepAngle * i * 1.0 + m_iStartAngle);
		int offx = curDistance * qCos(radians);
		int offy = curDistance * qSin(radians);
		item->move(pos() + QPoint(offx, offy));

		int curSize = msecond * m_iItemSize / ShowMenuEndValue;
		item->SetRadius(curSize);

		item->setVisible(ShowMenuStartValue != msecond);
	};

	::SetWindowPos(HWND(winId()), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}

懸浮球指定時間未激活時,淡出,減少對用戶視覺衝擊

void PopRingMenu::SetSlowlyFade(bool enabled)
{
	if (enabled)
	{
		if (nullptr == m_pOpacityAnimation)
		{
			m_pOpacityAnimation = new QPropertyAnimation(this, "opacity");
			m_pOpacityAnimation->setEasingCurve(QEasingCurve::OutCubic);
			m_pOpacityAnimation->setStartValue(SlowlyStartValue);
			m_pOpacityAnimation->setEndValue(SlowLyEndValue);
			m_pOpacityAnimation->setDuration(SlowlyFadeDuration);
		}
	}
	else
	{

		if (m_pOpacityAnimation)
		{
			delete m_pOpacityAnimation;
			m_pOpacityAnimation = nullptr;
		}
	}
}

四、相關文章

  1. qt 之菜單項定製
  2. Qt 之 QAbstractItemView 右鍵菜單
  3. Qt 彈出式菜單陰影
  4. Qt 之自定義 QLineEdit 右鍵菜單
  5. Qt 之股票組件 - 自選股 -- 列表可以拖拽、右鍵常用菜單

值得一看的優秀文章:

  1. 財聯社-產品展示
  2. 廣聯達-產品展示
  3. Qt定製控件列表
  4. 牛逼哄哄的Qt庫

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




很重要--轉載聲明

  1. 本站文章無特別說明,皆爲原創,版權所有,轉載時請用鏈接的方式,給出原文出處。同時寫上原作者:朝十晚八 or Twowords

  2. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利於轉載者的目的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章