Qt之自定义标题栏拓展(十)
Qt开发 系列文章 - user-defined-titlebars(十)
目录
前言
一、方式一
1.效果演示
2.创建标题栏类
3.可视化UI设计
4.定义相关函数
5.使用标题栏类
二、方式二
1.效果演示
2.创建标题栏类
3.定义相关函数
4.使用标题栏类
总结
前言
Qt自带的窗口标题栏通常遵循操作系统的默认样式和布局,以确保在不同平台上都能提供一致且符合用户期望的用户体验,因此Qt自带的窗口标题栏无法自定义。但我们在Qt设计软件时,经常需要改变窗口标题栏的样式,以满足不同场合用户需求。本文紧接着上一篇博文Qt之修改窗口标题、图标以及自定义标题栏(九)-CSDN博客的基础上,在介绍几种实现自定义标题栏的方法,并提供简单示例。
一、方式一
上一篇博文Qt之修改窗口标题、图标以及自定义标题栏(九)实现该方式是通过直接手鲁编写标题栏UI代码,下面提供的方式是在可视化界面UI上来设计标题栏,简洁方便直观,然后绑定/链接到主窗口界面上。
1.效果演示
2.创建标题栏类
跟上一个有区别的是,在原有的项目上,添加新的类文件,选择带ui设计类的模版,选择如下。
然后,定义类和ui的名称为MyTitleBar,MyTitleBar类的头文件代码如下(示例):
/** 只提供与上一篇不一样的代码地方 **/
QT_BEGIN_NAMESPACE
namespace Ui { class MyTitleBar; }
QT_END_NAMESPACE
class MyTitleBar : public QWidget
{
private slots:
// 按钮触发的槽;
void on_m_pButtonClose_clicked();
void on_m_pButtonMax_clicked();
void on_m_pButtonRestore_clicked();
void on_m_pButtonMin_clicked();
private:
Ui::MyTitleBar *ui;
};
3.可视化UI设计
上面我们定义标题栏类的头文件,下面来在可视化界面UI上来设计标题栏,双击打开.ui文件,在图像化设计界面设计如下标题栏。
上面设计完后,Qt会自动生成相应的UI代码和头文件,相关类为Ui_MyTitleBar。
4.定义相关函数
设计好MyTitleBar的UI文件后,下面来说明相关功能函数。
#include "ui_mytitlebar.h"
#include "mytitlebar.h"
MyTitleBar::MyTitleBar(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MyTitleBar)
{
// UI初始化
ui->setupUi(this);
// 初始化控件
initControl();
// 初始化标题栏色彩
setBackgroundColor(m_colorR, m_colorG, m_colorB, m_isTransparent);
// 加载本地样式 .css文件
//loadStyleSheet("MyTitle");
// 初始化标题栏色彩
setBackgroundColor(230,232,250,0);
setStyleSheet("QToolButton#m_pButtonMenu,QPushButton#m_pButtonMin,QPushButton#m_pButtonMax,QPushButton#m_pButtonClose{\
border-radius:3px;\
color:#324C6C;\
padding:3px;\
margin:0px;\
background:none;\
border-style:none;\
}");
setStyleSheet("QToolButton#m_pButtonMenu:hover,QPushButton#m_pButtonMin:hover,QPushButton#m_pButtonMax:hover{\
color:#FFFFFF;\
margin:1px 1px 2px 1px;\
background-color:rgba(51,127,209,230);\
}");
}
MyTitleBar::~MyTitleBar()
{
}
void MyTitleBar::initControl()
{
m_colorR=0;
m_colorG=153;
m_colorB=153;
m_isPressed=false;
m_buttonType=MIN_MAX_BUTTON;
m_windowBorderWidth=0;
m_isTransparent=false;
setFixedHeight(30);
setWindowFlags(Qt::FramelessWindowHint);
// 添加换肤菜单
QStringList name;
name << "银色" << "蓝色" << "浅蓝色" << "深蓝色";
foreach (QString str, name) {
QAction *action = new QAction(str, this);
ui->m_pButtonMenu->addAction(action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(onButtonchangeStyle()));
}
}
// 以下为按钮操作响应的槽
void MyTitleBar::onButtonchangeStyle()
{
QAction *act = (QAction *)sender();
QString name = act->text();
//QString qssFile = "qss/blue.css";
if (name == "银色") {
//qssFile = "qss/silvery.css";
setBackgroundColor(230,232,250,0);
}
else if (name == "蓝色"){
//qssFile = "qss/blue.css";
setBackgroundColor(50,76,108,0);
}
else if (name == "浅蓝色"){
//qssFile = "qss/lightblue.css";
setBackgroundColor(56,100,135,0);
}
else if (name == "深蓝色"){
//qssFile = "qss/darkblue.css";
setBackgroundColor(122,175,227,0);
}
//loadStyleSheet(qssFile);
//emit signalchangeStyle(qssFile);
}
void MyTitleBar::on_m_pButtonClose_clicked()
{
emit signalButtonCloseClicked();
}
void MyTitleBar::on_m_pButtonMax_clicked()
{
ui->m_pButtonMax->setVisible(false);
ui->m_pButtonRestore->setVisible(true);
emit signalButtonMaxClicked();
}
void MyTitleBar::on_m_pButtonRestore_clicked()
{
ui->m_pButtonRestore->setVisible(false);
ui->m_pButtonMax->setVisible(true);
emit signalButtonRestoreClicked();
}
void MyTitleBar::on_m_pButtonMin_clicked()
{
emit signalButtonMinClicked();
}
5.使用标题栏类
在用户的构造函数上面添加对标题栏类的使用,跟上一篇博文一样。
二、方式二
方式一实现自定义标题栏时,定义一个标题栏类,在生成标题栏时,会覆盖原有的MenuBar和ToolBar,方式二改进后,修改标题栏不覆盖,效果如下。
1.效果演示
2.创建标题栏类
跟上一篇博文一样,在原有的项目上,添加C++新的类文件,选择如下。
然后定义标题栏类,代码部分如下(示例):
#include <QDialog>
#include <QMutex>
class QLabel;
class QPushButton;
class QToolButton;
class QVBoxLayout;
class QHBoxLayout;
class QFrame;
class QSpacerItem;
class QLineEdit;
class QComboBox;
class QAbstractButton;
class QUIWidget : public QDialog
#endif
{
Q_OBJECT
Q_ENUMS(Style)
Q_PROPERTY(QString title READ getTitle WRITE setTitle)
Q_PROPERTY(Qt::Alignment alignment READ getAlignment WRITE setAlignment)
public:
explicit QUIWidget(QWidget *parent = 0);
~QUIWidget();
public Q_SLOTS:
//设置主窗体
void setMainWidget(QWidget *mainWidget);
//设置部件图标
void setIcon(QUIWidget::Widget widget, QChar str, quint32 size = 9);
//设置部件图片
void setPixmap(QUIWidget::Widget widget, const QString &file, const QSize &size = QSize(16, 16));
//设置部件是否可见
void setVisible(QUIWidget::Widget widget, bool visible = true);
//设置只有关闭按钮
void setOnlyCloseBtn();
//设置标题栏高度
void setTitleHeight(int height);
//设置按钮统一宽度
void setBtnWidth(int width);
//设置标题及文本样式
void setTitle(const QString &title);
void setAlignment(Qt::Alignment alignment);
};
3.定义相关函数
1.初始化函数
在标题栏类构造函数上,初始化如下设置,代码如下。
QUIWidget::QUIWidget(QWidget *parent) : QDialog(parent)
{
//定义标题栏字体文本编码
this->setTranslator(":/qm/qt_zh_CN.qm");
this->setCode();
this->initControl();
this->initForm();
//设置标题栏标题
#ifdef csd
setTitle("**软件");
#else
setTitle("csd演示版");
#endif
//设置标题文本居中
setAlignment(Qt::AlignCenter);
//设置窗体可拖动大小
setSizeGripEnabled(true);
//设置换肤下拉菜单可见
setVisible(QUIWidget::BtnMenu, true);
//设置标题栏高度
setTitleHeight(30);
//设置按钮宽度
setBtnWidth(30);
//设置软件左上角图标--没有图标时显示图形字体
setPixmap(QUIWidget::Lab_Ico, "ico/butterfly.ico", QSize(30,30));
//默认样式风格blue
setStyle(":/qss/blue.css");
QFont font;
font.setPointSize(12);
this->setFont(font);
}
void QUIWidget::initControl()
{
this->setObjectName(QString::fromUtf8("QUIWidget"));
this->resize(900, 750);
verticalLayout1 = new QVBoxLayout(this);
verticalLayout1->setSpacing(0);
verticalLayout1->setContentsMargins(11, 11, 11, 11);
verticalLayout1->setObjectName(QString::fromUtf8("verticalLayout1"));
verticalLayout1->setContentsMargins(1, 1, 1, 1);
widgetMain = new QWidget(this);
widgetMain->setObjectName(QString::fromUtf8("widgetMain"));
widgetMain->setStyleSheet(QString::fromUtf8(""));
verticalLayout2 = new QVBoxLayout(widgetMain);
verticalLayout2->setSpacing(0);
verticalLayout2->setContentsMargins(11, 11, 11, 11);
verticalLayout2->setObjectName(QString::fromUtf8("verticalLayout2"));
verticalLayout2->setContentsMargins(0, 0, 0, 0);
widget_title = new QWidget(widgetMain);
widget_title->setObjectName(QString::fromUtf8("widget_title"));
QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(widget_title->sizePolicy().hasHeightForWidth());
widget_title->setSizePolicy(sizePolicy);
widget_title->setMinimumSize(QSize(0, 30));
horizontalLayout4 = new QHBoxLayout(widget_title);
horizontalLayout4->setSpacing(0);
horizontalLayout4->setContentsMargins(11, 11, 11, 11);
horizontalLayout4->setObjectName(QString::fromUtf8("horizontalLayout4"));
horizontalLayout4->setContentsMargins(0, 0, 0, 0);
lab_Ico = new QLabel(widget_title);
lab_Ico->setObjectName(QString::fromUtf8("lab_Ico"));
QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred);
sizePolicy1.setHorizontalStretch(0);
sizePolicy1.setVerticalStretch(0);
sizePolicy1.setHeightForWidth(lab_Ico->sizePolicy().hasHeightForWidth());
lab_Ico->setSizePolicy(sizePolicy1);
lab_Ico->setMinimumSize(QSize(30, 0));
lab_Ico->setAlignment(Qt::AlignCenter);
horizontalLayout4->addWidget(lab_Ico);
lab_Title = new QLabel(widget_title);
lab_Title->setObjectName(QString::fromUtf8("lab_Title"));
QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Preferred);
sizePolicy2.setHorizontalStretch(0);
sizePolicy2.setVerticalStretch(0);
sizePolicy2.setHeightForWidth(lab_Title->sizePolicy().hasHeightForWidth());
lab_Title->setSizePolicy(sizePolicy2);
lab_Title->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter);
horizontalLayout4->addWidget(lab_Title);
widget_menu = new QWidget(widget_title);
widget_menu->setObjectName(QString::fromUtf8("widget_menu"));
sizePolicy1.setHeightForWidth(widget_menu->sizePolicy().hasHeightForWidth());
widget_menu->setSizePolicy(sizePolicy1);
horizontalLayout = new QHBoxLayout(widget_menu);
horizontalLayout->setSpacing(0);
horizontalLayout->setContentsMargins(11, 11, 11, 11);
horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
horizontalLayout->setContentsMargins(0, 0, 0, 0);
btnMenu = new QToolButton(widget_menu);
btnMenu->setObjectName(QString::fromUtf8("btnMenu"));
QSizePolicy sizePolicy3(QSizePolicy::Fixed, QSizePolicy::Expanding);
sizePolicy3.setHorizontalStretch(0);
sizePolicy3.setVerticalStretch(0);
sizePolicy3.setHeightForWidth(btnMenu->sizePolicy().hasHeightForWidth());
btnMenu->setSizePolicy(sizePolicy3);
btnMenu->setMinimumSize(QSize(30, 0));
btnMenu->setMaximumSize(QSize(30, 16777215));
btnMenu->setFocusPolicy(Qt::NoFocus);
btnMenu->setPopupMode(QToolButton::InstantPopup);
horizontalLayout->addWidget(btnMenu);
btnMenu_Min = new QPushButton(widget_menu);
btnMenu_Min->setObjectName(QString::fromUtf8("btnMenu_Min"));
QSizePolicy sizePolicy4(QSizePolicy::Minimum, QSizePolicy::Expanding);
sizePolicy4.setHorizontalStretch(0);
sizePolicy4.setVerticalStretch(0);
sizePolicy4.setHeightForWidth(btnMenu_Min->sizePolicy().hasHeightForWidth());
btnMenu_Min->setSizePolicy(sizePolicy4);
btnMenu_Min->setMinimumSize(QSize(30, 0));
btnMenu_Min->setMaximumSize(QSize(30, 16777215));
btnMenu_Min->setCursor(QCursor(Qt::ArrowCursor));
btnMenu_Min->setFocusPolicy(Qt::NoFocus);
horizontalLayout->addWidget(btnMenu_Min);
btnMenu_Max = new QPushButton(widget_menu);
btnMenu_Max->setObjectName(QString::fromUtf8("btnMenu_Max"));
sizePolicy3.setHeightForWidth(btnMenu_Max->sizePolicy().hasHeightForWidth());
btnMenu_Max->setSizePolicy(sizePolicy3);
btnMenu_Max->setMinimumSize(QSize(30, 0));
btnMenu_Max->setMaximumSize(QSize(30, 16777215));
btnMenu_Max->setCursor(QCursor(Qt::ArrowCursor));
btnMenu_Max->setFocusPolicy(Qt::NoFocus);
horizontalLayout->addWidget(btnMenu_Max);
btnMenu_Close = new QPushButton(widget_menu);
btnMenu_Close->setObjectName(QString::fromUtf8("btnMenu_Close"));
sizePolicy3.setHeightForWidth(btnMenu_Close->sizePolicy().hasHeightForWidth());
btnMenu_Close->setSizePolicy(sizePolicy3);
btnMenu_Close->setMinimumSize(QSize(30, 0));
btnMenu_Close->setMaximumSize(QSize(30, 16777215));
btnMenu_Close->setCursor(QCursor(Qt::ArrowCursor));
btnMenu_Close->setFocusPolicy(Qt::NoFocus);
horizontalLayout->addWidget(btnMenu_Close);
horizontalLayout4->addWidget(widget_menu);
verticalLayout2->addWidget(widget_title);
widget = new QWidget(widgetMain);
widget->setObjectName(QString::fromUtf8("widget"));
verticalLayout3 = new QVBoxLayout(widget);
verticalLayout3->setSpacing(0);
verticalLayout3->setContentsMargins(11, 11, 11, 11);
verticalLayout3->setObjectName(QString::fromUtf8("verticalLayout3"));
verticalLayout3->setContentsMargins(0, 0, 0, 0);
verticalLayout2->addWidget(widget);
verticalLayout1->addWidget(widgetMain);
connect(this->btnMenu_Min, SIGNAL(clicked(bool)), this, SLOT(on_btnMenu_Min_clicked()));
connect(this->btnMenu_Max, SIGNAL(clicked(bool)), this, SLOT(on_btnMenu_Max_clicked()));
connect(this->btnMenu_Close, SIGNAL(clicked(bool)), this, SLOT(on_btnMenu_Close_clicked()));
}
void QUIWidget::initForm()
{
//设置图形字体
setIcon(QUIWidget::Lab_Ico, QChar(0xf099), 11); //设置左上角图标-图形字体
setIcon(QUIWidget::BtnMenu, 0xf0d7);
setIcon(QUIWidget::BtnMenu_Min, 0xf068);
setIcon(QUIWidget::BtnMenu_Max, 0xf067);
setIcon(QUIWidget::BtnMenu_Close, 0xf00d);
//设置标题及对齐方式
setTitle("QUI Demo");
setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
setVisible(QUIWidget::BtnMenu, false);
mainWidget = 0;
max = false;
location = this->geometry();
this->setProperty("form", true);
this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint);//第一个参数是设置无边框。第二个参数是允许任务栏按钮右键菜单,第三个参数是允许最小化最大化与还原
this->setWindowFlags(Qt::FramelessWindowHint);//任务栏单击图标后最小化后图标会消失,无法解决,因此不让最小化
//绑定事件过滤器监听鼠标移动
this->installEventFilter(this);
this->widget_title->installEventFilter(this);
//添加换肤菜单
QStringList name;
name << "银色" << "蓝色" << "浅蓝色" << "深蓝色" << "灰色" << "浅灰色" << "深灰色" << "黑色"
<< "浅黑色" << "深黑色" << "PS黑色" << "黑色扁平" << "白色扁平" << "浅紫色" << "橙色";
foreach (QString str, name) {
QAction *action = new QAction(str, this);
this->btnMenu->addAction(action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(changeStyle()));
}
}
2.功能函数
具体设置部件图标、设置部件图片、设置部件是否可见、设置标题栏高度、设置按钮统一宽度、设置标题及文本样式等等设置,代码如下。
void QUIWidget::setIcon(QUIWidget::Widget widget, QChar str, quint32 size)
{
if (widget == QUIWidget::Lab_Ico) {
IconHelper::Instance()->setIcon(this->lab_Ico, str, size);
} else if (widget == QUIWidget::BtnMenu) {
IconHelper::Instance()->setIcon(this->btnMenu, str, size);
} else if (widget == QUIWidget::BtnMenu_Min) {
IconHelper::Instance()->setIcon(this->btnMenu_Min, str, size);
} else if (widget == QUIWidget::BtnMenu_Max) {
IconHelper::Instance()->setIcon(this->btnMenu_Max, str, size);
} else if (widget == QUIWidget::BtnMenu_Close) {
IconHelper::Instance()->setIcon(this->btnMenu_Close, str, size);
}
}
void QUIWidget::setPixmap(QUIWidget::Widget widget, const QString &file, const QSize &size)
{
QPixmap pix = QPixmap(file);
//按照宽高比自动缩放
pix = pix.scaled(size, Qt::KeepAspectRatio);
if (widget == QUIWidget::Lab_Ico) {
this->lab_Ico->setPixmap(pix);
} else if (widget == QUIWidget::BtnMenu) {
this->btnMenu->setIcon(QIcon(file));
} else if (widget == QUIWidget::BtnMenu_Min) {
this->btnMenu_Min->setIcon(QIcon(file));
} else if (widget == QUIWidget::BtnMenu_Max) {
this->btnMenu_Max->setIcon(QIcon(file));
} else if (widget == QUIWidget::BtnMenu_Close) {
this->btnMenu_Close->setIcon(QIcon(file));
}
}
void QUIWidget::setVisible(QUIWidget::Widget widget, bool visible)
{
if (widget == QUIWidget::Lab_Ico) {
this->lab_Ico->setVisible(visible);
} else if (widget == QUIWidget::BtnMenu) {
this->btnMenu->setVisible(visible);
} else if (widget == QUIWidget::BtnMenu_Min) {
this->btnMenu_Min->setVisible(visible);
} else if (widget == QUIWidget::BtnMenu_Max) {
this->btnMenu_Max->setVisible(visible);
} else if (widget == QUIWidget::BtnMenu_Close) {
this->btnMenu_Close->setVisible(visible);
}
}
void QUIWidget::setOnlyCloseBtn()
{
this->btnMenu->setVisible(false);
this->btnMenu_Min->setVisible(false);
this->btnMenu_Max->setVisible(false);
}
void QUIWidget::setTitleHeight(int height)
{
this->widget_title->setFixedHeight(height);
}
void QUIWidget::setBtnWidth(int width)
{
this->lab_Ico->setFixedWidth(width);
this->btnMenu->setFixedWidth(width);
this->btnMenu_Min->setFixedWidth(width);
this->btnMenu_Max->setFixedWidth(width);
this->btnMenu_Close->setFixedWidth(width);
}
void QUIWidget::setTitle(const QString &title)
{
if (this->title != title) {
this->title = title;
this->lab_Title->setText(title);
this->setWindowTitle(this->lab_Title->text());
}
}
void QUIWidget::setAlignment(Qt::Alignment alignment)
{
if (this->alignment != alignment) {
this->alignment = alignment;
this->lab_Title->setAlignment(alignment);
}
}
void QUIWidget::on_btnMenu_Min_clicked()
{
this->showMinimized();
}
void QUIWidget::on_btnMenu_Max_clicked()
{
if (max) {
this->setGeometry(location);
} else {
location = this->geometry();
this->setGeometry(qApp->desktop()->availableGeometry());
}
max = !max;
}
void QUIWidget::on_btnMenu_Close_clicked()
{
close();
}
3.窗口关联
打开主函数文件,在Main函数上将主窗口(用户)链接到标题栏窗口上,关联起来,代码如下。
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//定义用户窗口
MainWindow *w = new MainWindow;
//定义标题栏
QUIWidget qui;
//将主窗体链接/关联到标题栏窗口上
qui.setMainWidget(w);
qui.show();
return a.exec();
}
void QUIWidget::setMainWidget(QWidget *mainWidget)
{
//一个QUI窗体对象只能设置一个主窗体
if (this->mainWidget == 0) {
//将子窗体添加到布局
this->widget->layout()->addWidget(mainWidget);
//自动设置大小
resize(mainWidget->width(), mainWidget->height() + this->widget_title->height());
this->mainWidget = mainWidget;
this->mainWidget->installEventFilter(this);
}
}
4.使用标题栏类
因为在Main函数上,已经将主窗体链接/关联到标题栏窗口上,因此在用户层无需关注/设置标题栏相关参数。这里和方式一不一样的是,方式是将标题栏类作为主窗口的私有变量使用,而方式二只是将两个窗口界面关联起来而已。
至此方式二讲解完毕,该种方式有个缺点是需要手鲁UI设计代码。更快捷的是,通过可视化界面UI来设计标题栏,然后绑定/链接到主窗口界面上,跟方式一一样。
总结
博文中相应的工程代码Qt-Case.zip 利用Qt开发软件进行编的例程,为博文提供案例-CSDN文库。