【Qt】实现顶部导航栏自适应滑动效果

需求:

顶部导航栏有若干选项,可能很多,顶部区域不能完全展示,比如10个选项,界面一次只能展示五个,那么要求把后面的选项隐藏起来,并且,当点击第四个第五个按钮的时候,自动滑动到中间位置,后面的也滑动出来。
看下效果图
在这里插入图片描述

分析

这里面有几个点:

  • 顶部导航栏的按钮会很多,要求能够正常隐藏那些显示不了的按钮
  • 顶部导航栏的按钮宽度要自适应,也就是根据按钮中的文字宽度来调整按钮宽度
  • 点击按钮实现滑动效果,注意,靠两边的按钮不滑动(比如第一个、第二个本身就应该靠边,无法滑动)
  • 滑动效果要流畅

实现方式

#pragma once

#include <QtWidgets/QWidget>
#include "ui_topnavbar.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <QScrollArea>
#include <QScrollBar>
#include <QLabel>
#include <QScroller>
#include <QDebug>
#include <QList>
#include <QFontMetrics>
#include <QFont>
#include <QFile>
#include <QPropertyAnimation>

class TopNavBar : public QWidget
{
    Q_OBJECT

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

private:
    void scrollToButton(QPushButton* button);// 导航栏滑动
    void slotTopButtonSmoothScroll(QPushButton* button); // 导航栏平滑滑动
    void slotButtonClicked(bool clicked);
    void createText(QList<QString>& buttonText);
    void slotTestButtonClicked(bool clicked);
    void loadStyleSheet();

    QList<QString> buttonText;
    QScrollArea* _scrollArea;
    QList<QPushButton*> _topButtons;
    // QPushButton* testButton = nullptr;
    QLabel* iconLabel;
    QLabel* textLabel;
};

#include "topnavbar.h"
#include <QStyle>

TopNavBar::TopNavBar(QWidget *parent)
    : QWidget(parent)
{
    this->setMinimumHeight(500);
    this->setMinimumWidth(800);
    loadStyleSheet();
    createText(buttonText);
    // 创建一个滚动区域
    _scrollArea = new QScrollArea(this);
    _scrollArea->setWidgetResizable(true);// 内部控件自动填充滚动区域
    _scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);// 关闭垂直滚动条显示
    _scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);// 关闭水平滚动条显示
    _scrollArea->setFixedHeight(40);// 固定高度
    _scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);// 扩展策略
    QHBoxLayout* layoutButton = new QHBoxLayout;
    layoutButton->setSpacing(0);
    layoutButton->setContentsMargins(0, 0, 0, 0);

    // 创建一个容器用于放置按钮
    QWidget* buttonContainer = new QWidget(_scrollArea);
    buttonContainer->setLayout(layoutButton);
    // 循环创建按钮并添加到布局中
    for (int i = 0; i < buttonText.size(); ++i) {
        QPushButton* pbutton = new QPushButton(buttonText[i], buttonContainer);
        
        // 通过样式文件 控制button中文字与边界的边距
        pbutton->setStyleSheet("background:rgb(225,225,225);margin: 0px;padding: 10px 24px;");
        
        
        // 宽度自适应  按钮宽度最小为128 要求内部文字上下边距为10px  左右边距为24px
        // 如果内部文字过长 则根据内部文字宽度 自适应调整按钮宽度

        // 获取按钮的字体
        QFont font = pbutton->font();
        // 创建 QFontMetrics 对象
        QFontMetrics metrics(font);

        // 测量文本的宽度
        int textWidth = metrics.horizontalAdvance(pbutton->text());

        // 打印文本宽度
        qDebug() << "Text width in pixels:" << textWidth;

        int buttonWidth = textWidth + 48 > 128 ? textWidth + 48 : 128;
        pbutton->setMinimumWidth(buttonWidth);
        QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
        sizePolicy.setHorizontalStretch(0);
        sizePolicy.setVerticalStretch(0);
        pbutton->setSizePolicy(sizePolicy);
        // 高度扩展
        pbutton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
        // 添加布局
        layoutButton->addWidget(pbutton);
        // 加入按钮列表
        _topButtons.push_back(pbutton);
        // 连接信号槽,当按钮被点击时,滚动到该按钮的位置
        connect(pbutton, &QPushButton::clicked, [this, pbutton]() {
            slotTopButtonSmoothScroll(pbutton);
            });
        // 当按钮被点击后 切换其样式 并设置其他未被选择的按钮的样式
        connect(pbutton, &QPushButton::clicked, this, &TopNavBar::slotButtonClicked);
    }
    // 将容器设置为滚动区域的子窗口
    _scrollArea->setWidget(buttonContainer);
    QLabel* testLabel = new QLabel;
    testLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    // 垂直布局
    QVBoxLayout* layout = new QVBoxLayout;
    layout->addWidget(_scrollArea);
    layout->addWidget(testLabel);
    
    this->setLayout(layout);
}
TopNavBar::~TopNavBar()
{}
// 没加动画  直接移动
void TopNavBar::scrollToButton(QPushButton* button) {
    // 计算滚动条应该滚动到的位置
    int scrollPosition = button->pos().x() - (_scrollArea->width() - button->width()) / 2;

    // qDebug() << "scrollPosition:" << scrollPosition;
    if (scrollPosition < 0)
    {
        scrollPosition = 0;
    }
    
    QScrollBar* scrollBar = _scrollArea->horizontalScrollBar();
    scrollBar->setValue(scrollPosition);
}
// 加了动画 平滑滑动
void TopNavBar::slotTopButtonSmoothScroll(QPushButton* button) {
    int scrollPosition = button->pos().x() - (_scrollArea->width() - button->width()) / 2;
    QScrollBar* scrollBarH = _scrollArea->horizontalScrollBar();
    
    qDebug() << "----------------------------------" ;
    qDebug() << "button->pos().x():" << button->pos().x();
    qDebug() << "scrollArea->width():" << _scrollArea->width();
    qDebug() << "button->width():" << button->width();
    qDebug() << "scrollBarH->minimum():" << scrollBarH->minimum();
    qDebug() << "scrollBarH->maximum():" << scrollBarH->maximum();
    
    scrollPosition = qBound(scrollBarH->minimum(), scrollPosition, scrollBarH->maximum());
    
    QPropertyAnimation* smoothAnimation = new QPropertyAnimation(scrollBarH, "value");
    smoothAnimation->setDuration(300); // 设置动画持续时间
    smoothAnimation->setStartValue(scrollBarH->value());
    smoothAnimation->setEndValue(scrollPosition);
    smoothAnimation->start();// 启动动画
}
void TopNavBar::slotButtonClicked(bool clicked)
{
    QPushButton* pButton = dynamic_cast<QPushButton*>(sender());
    if (!pButton)
    {
        return;
    }
    for (auto button : _topButtons)
    {
        if (button == pButton)
        {
            button->setStyleSheet("background:gray;margin: 0px;padding: 10px 24px;");
        }
        else
        {
            button->setStyleSheet("background:rgb(225,225,225);margin: 0px;padding: 10px 24px;");
        }
        
    }
}
void TopNavBar::createText(QList<QString>& buttonText)
{
    buttonText.push_back("Factory Reset Factory Reset");
    buttonText.push_back("Update");
    buttonText.push_back("Projects");
    buttonText.push_back("L");
    buttonText.push_back("AC Back");
    buttonText.push_back("Fan Mode");

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/875517.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio 加载多个FLutter项目

按顺序操作即可 选择工程 选择Modules, 导入 module 选中创建module 选择要导入的目录&#xff0c;只选择主目录&#xff0c;下面的文件不要选 添加完成&#xff0c;点击ok后&#xff0c;会进行导入 最终导入成功

【rpg像素角色】俯视角-行走动画

制作像素角色的俯视角行走动画并不像看上去那么复杂&#xff0c;尤其是在你已经完成了角色的4个方向站立姿势之后&#xff08;其中左右方向可以通过水平翻转实现&#xff09;。接下来&#xff0c;我会一步步为你讲解如何制作行走动画。 1. 理解行走规律 在制作行走动画之前&am…

堆排序

一&#xff1a;思想 堆排序(Heapsort)是指利用 堆 这种数据结构所设计的一种排序算法&#xff0c;它是选择排序的一种。它是通过堆来进行选择数据。 动图&#xff1a; 二&#xff1a;实现思路 假设&#xff1a;现在对一个7个整形的数组进行升序堆排&#xff08;2 1 5 7 4 3 …

基于 CycleGAN 对抗网络的自定义数据集训练

目录 生成对抗网络&#xff08;GAN&#xff09; CycleGAN模型训练 训练数据生成 下载开源项目CycleGAN 配置训练环境 开始训练 模型测试 可视化结果 生成对抗网络&#xff08;GAN&#xff09; 首先介绍一下什么是GAN网络&#xff0c;它是由生成器&#xff08;Generator…

【CTF Web】BUUCTF Upload-Labs-Linux Pass-13 Writeup(文件上传+PHP+文件包含漏洞+PNG图片马)

Upload-Labs-Linux 1 点击部署靶机。 简介 upload-labs是一个使用php语言编写的&#xff0c;专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共20关&#xff0c;每一关都包含着不同上传方式。 注意 1.每一关没有固定的…

Modbus协议02:存储区简介

视频链接&#xff1a;【2】Modbus协议存储区说明_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV11G4y1W7pU?p2&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.为什么需要存储区、存储区类型及代号 2.Modbus存储区范围及地址模型

SLM561A​​系列 60V 10mA到50mA线性恒流LED驱动芯片 为智能家居照明注入新活力

SLM561A系列选型参考&#xff1a; SLM561A10ae-7G SOD123 SLM561A15ae-7G SOD123 SLM561A20ae-7G SOD123 SLM561A25ae-7G SOD123 SLM561A30ae-7G SOD123 SLM561A35ae-7G SOD123 SLM561A40ae-7G SOD123 SLM561A45ae-7G SOD123 SLM561A50ae-7G SOD123 …

在Webmin上默认状态无法正常显示 Mariadb V11.02及以上版本

OS: Armbian OS 24.5.0 Bookworm Mariadb V11.02及以上版本 Webmin&#xff1a;V2.202 非常小众的问题&#xff0c;主要是记录一下。 如题 Webmin 默认无法正常显示 Mariadb V11.02及以上版本 如果对 /etc/webmin/mysql/config 文件作相应调整就可以再现Mariadb管理界面。 路径…

AI prompt(提示词)

# 好用的用于学习的AI提示词 ## 费曼学习法 请使用费曼学习法&#xff0c;用简单的语言解释&#xff08;量子力学&#xff09;是什么&#xff0c;并提供一个简单的例子来说明它如何应用 ## 帕累托法则&#xff08;80/20原则&#xff09; 将&#xff08;量子力学&#xff09;最…

09_Tensorflow2图像处理大赏:让你的图片笑出AI感,惊艳朋友圈!

1. 图像处理案例 1.1 逆时针旋转90度 import tensorflow as tf import matplotlib.pyplot as plt import matplotlib.cm as cm import numpy import osdef show_pic(pic,name,cmapNone):显示图像plt.imshow(pic,cmapcmap) plt.axis(off) # 打开坐标轴为 on # 设置图像标题…

【C++】认识C++(前言)

&#x1f984;个人主页:小米里的大麦-CSDN博客 &#x1f38f;所属专栏:C_小米里的大麦的博客-CSDN博客 &#x1f381;代码托管:C: 探索C编程精髓&#xff0c;打造高效代码仓库 (gitee.com) ⚙️操作环境:Visual Studio 2022 目录 一、本节概述 二、什么是C 三、C发展史 四…

苏茵茵:以时尚之名,诠释品质生活

在女性追求个性化与自我表达的今天&#xff0c;时尚早已超越了简单的穿着打扮&#xff0c;它成为女性展现自我风格、彰显独特魅力的重要方式。从广泛的兴趣爱好到精心雕琢的个人风格&#xff0c;每一处细节都闪耀着女性对个性独特与自我表达的深切渴望。正是这股不可阻挡的潮流…

Unity6 + UE5.4 PSO缓存实践记录

题图&#xff08;取自COD冷战的着色器编译提示&#xff09; PSO&#xff08;管线状态对象 Pipeline State Object&#xff09;是伴随现代图形API&#xff08;DirectX12、Vulkan、Metal&#xff09;而出现的概念&#xff0c;它本质上是单次绘制时渲染管线所处的状态信息的集合&…

Blender渲染太慢怎么办?blender云渲染已开启

动画行业蓬勃发展&#xff0c;动画制作软件亦持续推陈出新&#xff0c;当制作平台日益丰富&#xff0c;创作难度降低&#xff0c;创作效率提升&#xff0c;如何高效完成复杂动画的渲染就成了从业者更关心的问题。 云渲染技术的出现&#xff0c;无疑为动画制作者提供了前所未有…

kafka原理剖析及实战演练

一、消息系统概述 一&#xff09;消息系统按消息发送模型分类 1、peer-to-peer&#xff08;单播&#xff09; 特点&#xff1a; 一般基于pull或polling接收消息发送对队列中的消息被一个而且仅仅一个接收者所接收&#xff0c;即使有多个接收者在同一队列中侦听同一消息即支持异…

利用熵权法进行数值评分计算——算法过程

1、概述 在软件系统中&#xff0c;研发人员常常遇上需要对系统内的某种行为/模型进行评分的情况。例如根据系统的各种漏洞情况对系统安全性进行评分、根据业务员最近操作系统的情况对业务员工作状态进行打分等等。显然研发人员了解一种或者几种标准评分算法是非常有利于开展研…

中控室控制台处在自动状态什么意思

在现代工业和智能控制系统中&#xff0c;中控室控制台作为集中控制和管理各种设备、系统和流程的核心&#xff0c;扮演着至关重要的角色。当提到中控室控制台处在自动状态时&#xff0c;这通常意味着控制台已经切换到一种高度智能化的工作模式&#xff0c;能够自动调整和管理各…

【SQL】百题计划:SQL判断条件OR的使用。

【SQL】百题计划-20240912 Select name, population, area from World where area>3000000 or population > 25000000;

品读 Java 经典巨著《Effective Java》90条编程法则,第4条:通过私有构造器强化不可实例化的能力

文章目录 【前言】欢迎订阅【品读《Effective Java》】系列专栏java.lang.Math 类的设计经验总结 【前言】欢迎订阅【品读《Effective Java》】系列专栏 《Effective Java》是 Java 开发领域的经典著作&#xff0c;作者 Joshua Bloch 以丰富的经验和深入的知识&#xff0c;全面…

网络运输层之(1)TCP协议基础

网络运输层之(1)TCP协议基础 Author: Once Day Date: 2024年9月12日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 通信网络技术_Once-Day的博客-…