草庐IT

Qt 自定义标题栏,最小化、最大化、关闭窗口,双击最大化,鼠标拖动等效果实现

wang_chao118 2023-11-06 原文

文章目录


前言

本次实验内容为Qt自定义标题栏,最小化、最大化、关闭窗口,双击最大化,鼠标拖动等界面软件的基本常规操作。

我们在做一个软件界面的时候,有时需要其任务栏显示一些文本、图片甚至一些自定义控件的内容,但是通过Qt自动创建的窗口类仅自带系统风格的标题栏,往往并不能满足我们的自定义设计需求。

在这种情况下,需要用Qt设计自定义标题栏内的相关内容。本篇博客记录本人实现这一功能的前后代码过程,并不一定是标准方式,仅作一个记录,在后面项目用的时候进行查阅。


效果

主要实现的窗口操作为以下5点内容:
(1)在“灰色”区域,即自定义标题栏区域内,最左侧为logo图标,往右为软件名称标题,最右边分别外三个按钮,依次为“最小化”、“最大化”、“关闭”按钮;
(2)标题栏区域(包含logo以及名称标题上)拖动鼠标,实现整个窗口的拖动功能,蓝色区域拖拽鼠标,不会拖动整个窗口;
(3)标题栏区域(包含logo以及名称标题上)双击鼠标左键,实现最大化、还原窗口的操作;
(4)鼠标移动至“最小化”、“最大化”、“关闭”按钮上会改变按钮的背景色;
(5)鼠标单击最小化、最大化、关闭按钮,嫩实现相应的窗口操作功能。
下图为自定义标题栏的软件界面效果。

代码

建立Qt工程文件,自动生成widget.h,widget.cpp以及widget.ui,再新建一个Qt 的Ui界面类,即title类,生成title.h,title.cpp,title.ui三个文件

widget类为整体窗体类,用于最底层展示窗口,title类为自定义标题栏类。

整个工程文件夹下内容如下图所示:

.pro文件

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11
SOURCES += \
    main.cpp \
    title.cpp \
    widget.cpp

HEADERS += \
    title.h \
    widget.h

FORMS += \
    title.ui \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

RESOURCES += \
    src.qrc

widget.h

在widget.h文件中将title类的头文件title.h包含进去,并声明一个title类指针。

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "title.h"

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
    title *t1;

};
#endif // WIDGET_H

widget.cpp

在Widget构造函数中,首先建立ui界面,设置窗口的无边框属性,实例化一个title对象,并将该对象放置到窗体的对象树上。

此句为设置无自带标题栏的属性,必须有这句话。
setWindowFlags(Qt::FramelessWindowHint);

新建一个垂直布局
QVBoxLayout* layout = new QVBoxLayout(this);

将自定义标题栏放置入垂直布局中去
layout->addWidget(t1);

#include "widget.h"
#include "ui_widget.h"
#include <QVBoxLayout>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    setWindowFlags(Qt::FramelessWindowHint);    //隐藏标题栏(无边框)
    t1 = new title(this);
    t1->setParent(this);
	//新建一个垂直布局,并将title对象、ui->stackedWidget放置入该垂直布局中去
    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->addWidget(t1);
    layout->addWidget(ui->stackedWidget);
    //设置垂直布局间隙
    layout->setSpacing(0);
    layout->setMargin(0);
}

Widget::~Widget()
{
    delete ui;
}

widget.ui

ui界面中,在Widget中放置一个QStackedWidget用于后续工作,不需要在此处进行布局。

title.h

在widget.h文件中将title类的头文件title.h包含进去,并声明一个title类指针。

#ifndef TITLE_H
#define TITLE_H

#include <QWidget>
#include <QMouseEvent>

namespace Ui {
class title;
}

class title : public QWidget
{
    Q_OBJECT

public:
    explicit title(QWidget *parent = nullptr);
    ~title();

    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    void mouseDoubleClickEvent(QMouseEvent *event);

private slots:

    void onClicked();

private:
    Ui::title *ui;
    QPoint m_start;  //起始点
    QPoint m_end;    //结束点
    bool m_leftButtonPressed;   //鼠标左键按下标记
};

#endif // TITLE_H

title.cpp

在title构造函数中,首先建立ui界面,接着连接鼠标点击title中的三个QPushButton时产生的信号及对应操作的槽函数。
此处将三个按钮的点击信号连接至同一个槽函数,在槽函数中判断点击信号的发送对象,然后作出相应操作。

重写mousePressEvent、mouseMoveEvent、mouseMoveEvent、mouseDoubleClickEvent四个鼠标事件

#include "title.h"
#include "ui_title.h"
#include <QDebug>

title::title(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::title)
{
    ui->setupUi(this);
    connect(ui->pushButton,SIGNAL(clicked(bool)),this, SLOT(onClicked()));
    connect(ui->pushButton_2,SIGNAL(clicked(bool)),this, SLOT(onClicked()));
    connect(ui->pushButton_3,SIGNAL(clicked(bool)),this, SLOT(onClicked()));
}

title::~title()
{
    delete ui;
}

void title::mousePressEvent(QMouseEvent *event)
{
    //鼠标左键按下事件
    if(event->button()==Qt::LeftButton)
    {
        //记录鼠标左键状态
        m_leftButtonPressed = true;
        //记录鼠标在屏幕中的位置
        m_start = event->globalPos();
//        qDebug()<<QString::fromLocal8Bit("左键被按下了");
    }
}

void title::mouseMoveEvent(QMouseEvent *event)
{
    if(m_leftButtonPressed)
    {
        //将父窗体移动到父窗体原来的位置加上鼠标移动的位置:event->globalPos()-m_start
        //this->window()获取
        this->window()->move(this->window()->geometry().topLeft()+event->globalPos()-m_start);
        //将鼠标在屏幕中的位置替换为新的位置
        m_start = event->globalPos();
    }
}

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

void title::mouseDoubleClickEvent(QMouseEvent *event)
{
    Q_UNUSED(event);
    ui->pushButton_3->click();
}

void title::onClicked()
{
    QPushButton *pButton = qobject_cast<QPushButton *>(sender());
    QWidget *pWindow = this->window();

    if (pWindow->isTopLevel())
    {
        if(pButton == ui->pushButton)
        {
            pWindow->showMinimized();
        }
        else if(pButton == ui->pushButton_2)
        {
            pWindow->close();
        }
        else if(pButton == ui->pushButton_3)
        {
            pWindow->isMaximized()?pWindow->showNormal():pWindow->showMaximized();
        }
    }
}


title.ui

title.ui界面中,在title中放置两个QLabel三个QPushButton,两个QLabel分别用于展示logo及软件标题名称,三个QPushButton为最小化、最大化以及关闭操作。

在右侧的对象树上,顶层对象上右键改变样式表,添加如下的样式表,直接设置title类中各个子对象的样式。

注意:此处设置标题名称应通过右键设置样式表的方式设置其相关属性,不能通过右键“改变多文本信息”来设置其属性,否则标题栏鼠标拖动的效果将会失效

QWidget#widget
{
	background-color: rgb(98, 98, 98);
}
QLabel#label_2
{
	font: 16pt bold "楷体";
	font-weight:900;
	color:white;
}
QPushButton 
{
	border-style: solid;
	border-width: 0px;
	border-radius: 0px;
	background-color: rgba(223, 223, 223, 0);
}
QPushButton::focus{outline: none;}
QPushButton::hover 
{
	border-style: solid;
	border-width: 0px;
	border-radius: 0px;
	background-color: rgba(223, 223, 223, 150);
}

直接在QtCreator右下方设置QPushButton类相关属性,这里的pushButton_3比较特殊,它具有两个状态,一是“还原”状态,二是“最大化”状态,在QAbstractButton里有icon的属性,Normal Off状态下选择“最大化.png”,Active On状态下选择“还原.png”,并且需要勾选“checkabel”复选框

当然,这里的相关设置也可以通过代码实现,我个人还是比较习惯于直接在Ui界面内设置了,比较方便。

有关Qt 自定义标题栏,最小化、最大化、关闭窗口,双击最大化,鼠标拖动等效果实现的更多相关文章

  1. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  2. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  3. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

  4. ruby-on-rails - 如何在 Rails 3 中创建自定义脚手架生成器? - 2

    有这些railscast。http://railscasts.com/episodes/218-making-generators-in-rails-3有了这个,你就会知道如何创建样式表和脚手架生成器。http://railscasts.com/episodes/216-generators-in-rails-3通过这个,您可以了解如何添加一些文件来修改脚手架View。我想把两者结合起来。我想创建一个生成器,它也可以创建脚手架View。有点像RyanBates漂亮的生成器或web_app_themegem(https://github.com/pilu/web-app-theme)。我

  5. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  6. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  7. ruby-on-rails - Rails - 使用/自定义 URL : '/dashboard' 指定根路径 - 2

    如何使此根路径转到:“/dashboard”而不仅仅是http://example.com?root:to=>'dashboard#index',:constraints=>lambda{|req|!req.session[:user_id].blank?} 最佳答案 您可以通过以下方式实现:root:to=>redirect('/dashboard')match'/dashboard',:to=>"dashboard#index",:constraints=>lambda{|req|!req.session[:user_id].b

  8. Qt Designer的简单使用 - 2

    在前面两节的例子中,主界面窗口的尺寸和标签控件显示的矩形区域等,都是用C++代码编写的。窗口和控件的尺寸都是预估的,控件如果多起来,那就不好估计每个控件合适的位置和大小了。用C++代码编写图形界面的问题就是不直观,因此Qt项目开发了专门的可视化图形界面编辑器——QtDesigner(Qt设计师)。通过QtDesigner就可以很方便地创建图形界面文件*.ui,然后将ui文件应用到源代码里面,做到“所见即所得”,大大方便了图形界面的设计。本节就演示一下QtDesigner的简单使用,学习拖拽控件和设置控件属性,并将ui文件应用到Qt程序代码里。使用QtDesigner设计界面在开始菜单中找到「Q

  9. ruby-on-rails - 在 heroku 的 .fonts 文件夹中包含自定义字体,似乎无法识别它们 - 2

    Heroku支持人员告诉我,为了在我的Web应用程序中使用自定义字体(未安装在系统中,您可以在bash控制台中使用fc-list查看已安装的字体)我必须部署一个包含所有字体的.fonts文件夹里面的字体。问题是我不知道该怎么做。我的意思是,我不知道文件名是否必须遵循heroku的任何特殊模式,或者我必须在我的代码中做一些事情来考虑这种字体,或者如果我将它包含在文件夹中它是自动的......事实是,我尝试以不同的方式更改字体的文件名,但根本没有使用该字体。为了提供更多详细信息,我们使用字体的过程是将PDF转换为图像,更具体地说,使用rghostgem。并且最终图像根本不使用自定义字体。在

  10. ruby - 获取数组中的值并最小化某个类属性的最优雅的方法是什么? - 2

    假设我有以下类(class):classPersondefinitialize(name,age)@name=name@age=ageenddefget_agereturn@ageendend我有一组Person对象。是否有一种简洁的、类似于Ruby的方法来获取最小(或最大)年龄的人?如何根据它对它们进行排序? 最佳答案 这样做会:people_array.min_by(&:get_age)people_array.max_by(&:get_age)people_array.sort_by(&:get_age)

随机推荐