Qt Widget 自定义TitleBar带阴影窗口

自定义一个titlebar窗口,

不带任何资源、QSS,纯代码

1. 设置主窗口

透明背景,让central_widget透明方式显示,给后续main添加dropshadow效果,用于放置实际的业务控件。

setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
setAttribute(Qt::WA_TranslucentBackground);

    
QWidget *central_widget = new QWidget(this);
setCentralWidget(central_widget);
central_widget->setStyleSheet("background:transparent");

QVBoxLayout *central_layout = new QVBoxLayout(central_widget);
central_layout->setMargin(5);  // 这个 margin 一般为effect的 一半

QWidget *main = new QWidget(this);
central_layout->addWidget(main);

    QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect(this);
    effect->setBlurRadius(10);
    effect->setColor(QColor("#373737"));
    effect->setOffset(0,0);

main->setStyleSheet("background-color: black");
main->setGraphicsEffect(effect);

QVBoxLayout *vbox_main = new QVBoxLayout(main);
vbox_main->setMargin(0);
vbox_main->setSpacing(0);

...
// 添加后续业务代码。

2. 自定义右侧的按钮组

QWidget *titlebar = new QWidget(this);
titlebar->setStyleSheet("background-color: rgb(192,192,192)");
titlebar->setMaximumHeight(28);
vbox_main->addWidget(titlebar);

QHBoxLayout *titlebar_layout = new QHBoxLayout(titlebar);
titlebar_layout->setMargin(2);
titlebar_layout->setSpacing(2);

titlebar_layout->addStretch();


QPushButton* min_button = new QPushButton(this);
min_button->setIcon(style()->standardIcon(QStyle::SP_TitleBarMinButton));
titlebar_layout->addWidget(min_button);
connect(min_button, &QPushButton::pressed, this, [=]()
{
    showMinimized();
});

QPushButton* max_button = new QPushButton(this);
max_button->setIcon(style()->standardIcon(QStyle::SP_TitleBarMaxButton));
titlebar_layout->addWidget(max_button);
connect(max_button, &QPushButton::pressed, this, [=]()
{
    showFullScreen();
});

QPushButton* normal_button = new QPushButton(this);
normal_button->setIcon(style()->standardIcon(QStyle::SP_TitleBarNormalButton));
titlebar_layout->addWidget(normal_button);
connect(normal_button, &QPushButton::pressed, this, [=]()
{
    showNormal();
});
normal_button->setVisible(false);

QPushButton* close_button = new QPushButton(this);
close_button->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
titlebar_layout->addWidget(close_button);
connect(close_button, &QPushButton::pressed, this, [=]()
{
    close();
});

其中,最小化、全屏化、正常化、关闭按钮,可以取 Qt内置的标准icon,

style()->standardIcon(QStyle::SP_TitleBarMinButton);
SP_TitleBarMaxButton
SP_TitleBarNormalButton
SP_TitleBarCloseButton

可通过QPainter来变色

QIcon changeColor(const QIcon &icon, const QSize &size, const QColor &color)
{
    QPixmap pixmap = new icon.pixmap(size);
    QPainter painter(&pixmap);
    painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
    painter.fillRect(pixmap.rect(), color);
    return QIcon(pixmap);
}

通过获取topLevelWidget, 然后调用下列方法

    void showMinimized();
    void showMaximized();
    void showFullScreen();
    void showNormal();

    bool close();

3. 标题栏的拖动

void QFramelessShadowWindow::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton && event->x() < _max_button->x() - 30 && event->y() < 30)
    {
        _dragging = true;
        _mouse_position = event->globalPos();
        _window_position = geometry().topLeft();
    }
}

void QFramelessShadowWindow::mouseMoveEvent(QMouseEvent *event)
{
    if (_dragging && !_is_fullscreen)
    {
        QPoint offset = event->globalPos() - _mouse_position;
        move( _window_position + offset);
    }
}

void QFramelessShadowWindow::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
        _dragging = false;
}

4. 最后一个问题

当全屏后,会出现 放置实际的业务控件的区域显示不全,此时不需要显示dropshadow的阴影效果。可以响应主窗口的changeEvent事件,动态对layout的margin进行调整

void QFramelessShadowWindow::changeEvent(QEvent *event)
{
    bool from_normal = false;
    if (QEvent::WindowStateChange == event->type())
    {
        QWindowStateChangeEvent *state_event = static_cast<QWindowStateChangeEvent *>(event);
        if (Q_NULLPTR != state_event)
        {
            if (state_event->oldState() == Qt::WindowNoState)
            {
                from_normal = true;
            }
            else if (state_event->oldState() == Qt::WindowFullScreen)
            {
                _max_button->setVisible(true);
                _normal_button->setVisible(false);
                centralWidget()->layout()->setMargin(5);
                _is_fullscreen = false;
            }
        }
    }

    QMainWindow::changeEvent(event);
    Qt::WindowStates state = windowState();
    if (from_normal && state == Qt::WindowFullScreen)
    {
        _max_button->setVisible(false);
        _normal_button->setVisible(true);
        centralWidget()->layout()->setMargin(0);
        _is_fullscreen = true;
    }
}

您可以通过以下步骤自定义Qt窗口的标题栏: 1. 在Qt设计器中,将窗口的标题栏设置为无(TitleBar->NoTitle)。 2. 在窗口中添加一个QWidget作为自定义标题栏。 3. 在自定义标题栏中添加需要的控件,比如QPushButton、QLabel等。 4. 重写窗口的鼠标事件,实现拖动窗口的功能。为此,您需要在窗口中实现mousePressEvent、mouseMoveEvent和mouseReleaseEvent三个事件函数。 5. 在mousePressEvent中,记录鼠标点击位置,用于计算窗口的移动距离。 6. 在mouseMoveEvent中,计算鼠标移动距离,并根据距离移动窗口。 7. 在mouseReleaseEvent中,清除记录的鼠标点击位置。 8. 最后,将自定义标题栏添加到窗口中,调整位置和大小即可。 下面是一个示例代码,可以帮助您更好地理解如何实现自定义标题栏: ```cpp #include <QtGui> #include <QtWidgets> class CustomTitleBar : public QWidget { public: CustomTitleBar(QWidget* parent = nullptr) : QWidget(parent) { setFixedHeight(30); QLabel* titleLabel = new QLabel("Custom Title Bar"); QPushButton* minimizeButton = new QPushButton("-"); QPushButton* maximizeButton = new QPushButton("□"); QPushButton* closeButton = new QPushButton("X"); QHBoxLayout* layout = new QHBoxLayout(this); layout->addWidget(titleLabel); layout->addStretch(); layout->addWidget(minimizeButton); layout->addWidget(maximizeButton); layout->addWidget(closeButton); connect(minimizeButton, &QPushButton::clicked, parent, &QWidget::showMinimized); connect(maximizeButton, &QPushButton::clicked, parent, &QWidget::showMaximized); connect(closeButton, &QPushButton::clicked, parent, &QWidget::close); } }; class MainWindow : public QMainWindow { public: MainWindow(QWidget* parent = nullptr) : QMainWindow(parent) { setWindowTitle("Custom Title Bar"); CustomTitleBar* titleBar = new CustomTitleBar(this); setMenuWidget(titleBar); QVBoxLayout* layout = new QVBoxLayout; QLabel* label = new QLabel("Hello, World!"); layout->addWidget(label); QWidget* centralWidget = new QWidget; centralWidget->setLayout(layout); setCentralWidget(centralWidget); } protected: void mousePressEvent(QMouseEvent* event) override { if (event->button() == Qt::LeftButton) { m_dragPosition = event->globalPos() - frameGeometry().topLeft(); event->accept(); } } void mouseMoveEvent(QMouseEvent* event) override { if (event->buttons() & Qt::LeftButton) { move(event->globalPos() - m_dragPosition); event->accept(); } } void mouseReleaseEvent(QMouseEvent* event) override { if (event->button() == Qt::LeftButton) { m_dragPosition = QPoint(); event->accept(); } } private: QPoint m_dragPosition; }; int main(int argc, char* argv[]) { QApplication app(argc, argv); MainWindow window; window.show(); return app.exec(); } ``` 在这个示例中,我们首先实现了一个CustomTitleBar类,该类继承自QWidget,用于创建自定义的标题栏。在CustomTitleBar中,我们添加了一个QLabel和三个QPushButton,分别用于显示标题、最小化窗口、最大化/还原窗口和关闭窗口。我们通过QHBoxLayout将这些控件添加到CustomTitleBar中,并使用connect函数将这些按钮的点击事件连接到MainWindow的相应函数。 在MainWindow中,我们创建了一个CustomTitleBar对象,并使用setMenuWidget函数将其设置为菜单栏。然后,我们创建了一个QVBoxLayout,并向其中添加一个QLabel。最后,我们将QVBoxLayout设置为中心部件,并通过setCentralWidget函数将其添加到MainWindow中。 对于拖动窗口的实现,我们重写了MainWindow的mousePressEvent、mouseMoveEvent和mouseReleaseEvent三个函数。在mousePressEvent中,我们记录了鼠标点击位置,用于计算窗口的移动距离。在mouseMoveEvent中,我们计算了鼠标移动距离,并根据距离移动窗口。在mouseReleaseEvent中,我们清除了记录的鼠标点击位置。 最后,我们创建了一个QApplication对象,并显示了MainWindow。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值