Qt开发 系列文章 - titles-icons-titlebars(九)
目录
前言
在我们利用Qt设计软件时,经常需要修改窗口标题、更改软件图标等之类的操作,更有甚者需要更换标题栏(因为Qt自带的窗口标题栏无法自定义)。这时需要用到QIcon类、QPixmap类等等相关基础功能的操作,以下是关于如何在不同场景中添加图标、修改标题、更换标题栏(自定义标题栏)的简单例子。
一、修改标题
本文提供修改软件窗口标题的方式有两种。
- 第一种直接在软件初始化函数(即构造函数)添加如下代码。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("我的窗口");
}
- 第二种,打开项目可视化UI设计界面,在上面进行修改,如下图所示。
以上两种方法修改完后,编译运行,会看到显示如下。
二、添加图标
添加图标的方式有多种,本文提供如下几种供参考。不管有几种实现方式,首先第一步是先把有图标的文件添加本项目路劲下面。
- 第一种方式,打开项目pro文件,添加如下代码。
win32:RC_ICONS += $$PWD/ico/A_tubiao.ico
- 第二种方式,在软件初始化函数(即构造函数)添加如下代码。
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("我的窗口");
this->setWindowIcon(QIcon(QDir::currentPath() + "/ico/A_tubiao.ico"));
}
- 第三种方式,打开项目可视化UI设计界面,在上面进行修改,如下图所示。
上面添加完后,会有如下显示,说明添加成功。
以上几种方法修改完后,编译运行,会看到显示如下。
三、更换标题栏
Qt是一个跨平台的框架,它旨在提供在不同操作系统上都能保持一致的用户界面体验。Qt自带的窗口标题栏通常遵循操作系统的默认样式和布局,以确保在不同平台上都能提供一致且符合用户期望的用户体验,因此Qt自带的窗口标题栏无法自定义。
尽管Qt自带的窗口标题栏无法自定义,但可以通过一些方法来实现自定义标题栏的效果。例如,可以隐藏系统自带的标题栏(通过设置窗口标志Qt::FramelessWindowHint),然后自行设计一个包含所需功能(如最小化、最大化、关闭按钮等)的自定义标题栏。下面本文将提供一种自定义标题栏。
1.效果演示
2.创建标题栏类
在我们原有的项目上,添加新的类文件,选择C++设计类的模版,选择如下。
然后,定义类的名称为MyTitleBar,代码如下(示例):
#ifndef MYTITLEBAR_H
#define MYTITLEBAR_H
#include <QWidget>
enum ButtonType
{
MIN_BUTTON = 0, // 最小化和关闭按钮;
MIN_MAX_BUTTON , // 最小化、最大化和关闭按钮;
ONLY_CLOSE_BUTTON // 只有关闭按钮;
};
class MyTitleBar : public QWidget
{
Q_OBJECT
public:
MyTitleBar(QWidget *parent);
~MyTitleBar();
void setBackgroundColor(int r, int g, int b , bool isTransparent = false);
void setTitleIcon(QString filePath , QSize IconSize = QSize(25 , 25));
void setTitleContent(QString titleContent , int titleFontSize = 9);
void setTitleWidth(int width);
void setButtonType(ButtonType buttonType);
void setTitleRoll();
void setWindowBorderWidth(int borderWidth);
void saveRestoreInfo(const QPoint point, const QSize size);
void getRestoreInfo(QPoint& point, QSize& size);
private:
void paintEvent(QPaintEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void initControl();
void initConnections();
void loadStyleSheet(const QString &qssFile);
signals:
void signalchangeStyle(const QString &qssFile);
void signalButtonMinClicked();
void signalButtonRestoreClicked();
void signalButtonMaxClicked();
void signalButtonCloseClicked();
private slots:
void onButtonchangeStyle();
void onButtonMinClicked();
void onButtonRestoreClicked();
void onButtonMaxClicked();
void onButtonCloseClicked();
void onRollTitle();
private:
QLabel* m_pIcon; // 标题栏图标;
QLabel* m_pTitleContent; // 标题栏内容;
QToolButton *m_pButtonMenu; // 色彩菜单
QPushButton* m_pButtonMin; // 最小化按钮;
QPushButton* m_pButtonRestore; // 最大化还原按钮;
QPushButton* m_pButtonMax; // 最大化按钮;
QPushButton* m_pButtonClose; // 关闭按钮;
int m_colorR;
int m_colorG;
int m_colorB;
QPoint m_restorePos;
QSize m_restoreSize;
bool m_isPressed;
QPoint m_startMovePos;
QTimer m_titleRollTimer;
QString m_titleContent;
ButtonType m_buttonType;
int m_windowBorderWidth;
bool m_isTransparent;
};
#endif // MYTITLEBAR_H
3.定义相关函数
上面我们定义标题栏类的头文件,下面来说明相关功能函数。
#include "mytitlebar.h"
#include <QHBoxLayout>
#include <QPainter>
#include <QFile>
#include <QMouseEvent>
#include <QAction>
MyTitleBar::MyTitleBar(QWidget *parent)
: QWidget(parent)
, m_colorR(0)
, m_colorG(153)
, m_colorB(153)
, m_isPressed(false)
, m_buttonType(MIN_MAX_BUTTON)
, m_windowBorderWidth(0)
, m_isTransparent(false)
{
// 初始化;
initControl();
// 信号槽的绑定;
initConnections();
// 加载本地样式 MyTitle.css文件;
//loadStyleSheet("MyTitle");
}
// 初始化控件;
void MyTitleBar::initControl()
{
// 定义按键变量
m_pIcon = new QLabel;
m_pTitleContent = new QLabel;
m_pButtonMenu = new QToolButton;
m_pButtonMin = new QPushButton;
m_pButtonRestore = new QPushButton;
m_pButtonMax = new QPushButton;
m_pButtonClose = new QPushButton;
QSizePolicy sizePolicy3(QSizePolicy::Fixed, QSizePolicy::Expanding);
sizePolicy3.setHorizontalStretch(0);
sizePolicy3.setVerticalStretch(0);
sizePolicy3.setHeightForWidth(m_pButtonMenu->sizePolicy().hasHeightForWidth());
m_pButtonMenu->setSizePolicy(sizePolicy3);
m_pButtonMenu->setMinimumSize(QSize(30, 0));
m_pButtonMenu->setMaximumSize(QSize(30, 16777215));
m_pButtonMenu->setFocusPolicy(Qt::NoFocus);
m_pButtonMenu->setPopupMode(QToolButton::InstantPopup);
// 设置按键大小
m_pButtonMin->setFixedSize(QSize(30, 30));
m_pButtonRestore->setFixedSize(QSize(30, 30));
m_pButtonMax->setFixedSize(QSize(30, 30));
m_pButtonClose->setFixedSize(QSize(30, 30));
// 设置名称
m_pTitleContent->setObjectName("TitleContent");
m_pButtonMenu->setObjectName("btnMenu");
m_pButtonMin->setObjectName("ButtonMin");
m_pButtonRestore->setObjectName("ButtonRestore");
m_pButtonMax->setObjectName("ButtonMax");
m_pButtonClose->setObjectName("ButtonClose");
// 设置名称
m_pButtonMenu->setToolTip(QStringLiteral("菜单"));
m_pButtonMin->setToolTip(QStringLiteral("最小化"));
m_pButtonRestore->setToolTip(QStringLiteral("还原"));
m_pButtonMax->setToolTip(QStringLiteral("最大化"));
m_pButtonClose->setToolTip(QStringLiteral("关闭"));
// 定义图标
QIcon icon,icon1;
// 设置菜单图标
icon.addFile(QString("ico/branch_menu.png"), QSize(), QIcon::Normal, QIcon::Off);
m_pButtonMenu->setIcon(icon);
// 设置关闭图标
icon1.addFile(QString("ico/branch_close.png"), QSize(), QIcon::Normal, QIcon::Off);
m_pButtonClose->setIcon(icon1);
// 设置最小化图标
icon.addFile(QString("ico/branch_min.png"), QSize(), QIcon::Normal, QIcon::Off);
m_pButtonMin->setIcon(icon);
// 设置最大化图标
icon.addFile(QString("ico/branch_max.png"), QSize(), QIcon::Normal, QIcon::Off);
m_pButtonMax->setIcon(icon);
// 设置还原图标
icon.addFile(QString("ico/branch_res.png"), QSize(), QIcon::Normal, QIcon::Off);
m_pButtonRestore->setIcon(icon);
// 创建Layout框
QHBoxLayout* mylayout = new QHBoxLayout(this);
mylayout->addWidget(m_pIcon);
mylayout->addWidget(m_pTitleContent);
mylayout->addWidget(m_pButtonMenu);
mylayout->addWidget(m_pButtonMin);
mylayout->addWidget(m_pButtonRestore);
mylayout->addWidget(m_pButtonMax);
mylayout->addWidget(m_pButtonClose);
// 设置Layout框格式
mylayout->setContentsMargins(5, 0, 0, 0);
mylayout->setSpacing(0);
// 设置标题栏内容大小
m_pTitleContent->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
this->setFixedHeight(TITLE_HEIGHT);
this->setWindowFlags(Qt::FramelessWindowHint);
// 添加换肤菜单
QStringList name;
name << "蓝色" << "浅蓝色" << "灰色" << "浅灰色" << "浅黑色" << "浅紫色" << "橙色";
foreach (QString str, name) {
QAction *action = new QAction(str, this);
this->m_pButtonMenu->addAction(action);
connect(action, SIGNAL(triggered(bool)), this, SLOT(onButtonchangeStyle()));
}
}
// 信号槽的绑定;
void MyTitleBar::initConnections()
{
connect(m_pButtonMin, SIGNAL(clicked()), this, SLOT(onButtonMinClicked()));
connect(m_pButtonRestore, SIGNAL(clicked()), this, SLOT(onButtonRestoreClicked()));
connect(m_pButtonMax, SIGNAL(clicked()), this, SLOT(onButtonMaxClicked()));
connect(m_pButtonClose, SIGNAL(clicked()), this, SLOT(onButtonCloseClicked()));
}
// 以下为按钮操作响应的槽
void MyTitleBar::onButtonchangeStyle()
{
QAction *act = (QAction *)sender();
QString name = act->text();
QString qssFile = "qss/blue.css";
if (name == "蓝色")
qssFile = "qss/blue.css";
else if (name == "浅蓝色")
qssFile = "qss/lightblue.css";
else if (name == "灰色")
qssFile = "qss/flatgray.css";
else if (name == "浅灰色")
qssFile = "qss/lightgray.css";
else if (name == "浅黑色")
qssFile = "qss/blacksoft.css";
else if (name == "浅紫色")
qssFile = "qss/purple.css";
else if (name == "橙色")
qssFile = "qss/orange.css";
loadStyleSheet(qssFile);
emit signalchangeStyle(qssFile);
}
void MyTitleBar::onButtonMinClicked()
{
emit signalButtonMinClicked();
}
void MyTitleBar::onButtonRestoreClicked()
{
m_pButtonRestore->setVisible(false);
m_pButtonMax->setVisible(true);
emit signalButtonRestoreClicked();
}
void MyTitleBar::onButtonMaxClicked()
{
m_pButtonMax->setVisible(false);
m_pButtonRestore->setVisible(true);
emit signalButtonMaxClicked();
}
void MyTitleBar::onButtonCloseClicked()
{
emit signalButtonCloseClicked();
}
// 加载本地样式文件;
// 可以将样式直接写在文件中,程序运行时直接加载进来;
void MyTitleBar::loadStyleSheet(const QString &qssFile)
{
QFile file(qssFile);
if (file.open(QFile::ReadOnly)) {
QString qss = this->styleSheet();
qss += QLatin1String(file.readAll());
this->setStyleSheet(qss);
file.close();
}
}
// 设置标题栏背景色,在paintEvent事件中进行绘制标题栏背景色;
// 在构造函数中给了默认值,可以外部设置颜色值改变标题栏背景色;
void MyTitleBar::setBackgroundColor(int r, int g, int b, bool isTransparent)
{
m_colorR = r;
m_colorG = g;
m_colorB = b;
m_isTransparent = isTransparent;
// 重新绘制(调用paintEvent事件);
update();
}
// 设置标题栏图标;
void MyTitleBar::setTitleIcon(QString filePath, QSize IconSize)
{
QPixmap titleIcon(filePath);
m_pIcon->setPixmap(titleIcon.scaled(IconSize));
}
void MyTitleBar::setTitleContent(QString titleContent, int titleFontSize)
{
// 设置标题字体大小;
QFont font = m_pTitleContent->font();
font.setPointSize(titleFontSize);
m_pTitleContent->setFont(font);
// 设置标题内容;
m_pTitleContent->setText(titleContent);
m_titleContent = titleContent;
}
// 设置标题栏长度;
void MyTitleBar::setTitleWidth(int width)
{
this->setFixedWidth(width);
}
// 设置标题栏上按钮类型;
// 由于不同窗口标题栏上的按钮都不一样,所以可以自定义标题栏中的按钮;
// 这里提供了四个按钮,分别为最小化、还原、最大化、关闭按钮,如果需要其他按钮可自行添加设置;
void MyTitleBar::setButtonType(ButtonType buttonType)
{
m_buttonType = buttonType;
switch (buttonType)
{
case MIN_BUTTON:
{
m_pButtonRestore->setVisible(false);
m_pButtonMax->setVisible(false);
}
break;
case MIN_MAX_BUTTON:
{
m_pButtonRestore->setVisible(false);
}
break;
case ONLY_CLOSE_BUTTON:
{
m_pButtonMin->setVisible(false);
m_pButtonRestore->setVisible(false);
m_pButtonMax->setVisible(false);
}
break;
default:
break;
}
}
// 设置标题栏中的标题是否会自动滚动;
// 一般情况下标题栏中的标题内容是不滚动的,但是既然自定义就看自己需要嘛,想怎么设计就怎么搞O(∩_∩)O!
void MyTitleBar::setTitleRoll()
{
connect(&m_titleRollTimer, SIGNAL(timeout()), this, SLOT(onRollTitle()));
m_titleRollTimer.start(200);
}
// 设置窗口边框宽度;
void MyTitleBar::setWindowBorderWidth(int borderWidth)
{
m_windowBorderWidth = borderWidth;
}
// 保存窗口最大化前窗口的位置以及大小;
void MyTitleBar::saveRestoreInfo(const QPoint point, const QSize size)
{
m_restorePos = point;
m_restoreSize = size;
}
// 获取窗口最大化前窗口的位置以及大小;
void MyTitleBar::getRestoreInfo(QPoint& point, QSize& size)
{
point = m_restorePos;
size = m_restoreSize;
}
// 绘制标题栏背景色;
void MyTitleBar::paintEvent(QPaintEvent *event)
{
// 是否设置标题透明;
if (!m_isTransparent)
{
//设置背景色;
QPainter painter(this);
QPainterPath pathBack;
pathBack.setFillRule(Qt::WindingFill);
pathBack.addRoundedRect(QRect(0, 0, this->width(), this->height()), 3, 3);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.fillPath(pathBack, QBrush(QColor(m_colorR, m_colorG, m_colorB)));
}
// 当窗口最大化或者还原后,窗口长度变了,标题栏的长度应当一起改变;
// 这里减去m_windowBorderWidth ,是因为窗口可能设置了不同宽度的边框;
// 如果窗口有边框则需要设置m_windowBorderWidth的值,否则m_windowBorderWidth默认为0;
if (this->width() != (this->parentWidget()->width() - m_windowBorderWidth))
{
this->setFixedWidth(this->parentWidget()->width() - m_windowBorderWidth);
}
QWidget::paintEvent(event);
}
// 双击响应事件,主要是实现双击标题栏进行最大化和最小化操作;
void MyTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
// 只有存在最大化、还原按钮时双击才有效;
if (m_buttonType == MIN_MAX_BUTTON)
{
// 通过最大化按钮的状态判断当前窗口是处于最大化还是原始大小状态;
// 或者通过单独设置变量来表示当前窗口状态;
if (m_pButtonMax->isVisible())
{
onButtonMaxClicked();
}
else
{
onButtonRestoreClicked();
}
}
return QWidget::mouseDoubleClickEvent(event);
}
// 以下通过mousePressEvent、mouseMoveEvent、mouseReleaseEvent三个事件实现了鼠标拖动标题栏移动窗口的效果;
void MyTitleBar::mousePressEvent(QMouseEvent *event)
{
if (m_buttonType == MIN_MAX_BUTTON)
{
// 在窗口最大化时禁止拖动窗口;
if (m_pButtonMax->isVisible())
{
m_isPressed = true;
m_startMovePos = event->globalPos();
}
}
else
{
m_isPressed = true;
m_startMovePos = event->globalPos();
}
return QWidget::mousePressEvent(event);
}
void MyTitleBar::mouseMoveEvent(QMouseEvent *event)
{
if (m_isPressed)
{
QPoint movePoint = event->globalPos() - m_startMovePos;
QPoint widgetPos = this->parentWidget()->pos();
m_startMovePos = event->globalPos();
this->parentWidget()->move(widgetPos.x() + movePoint.x(), widgetPos.y() + movePoint.y());
}
return QWidget::mouseMoveEvent(event);
}
void MyTitleBar::mouseReleaseEvent(QMouseEvent *event)
{
m_isPressed = false;
return QWidget::mouseReleaseEvent(event);
}
// 该方法主要是让标题栏中的标题显示为滚动的效果;
void MyTitleBar::onRollTitle()
{
static int nPos = 0;
QString titleContent = m_titleContent;
// 当截取的位置比字符串长时,从头开始;
if (nPos > titleContent.length())
nPos = 0;
m_pTitleContent->setText(titleContent.mid(nPos));
nPos++;
}
4.使用标题栏类
在用户的构造函数上面添加对标题栏类的使用,主要是设置主窗口MainWindow的去除边框功能(通过设置窗口标志Qt::FramelessWindowHint)、设置窗口最小化时点击任务栏窗口可以显示出原窗口(通过设置窗口标志Qt::WindowMinimizeButtonHint)、设置窗口背景透明(通过设置窗口标志Qt::WA_TranslucentBackground),然后在初始化标题栏类,关联标题栏类和主窗口类的相关信号和槽函数,实现所需最小化、最大化、关闭按钮等功能,并包括对标题栏类图标、标题、大小尺寸等效果设置,具体代码如下。
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void onButtonchangeStyle(const QString &qssFile);
void onButtonMinClicked();
void onButtonRestoreClicked();
void onButtonMaxClicked();
void onButtonCloseClicked();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
void initTitleBar();
void paintEvent(QPaintEvent *event) override;
private:
Ui::MainWindow *ui;
MyTitleBar* m_titleBar;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 设置窗口去除边框 窗口最小化时点击任务栏窗口可以显示出原窗口;
this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);
// 设置窗口背景透明;
setAttribute(Qt::WA_TranslucentBackground);
// 关闭窗口时释放资源(注意!!!如果是主窗口不要设置WA_DeleteOnClose属性)
//setAttribute(Qt::WA_DeleteOnClose);
/*** 初始化标题栏 ***/
initTitleBar();
/*** 画图界面初始化 ***/
InitChart();
/*** 注册事件过滤器 ***/
ui->widget->installEventFilter(this);
ui->tab->installEventFilter(this);
ui->tab_2->installEventFilter(this);
/*** 创建右键菜单 ***/
CreateMenu(ui->widget);
CreateMenu(ui->tab);
CreateMenu(ui->tab_2);
/*** 工具栏影藏 ***/
ui->toolBar->hide();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::initTitleBar()
{
m_titleBar = new MyTitleBar(this);
m_titleBar->move(0, 0);
connect(m_titleBar, SIGNAL(signalchangeStyle(QString)), this, SLOT(onButtonchangeStyle(QString)));
connect(m_titleBar, SIGNAL(signalButtonMinClicked()), this, SLOT(onButtonMinClicked()));
connect(m_titleBar, SIGNAL(signalButtonRestoreClicked()), this, SLOT(onButtonRestoreClicked()));
connect(m_titleBar, SIGNAL(signalButtonMaxClicked()), this, SLOT(onButtonMaxClicked()));
connect(m_titleBar, SIGNAL(signalButtonCloseClicked()), this, SLOT(onButtonCloseClicked()));
// 设置标题栏效果,可以不设置;
m_titleBar->setTitleRoll();
// 设置标题栏图标;
m_titleBar->setTitleIcon(QDir::currentPath() + "/ico/butterfly.ico");
m_titleBar->setTitleContent(QStringLiteral("***Qt更改标题栏动态效果测试***"));
m_titleBar->setButtonType(MIN_MAX_BUTTON);
m_titleBar->setTitleWidth(this->width());
}
void MainWindow::onButtonchangeStyle(const QString &qssFile)
{
QFile file(qssFile);
if (file.open(QFile::ReadOnly)) {
QString qss = this->styleSheet();
qss += QLatin1String(file.readAll());
qApp->setStyleSheet(qss);
file.close();
}
}
void MainWindow::onButtonMinClicked()
{
showMinimized();
}
void MainWindow::onButtonRestoreClicked()
{
QPoint windowPos;
QSize windowSize;
m_titleBar->getRestoreInfo(windowPos, windowSize);
this->setGeometry(QRect(windowPos, windowSize));
}
void MainWindow::onButtonMaxClicked()
{
m_titleBar->saveRestoreInfo(this->pos(), QSize(this->width(), this->height()));
QRect desktopRect = QApplication::desktop()->availableGeometry();
QRect FactRect = QRect(desktopRect.x() - 3, desktopRect.y() - 3, desktopRect.width() + 6, desktopRect.height() + 6);
setGeometry(FactRect);
}
void MainWindow::onButtonCloseClicked()
{
close();
}
void MainWindow::paintEvent(QPaintEvent* event)
{
//设置背景色;
QPainter painter(this);
QPainterPath pathBack;
pathBack.setFillRule(Qt::WindingFill);
pathBack.addRoundedRect(QRect(0, 0, this->width(), this->height()), 3, 3);
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
painter.fillPath(pathBack, QBrush(QColor(238, 223, 204)));
return QWidget::paintEvent(event);
}
以上是对自定义标题栏的实现一种,相关例程附文末,当然还有其他实现方式,例如,更快捷的是,通过可视化界面UI来设计标题栏,然后绑定/链接到主窗口界面上,具体例程可见文末链接处。
总结
博文中相应的工程代码Qt-Case.zip 利用Qt开发软件进行编的例程,为博文提供案例-CSDN文库。