• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

QDockWidget 的自定义标题栏

武飞扬头像
烈烈秦风
帮助1

背景

不考虑复杂的dockwidget使用情况下,QDockWidget还是勉强一用的。在需要对UI进行定制的情形下,QDockWidget的标题栏就必然要进行自定义,而且QDockWidget也提供有相应的接口

void QDockWidget::setTitleBarWidget(QWidget *widget);

由于QDockWidget可以直接设置自定义标题栏,就不需要重写QDockWidget本身,也不需要重写鼠标事件用来拖拽标题栏控制DockWidget移动。特别强调,对标题栏的双击控制,仍采用QDockWidget原生效果,双击标题栏,dockWidget会嵌入或浮动到窗口中。

此处讨论的是自定义标题栏的右侧按钮集。
本文提供一种简单的QDockWidget自定义标题栏CqDockWgtTitleBar,提供有最小按钮、浮动按钮、最大按钮和关闭按钮。
此处,

  • 最小按钮,用于将DockWidget嵌入到父窗口中;
  • 浮动按钮,用于将DockWidget以正常尺寸浮动到当前屏幕上;
  • 最大按钮,用于将DockWidget最大化显示在当前屏幕上;
  • 关闭按钮,用于将DockWidget关闭,实际上是隐藏。可以通过其他控件将该DockWidget重新显示出来。

关于类CqDockWgtTitleBar的命名,DockWgtTitleBar,不言而喻,dockWidget的标题栏。
前缀Cq,有几重含义

  • Custom Qt,自定义化的Qt
  • C Qt,C 语言的Qt
  • China/Chinese Qt,中国化的Qt。还是需要积累更多更适用的Qt代码,方便实际项目开发,追求中国自主开发和基础功能代码的共享。

具体代码文件有CqDockWgtTitleBar.h和CqDockWgtTitleBar.cpp

  • 效果1 (DockWidget嵌入到应用窗口中,忽略主窗口同样显示为三维)
    学新通

  • 效果2 (DockWidget浮动在应用窗口中,忽略主窗口同样显示为三维)
    学新通

  • 效果3 (DockWidget移动到另一屏幕,最大化显示,忽略主窗口同样显示为三维,可以设想此类双屏显示二维地图和三维地球的应用场景。由于双屏分辨率不同,截屏图片中窗口标题栏高度显示存在问题,实际显示正常。)
    学新通

CqDockWgtTitleBar.h

/**
 * @file 	CqDockWgtTitleBar.h
 * @brief 	QDockWidget 自定义标题栏
 * @author 	秦风
 * @date 	2022.11.18 
 */

#ifndef CQDOCKWGTTITLEBAR_H
#define CQDOCKWGTTITLEBAR_H

#include <QWidget>

class QDockWidget;
class CqDockWgtTitleBarPrivate;

/**
 * @class 	CqDockWgtTitleBar
 * @brief 	QDockWidget 自定义标题栏
 */
class CqDockWgtTitleBar : public QWidget
{
	Q_OBJECT

public:
	/**
	 * @brief 	构造函数
	 * @param 	parent [in] 需要嵌入的dock窗口
	 * @param 	strTitle [in] 窗口标题
	 */
	CqDockWgtTitleBar(QDockWidget *parent, const QString &strTitle = "");

	//! 析构函数
	~CqDockWgtTitleBar();

	/**
	* @brief	设置窗口标题。
	* @params	title 标题名称
	*/
	virtual void setWindowTitle(const QString &title);

private slots:
	void clickMinBtnSlot();
	void clickMaxBtnSlot();
	void clickFloatBtnSlot();
	void floatChangedSlot(bool bFloat);

private:	
	Q_DISABLE_COPY(CqDockWgtTitleBar)
	Q_DECLARE_PRIVATE(CqDockWgtTitleBar)
	QScopedPointer<CqDockWgtTitleBarPrivate> d_ptr;
};

#endif //! CQDOCKWGTTITLEBAR_H
学新通

CqDockWgtTitleBar.cpp

#include "CqDockWgtTitleBar.h"

#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QDockWidget>
#include <QApplication>
#include <QDesktopWidget>
#include <QStyle>

class CqDockWgtTitleBarPrivate
{
	Q_DECLARE_PUBLIC(CqDockWgtTitleBar);

protected:
	CqDockWgtTitleBar* const q_ptr;	///< q指针

public:	
	// 构造函数
	CqDockWgtTitleBarPrivate(CqDockWgtTitleBar* parent);
	virtual ~CqDockWgtTitleBarPrivate();

	QDockWidget *m_pDockWidget;	///< DockWidget

	QLabel *m_pIconLbl;			///< 图标
	QLabel *m_pTitleLbl;		///< 标题

	QPushButton *m_pMinBtn;		///< 最小按钮,用于将DockWidget嵌入到父窗口中
	QPushButton *m_pFloatBtn;	///< 浮动按钮,用于将DockWidget以正常尺寸浮动到当前屏幕上
	QPushButton *m_pMaxBtn;		///< 最大按钮,用于将DockWidget最大化显示在当前屏幕上	
	QPushButton *m_pCloseBtn;	///< 关闭按钮
};

CqDockWgtTitleBarPrivate::CqDockWgtTitleBarPrivate(CqDockWgtTitleBar* parent)
	: q_ptr(parent)
{
	m_pDockWidget = nullptr;
}

CqDockWgtTitleBarPrivate::~CqDockWgtTitleBarPrivate()
{
}

CqDockWgtTitleBar::CqDockWgtTitleBar(QDockWidget *parent, const QString &strTitle)
	: QWidget(parent)	
	, d_ptr(new CqDockWgtTitleBarPrivate(this))
{
	Q_D(CqDockWgtTitleBar);
		
	d->m_pIconLbl = new QLabel(this);
	d->m_pTitleLbl = new QLabel(this);

	QIcon minIcon(style()->standardPixmap(QStyle::SP_TitleBarMinButton));
	QIcon floatIcon(style()->standardPixmap(QStyle::SP_TitleBarNormalButton));
	QIcon maxIcon(style()->standardPixmap(QStyle::SP_TitleBarMaxButton));
	QIcon closeIcon(style()->standardPixmap(QStyle::SP_TitleBarCloseButton));

	d->m_pMinBtn = new QPushButton(minIcon, "", this);
	d->m_pFloatBtn = new QPushButton(floatIcon, "", this);
	d->m_pMaxBtn = new QPushButton(maxIcon, "", this);	
	d->m_pCloseBtn = new QPushButton(closeIcon, "", this);

	QHBoxLayout *pLayout = new QHBoxLayout(this);
	pLayout->setMargin(0);
	pLayout->setSpacing(0);
	pLayout->addWidget(d->m_pIconLbl);
	pLayout->addWidget(d->m_pTitleLbl);
	pLayout->addStretch();

	pLayout->addWidget(d->m_pMinBtn);
	pLayout->addWidget(d->m_pFloatBtn);
	pLayout->addWidget(d->m_pMaxBtn);	
	pLayout->addWidget(d->m_pCloseBtn);
	
	d->m_pMinBtn->setFocusPolicy(Qt::NoFocus);
	d->m_pFloatBtn->setFocusPolicy(Qt::NoFocus);
	d->m_pMaxBtn->setFocusPolicy(Qt::NoFocus);	
	d->m_pCloseBtn->setFocusPolicy(Qt::NoFocus);

	d->m_pTitleLbl->setText(strTitle);
	d->m_pMinBtn->setVisible(false);
	d->m_pMinBtn->setFocusPolicy(Qt::NoFocus);
	d->m_pFloatBtn->setFocusPolicy(Qt::NoFocus);
	d->m_pMaxBtn->setFocusPolicy(Qt::NoFocus);	
	d->m_pCloseBtn->setFocusPolicy(Qt::NoFocus);

	d->m_pMinBtn->setToolTip(QStringLiteral("最小化"));
	d->m_pFloatBtn->setToolTip(QStringLiteral("向下还原"));
	d->m_pMaxBtn->setToolTip(QStringLiteral("最大化"));	
	d->m_pCloseBtn->setToolTip(QStringLiteral("关闭"));

	d->m_pIconLbl->setObjectName("dock_header_icon");
	d->m_pTitleLbl->setObjectName("dock_header_title");
	d->m_pFloatBtn->setObjectName("dock_header_floatBtn");
	d->m_pMaxBtn->setObjectName("dock_header_maxBtn");	
	d->m_pCloseBtn->setObjectName("dock_header_closeBtn");

	if (parent)
	{
		d->m_pDockWidget = parent;				
		connect(d->m_pMinBtn, SIGNAL(clicked()), this, SLOT(clickMinBtnSlot()));
		connect(d->m_pMaxBtn, SIGNAL(clicked()), this, SLOT(clickMaxBtnSlot()));
		connect(d->m_pFloatBtn, SIGNAL(clicked()), this, SLOT(clickFloatBtnSlot()));
		connect(d->m_pCloseBtn, SIGNAL(clicked()), d->m_pDockWidget, SLOT(close()));
		connect(d->m_pDockWidget, SIGNAL(topLevelChanged(bool)), this, SLOT(floatChangedSlot(bool)));
	}	
}

CqDockWgtTitleBar::~CqDockWgtTitleBar()
{
}

void CqDockWgtTitleBar::setWindowTitle(const QString &title)
{	
	Q_D(CqDockWgtTitleBar);
	d->m_pTitleLbl->setText(title);
}

void CqDockWgtTitleBar::clickMinBtnSlot()
{
	Q_D(CqDockWgtTitleBar);

	d->m_pMinBtn->setVisible(false);
	d->m_pMaxBtn->setVisible(true);
	d->m_pFloatBtn->setVisible(true);

	// 嵌入窗口
	if (d->m_pDockWidget->isFloating())
	{
		d->m_pDockWidget->setFloating(false);
	}	
}

void CqDockWgtTitleBar::clickMaxBtnSlot()
{
	Q_D(CqDockWgtTitleBar);
	d->m_pMinBtn->setVisible(true);
	d->m_pMaxBtn->setVisible(false);
	d->m_pFloatBtn->setVisible(true);

	// 设置浮动
	if (!d->m_pDockWidget->isFloating())
	{
		disconnect(d->m_pDockWidget, SIGNAL(topLevelChanged(bool)), this, SLOT(floatChangedSlot(bool)));
		d->m_pDockWidget->setFloating(true);	
		connect(d->m_pDockWidget, SIGNAL(topLevelChanged(bool)), this, SLOT(floatChangedSlot(bool)));	
	}

	d->m_pDockWidget->showMaximized();	
}

void CqDockWgtTitleBar::clickFloatBtnSlot()
{
	Q_D(CqDockWgtTitleBar);

	d->m_pMinBtn->setVisible(true);
	d->m_pMaxBtn->setVisible(true);
	d->m_pFloatBtn->setVisible(false);

	// 设置浮动
	if (!d->m_pDockWidget->isFloating())
	{
		d->m_pDockWidget->setFloating(true);

		QRect availGeo = QApplication::desktop()->availableGeometry(QApplication::desktop()->screenNumber(QCursor::pos()));
		d->m_pDockWidget->move((availGeo.width() - d->m_pDockWidget->width()) / 2,
			(availGeo.height() - d->m_pDockWidget->height()) / 2);
	}
	else
	{
		d->m_pDockWidget->showNormal();
	}		
}

void CqDockWgtTitleBar::floatChangedSlot(bool bFloat)
{
	Q_D(CqDockWgtTitleBar);
	d->m_pMinBtn->setVisible(bFloat);
	d->m_pMaxBtn->setVisible(!d->m_pDockWidget->isMaximized());
	d->m_pFloatBtn->setVisible(!bFloat || d->m_pDockWidget->isMaximized());
}
学新通

参考

本代码有参考
链接: Qt 之QDockwidget 自定义窗口标题栏

考虑了多屏幕的情形下,参考了其直接使用QStyle中图标,

style()->standardPixmap()

这是此前没有见过的用法。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbjfaa
系列文章
更多 icon
同类精品
更多 icon
继续加载