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

news2024/12/22 18:45:28

需求:

顶部导航栏有若干选项,可能很多,顶部区域不能完全展示,比如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");

}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件工程进度管理

答案&#xff1a;A D 解析&#xff1a; 由选项可以看出&#xff0c;有B,E,C,K&#xff0c;这里选择经过它们路径最长的就是正确答案 选项B 路线 ABIJL362819 路线 ABDIJL3522820 选项E 路线 AEGJL432817 路线 AEGHKL4334317 选项C 路线 ACFHKL5314316 选项D 路线 A…

【GBase 8c V5_3.0.0 分布式数据库常用维护命令】

一、查看数据库状态/检查&#xff08;gbase用户&#xff09; 1.gha_ctl monitor 使用gha_ctl monitor查看节点运行情况(跟dcs的地址和端口) gha_ctl monitor -c gbase -l http://172.20.10.8:2379 -Hall |coordinator | datanode | gtm | server|dcs:必选字段。指定查看哪类集…

Prometheus优化指南:如何提升系统性能

Prometheus 是一个强大的开源监控系统&#xff0c;它被广泛应用于云原生环境中&#xff0c;特别是在 Kubernetes 和其他容器化基础设施中。然而&#xff0c;随着监控数据量的增长&#xff0c;系统本身的性能可能会成为瓶颈。如果不进行优化&#xff0c;最终将影响到整体系统的可…

浏览器查消息

window.addEventListener(message,function(event){console.log(Received message,event.data)}); 并把弹窗口对准要接收消息的ifrme 发消息的窗口

大模型入门3:理解LLAMA

Model a stack of DecoderBlocks(SelfAttention, FeedForward, and RMSNorm) decoder block 整体结构&#xff1a;最大的区别在pre-norm x -> norm(x) -> attention() -> residual connect -> norm() -> ffn -> residual connect class DecoderBlock(nn.…

从零到一:构建你的第一个AI项目(实战教程)

引言 欢迎来到AI世界的初学者指南&#xff01;在这个实战教程中&#xff0c;我们将一步步构建一个基础的AI项目&#xff0c;让你从零开始&#xff0c;亲手体验人工智能的魅力。我们的目标是让即使没有任何编程或AI背景的你&#xff0c;也能通过本教程完成一个小型的AI应用。今天…

《程序猿之设计模式实战 · 装饰者模式》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

Python 求亲和数

亲和数&#xff08;Amicable Numbers&#xff09;是指两个不同的正整数&#xff0c;它们的真因数&#xff08;即除去本身的所有因数&#xff09;之和与对方的数相等。 def sum_of_proper_divisors(n):"""计算一个数的真因子之和"""divisors_su…

SpringBoot闲一品交易平台

SpringBoot闲一品交易平台 #vue项目实战 #计算机项目 #java项目 SpringBoot闲一品交易平台通过运用软件工程原理和开发方法&#xff0c;借助Spring Boot框架&#xff0c;旨在实现零食交易信息的高效管理&#xff0c;提升用户的购物体验和满意度。 技术栈 开发语言&#xff1a;…

用于安全研究的 Elastic Container Project

作者&#xff1a;来自 Elastic Andrew Pease•Colson Wilhoit•Derek Ditch 使用 Docker 启动 Elastic Stack 序言 Elastic Stack 是一个模块化数据分析生态系统。虽然这允许工程灵活性&#xff0c;但建立开发实例进行测试可能很麻烦。建立 Elastic Stack 的最简单方法是使用…

Day09-StatefuleSet控制器

Day09-StatefuleSet控制器 0、昨日内容回顾1、StatefulSets控制器1.1 StatefulSet概述1.2 StatefulSets控制器-网络唯一标识之headless1.3 StatefulSets控制器-独享存储 2、metric-server2.1 metric-server概述2.2 部署metric-server:2.3 hpa案例 3、helm概述3.1 安装helm3.2 h…

RabbitMQ 高级特性——持久化

文章目录 前言持久化交换机持久化队列持久化消息持久化 前言 前面我们学习了 RabbitMQ 的高级特性——消息确认&#xff0c;消息确认可以保证消息传输过程的稳定性&#xff0c;但是在保证了消息传输过程的稳定性之后&#xff0c;还存在着其他的问题&#xff0c;我们都知道消息…

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

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

Spring Boot集成Akka Stream快速入门Demo

1.什么是Akka Stream&#xff1f; Akka Streams是一个用于处理和传输元素序列的库。它建立在Akka Actors之上&#xff0c;使流的摄入和处理变得简单。由于它是建立在Akka Actors之上的&#xff0c;它为Akka现有的actor模型提供了一个更高层次的抽象。Akka流由3个主要部分组成-…

从0开始学习RocketMQ:快速部署启动

快速部署 快速部署一个单节点单副本 RocketMQ 服务&#xff0c;并完成简单的消息收发。 安装Apache RocketMQ 下载地址&#xff1a;RocketMQ官网下载 这里我们下载二进制包&#xff1a;rocketmq-all-5.3.0-bin-release.zip 直接解压即可&#xff1a;tar -zxvf rocketmq-all…

光伏开发:工商业光伏的流程管理全面解析

一、项目准备阶段 1、资源寻觅与沟通 首要任务是寻找适合的工商业屋顶或空地资源&#xff0c;并与业主初步交流&#xff0c;了解其意向、屋顶条件及用电情况。这一阶段的关键在于建立信任关系&#xff0c;为后续工作奠定基础。 2、资料收集与核查 全面收集业主资料&#xff…

算法练习题26——多项式输出(模拟)

输入格式 输入共有 2 行 第一行 1 个整数&#xff0c;n&#xff0c;表示一元多项式的次数。 第二行有 n1 个整数&#xff0c;其中第 i 个整数表示第 n−i1 次项的系数&#xff0c;每两个整数之间用空格隔开。 输出格式 输出共 1 行&#xff0c;按题目所述格式输出多项式。…

Navicat BI 中创建自定义字段:计算字段

在数据库设计和开发中&#xff0c;避免存储任何可以从其他字段计算或重建的数据是一种惯例。因此&#xff0c;在 Navicat BI 中构建图表时&#xff0c;你可能会缺少一些数据。但这不是问题&#xff0c;因为 Navicat BI 提供了专门用于此目的的计算字段。在今天的博客中&#xf…

网站按钮检测系统源码分享

网站按钮检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

浅谈MVC设计模式

1 前言 1.1 内容概要 熟悉使用JSON工具&#xff0c;完成Java对象&#xff08;Map&#xff09;和Json字符串之间的相互转换&#xff08;注意提供构造器和getter/setter方法&#xff09; 注意事项&#xff1a;不管使用的是什么JSON工具&#xff0c;都要提供类的无参构造方法和…