Hello Qt -- Qt custom title bar

Posted by infomamun on Sun, 06 Mar 2022 17:54:12 +0100

1, Introduction to Qt custom title bar

The title bar of QWidget and its subclass form components is controlled by the operating system, that is, the interface style of the title bar is the same as the theme style of the operating system. In engineering practice, developers need to define it by themselves to beautify the application interface.

2, Qt custom title bar implementation

1. Customize the function of title block

The functions required for customizing the title bar are as follows:

(1) A custom title bar needs to contain graphic elements such as minimize button, maximize button, close button, title label, icon label, etc.

(2) Drag and drop the title bar.

(3) Double click the title bar to maximize and minimize the form.

2. Customize the interface layout of the title block

The interface layout of custom title bar is as follows:

3. Implementation of title bar drag function

The dragging and panning process of the form is as follows:

When the mouse is pressed and moved in the title bar of the form, the form will translate according to the track of the mouse. Therefore, every time the form moves, it moves according to the vector of mouse movement in the current position. The implementation of the title bar drag function requires the implementation of three event handling functions: mousePressEvent, mouseMoveEvent and mouserereleaseevent.

The globalPos() function in MouseEvent returns the position coordinates relative to the screen, while pos() returns the position of the mouse in the current control (that is, the control that captures the mouse event).
Geometry () of QWidget form Topleft() returns the position of the upper left corner of the current form on the screen.

startPos = event->globalPos();//The global initial position of the mouse, remember when pressing
curWindowPos = geometry().topleft();//Global position of the form, when moving
endPos = event->globalPos();//The position after the mouse is pressed and moved. When moving
move(curWindowPos+(startPos-endPos));//According to the vector, the moving direction is the initial position minus the end position
startPos = endPos;//Record the initial position as the last last position, and then execute until the drag is released

The implementation code is as follows:

void TitleBar::mousePressEvent(QMouseEvent *event)
{
    //Left mouse button press event
    if (event->button() == Qt::LeftButton)
    {
        //Record the left mouse button status
        m_leftButtonPressed = true;
        //Record the position of the mouse on the screen
        m_start = event->globalPos();
    }
}

void TitleBar::mouseMoveEvent(QMouseEvent *event)
{
    //Press and hold continuously to do the corresponding event
    if(m_leftButtonPressed)
    {
        //Move the parent form to the original position of the parent form plus the position where the mouse moves: Event - > globalpos() - M_ start
        parentWidget()->move(parentWidget()->geometry().topLeft() +
                             event->globalPos() - m_start);

        //Replace the position of the mouse in the screen with the new position
        m_start = event->globalPos();
    }
}

void TitleBar::mouseReleaseEvent(QMouseEvent *event)
{
    //Left mouse button release
    if (event->button() == Qt::LeftButton)
    {
        //Record mouse status
        m_leftButtonPressed = false;
    }
}

4. Double click the title bar to maximize and minimize

Double click the mouse and the event handling function mouseDoubleClickEvent is implemented as follows:

void TitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
    m_maximizeButton->click();
}

The slot functions of the maximize, minimize and close buttons are as follows:

void TitleBar::onClicked()
{
    QPushButton *pButton = qobject_cast<QPushButton *>(sender());
    QWidget *pWindow = this->window();
    if (pWindow->isTopLevel())
    {
        if (pButton == m_minimizeButton)
        {
            pWindow->showMinimized();
        }
        else if (pButton == m_maximizeButton)
        {
            pWindow->isMaximized() ? pWindow->showNormal() : pWindow->showMaximized();
        }
        else if (pButton == m_closeButton)
        {
            pWindow->close();
        }
    }
}

3, Qt custom form base class example

1. Customize the functions of the form base class

The functions of the custom form base class are as follows:

(1) Customize the title block.

(2) Add a content component, and the internal interface layout of the content component is completely determined by specific users.

2. Implementation of custom form base class

TitleBar.h documents:

#ifndef TITLEBAR_H
#define TITLEBAR_H

#include <QWidget>
#include <QPushButton>
#include <QLabel>
#include <QHBoxLayout>
#include <QEvent>
#include <QMouseEvent>
#include <QApplication>
#include <QPoint>
#include <QPixmap>
#include <QString>
/**
 * @brief Title block interface components
 * @author 
 */

class TitleBar : public QWidget
{
    Q_OBJECT
public:
    explicit TitleBar(QWidget *parent = NULL);
    /**
     * @brief Set title block title
     * @param title,Parameter, set title
     */
    void setWindowTitle(const QString& title);
    /**
     * @brief Sets the icon for the title block
     * @param iconPath,Parameter, path of Icon
     */
    void SetTitleBarIcon(const QString& iconPath);

protected:
    /**
     * @brief Double click the event handler
     * @param event,Parameters, events
     * @note Double click the title bar to maximize / restore the interface
     */
    virtual void mouseDoubleClickEvent(QMouseEvent *event);

    /**
     * @brief Mouse down event handler
     * @param event,Parameters, events
     * @note Press the left mouse button
     */
    virtual void mousePressEvent(QMouseEvent *event);

    /**
     * @brief Mouse movement event handler
     * @param event,Parameters, events
     * @note Move mouse
     */
    virtual void mouseMoveEvent(QMouseEvent *event);

    /**
     * @brief Mouse release event handler
     * @param event,Parameters, events
     * @note Release the mouse
     */
    virtual void mouseReleaseEvent(QMouseEvent *event);

    /**
     * @brief Event filter processor
     * @param obj,parameter
     * @param event,Parameters, events
     * @return Return true for success and false for failure
     * @note Set title and Icon
     */
    virtual bool eventFilter(QObject *obj, QEvent *event);

    /**
     * @brief Maximize / restore
     */
    void updateMaximize();
protected slots:
    /**
     * @brief Slot functions that respond when the minimize, maximize / restore, and close buttons are clicked
     */
    void onClicked();

private:
    QLabel* m_iconLabel;
    QLabel* m_titleLabel;
    QPushButton* m_minimizeButton;
    QPushButton* m_maximizeButton;
    QPushButton* m_closeButton;
    QPoint m_start;//starting point
    QPoint m_end;//End point
    bool m_leftButtonPressed;//Press the mark with the left mouse button
};

#endif // TITLEBAR_H

TitleBar.cpp file:

#include "TitleBar.h"


TitleBar::TitleBar(QWidget *parent) : QWidget(parent)
{
    setFixedHeight(30);
    setWindowFlags(Qt::FramelessWindowHint);
    m_iconLabel = new QLabel(this);
    m_iconLabel->setFixedSize(20, 20);
    m_iconLabel->setScaledContents(true);

    m_titleLabel = new QLabel(this);
    m_titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);

    m_minimizeButton = new QPushButton(this);
    m_minimizeButton->setFixedSize(27, 22);
    m_minimizeButton->setObjectName("minimizeButton");

    m_maximizeButton = new QPushButton(this);
    m_maximizeButton->setFixedSize(27, 22);
    m_maximizeButton->setObjectName("maximizeButton");

    m_closeButton = new QPushButton(this);
    m_closeButton->setFixedSize(27, 22);
    m_closeButton->setObjectName("closeButton");

    QHBoxLayout* layout = new QHBoxLayout;
    layout->addWidget(m_iconLabel);
    layout->addStretch(1);
    layout->addWidget(m_titleLabel);
    layout->addStretch(1);
    layout->addWidget(m_minimizeButton);
    layout->addWidget(m_maximizeButton);
    layout->addWidget(m_closeButton);
    setLayout(layout);

    setProperty("titleBar", true);
    setObjectName("titleBar");
    connect(m_minimizeButton, SIGNAL(clicked(bool)), this, SLOT(onClicked()));
    connect(m_maximizeButton, SIGNAL(clicked(bool)), this, SLOT(onClicked()));
    connect(m_closeButton, SIGNAL(clicked(bool)), this, SLOT(onClicked()));
}

void TitleBar::setWindowTitle(const QString &title)
{
    m_titleLabel->setAlignment(Qt::AlignCenter);
    m_titleLabel->setText(title);
}

void TitleBar::SetTitleBarIcon(const QString &iconPath)
{
    QPixmap map(iconPath);
    m_iconLabel->setPixmap(map);
}

void TitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
    m_maximizeButton->click();
}

void TitleBar::mousePressEvent(QMouseEvent *event)
{
    //Left mouse button press event
    if (event->button() == Qt::LeftButton)
    {
        //Record the left mouse button status
        m_leftButtonPressed = true;
        //Record the position of the mouse on the screen
        m_start = event->globalPos();
    }
}

void TitleBar::mouseMoveEvent(QMouseEvent *event)
{
    //Press and hold continuously to do the corresponding event
    if(m_leftButtonPressed)
    {
        //Move the parent form to the original position of the parent form plus the position where the mouse moves: Event - > globalpos() - M_ start
        parentWidget()->move(parentWidget()->geometry().topLeft() +
                             event->globalPos() - m_start);
        //Replace the position of the mouse in the screen with the new position
        m_start = event->globalPos();
    }
}

void TitleBar::mouseReleaseEvent(QMouseEvent *event)
{
    //Left mouse button release
    if (event->button() == Qt::LeftButton)
    {
        //Record mouse status
        m_leftButtonPressed = false;
    }
}

bool TitleBar::eventFilter(QObject *obj, QEvent *event)
{
    switch(event->type())
    {
    //Set title
    case QEvent::WindowTitleChange:
    {
        QWidget *pWidget = qobject_cast<QWidget *>(obj);
        if (pWidget)
        {
            m_titleLabel->setText(pWidget->windowTitle());
            return true;
        }
    }

    //Settings Icon
    case QEvent::WindowIconChange:
    {
        QWidget *pWidget = qobject_cast<QWidget *>(obj);
        if (pWidget)
        {
            QIcon icon = pWidget->windowIcon();
            m_iconLabel->setPixmap(icon.pixmap(m_iconLabel->size()));
            return true;
        }
    }

    //Window state change and window size change
    case QEvent::WindowStateChange:
    case QEvent::Resize:
        updateMaximize();
        return true;
    }

    return QWidget::eventFilter(obj, event);
}

void TitleBar::updateMaximize()
{
    QWidget *pWindow = this->window();
    if (pWindow->isTopLevel())
    {
        bool bMaximize = pWindow->isMaximized();
        if (bMaximize)
        {
            m_maximizeButton->setToolTip(tr("Restore"));
            m_maximizeButton->setProperty("maximizeProperty", "restore");
        }
        else
        {
            m_maximizeButton->setProperty("maximizeProperty", "maximize");
            m_maximizeButton->setToolTip(tr("Maximize"));
        }
        m_maximizeButton->setStyle(QApplication::style());
    }
}

void TitleBar::onClicked()
{
    QPushButton *pButton = qobject_cast<QPushButton *>(sender());
    QWidget *pWindow = this->window();
    if (pWindow->isTopLevel())
    {
        if (pButton == m_minimizeButton)
        {
            pWindow->showMinimized();
        }
        else if (pButton == m_maximizeButton)
        {
            pWindow->isMaximized() ? pWindow->showNormal() : pWindow->showMaximized();
        }
        else if (pButton == m_closeButton)
        {
            pWindow->close();
        }
    }
}

QWindowBase.h documents:

#ifndef QWINDOWBASE_H
#define QWINDOWBASE_H

#include <QFrame>
#include <QWidget>
#include <QVBoxLayout>
#include "TitleBar.h"

/**
 * @brief Interface component base class
 * @note QWindowBase Interface components are mainly used as top-level windows. QWidget is used for interface components that are not top-level windows.
 */
class QWindowBase : public QFrame
{
    Q_OBJECT
public:
    QWindowBase(QFrame* parent = NULL);
    /**
     * @brief Set title
     * @param title,Input parameters, title content
     */
    void setWindowTitle(const QString& title);
    /**
     * @brief Sets the icon for the title block
     * @param iconPath,Input parameters, icon, resource path
     */
    void SetTitleBarIcon(const QString& iconPath);
    /**
     * @brief Get content component object pointer
     * @return Return to QWidget*
     */
    QWidget* contentWidget();
    /**
     * @brief Set title block height
     * @param h,Input parameters, title block height
     */
    void setWindowTitleHeight(int h);
private:
    QWidget* m_contentWidget;//Content component
    TitleBar* m_titleBar;//Title Block
    QVBoxLayout* m_layout;//Layout manager
};

#endif // QWINDOWBASE_H

QWindowBase.cpp file:

#include "QWindowBase.h"


QWindowBase::QWindowBase(QFrame *parent): QFrame(parent)
{
    setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
    m_titleBar = new TitleBar(this);
    m_contentWidget = new QWidget(this);
    m_contentWidget->setObjectName("Contents");
    m_layout = new QVBoxLayout;
    m_layout->addWidget(m_titleBar);
    m_layout->addWidget(m_contentWidget);
    m_layout->setSpacing(0);
    m_layout->setContentsMargins(0, 0, 0, 0);

    setLayout(m_layout);
}

void QWindowBase::setWindowTitle(const QString &title)
{
    m_titleBar->setWindowTitle(title);
}

void QWindowBase::SetTitleBarIcon(const QString &iconPath)
{
    m_titleBar->SetTitleBarIcon(iconPath);
}

QWidget *QWindowBase::contentWidget()
{
    return m_contentWidget;
}

void QWindowBase::setWindowTitleHeight(int h)
{
    m_titleBar->setFixedHeight(h);
}

CommonHelper.h documents:

#ifndef COMMONHELPER_H
#define COMMONHELPER_H

#include <QString>
#include <QFile>
#include <QApplication>
#include <QDebug>
#include <QColor>
#include <QPalette>

/**
 * @brief General function auxiliary class
 */
class CommonHelper
{
public:
    /**
     * @brief Set up QSS style sheets for applications
     * @param filepath,Input parameters, QSS file path
     */
    static void setStyleSheet(const QString& filepath)
    {
        //Load style file
        QFile qss(filepath);
        if(qss.open(QFile::ReadOnly))
        {
            QString stylesheet = QLatin1String(qss.readAll());
            QString paletteColor = stylesheet.mid(20, 7);
            qApp->setPalette(QPalette(QColor(paletteColor)));
            qApp->setStyleSheet(stylesheet);
        }
    }
};

#endif // COMMONHELPER_H

main.cpp file:

#include <QApplication>
#include "CommonHelper.h"
#include "QWindowBase.h"
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QTreeView>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWindowBase w;
    w.setWindowTitle("WidgetBase");
    QPushButton* button1 = new QPushButton("OK");
    QHBoxLayout* hLayout1 = new QHBoxLayout;
    hLayout1->addStretch(1);
    hLayout1->addWidget(button1);

    QVBoxLayout* layout = new QVBoxLayout;
    QTreeView* treeView = new QTreeView;
    layout->addWidget(treeView);
    layout->addLayout(hLayout1);
    layout->addStretch(1);
    w.contentWidget()->setLayout(layout);
    w.setWindowTitleHeight(40);
    w.show();
    CommonHelper::setStyleSheet("://qss/lightblue.qss");
    return a.exec();
}

Engineering documents:

QT       += core gui


greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = TitleBarDemo
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0


SOURCES += \
    main.cpp \
    TitleBar.cpp \
    QWindowBase.cpp

HEADERS += \
    TitleBar.h \
    CommonHelper.h \
    QWindowBase.h

RESOURCES += \
    TitileBarDemo.qrc

Project directory structure:

3. User defined form base class result display

Operation results:

Topics: Qt