Qt实战 数据统计柱状图显示

news2024/11/15 15:57:45

前段时间有朋友找我做个问卷调查的软件,我说现在很多在线文档都有这功能,为啥还要使用Qt撸一个,他说要申请软著,我说,欧了。

我们先看看WPS在线问卷的统计中,柱状图统计的效果吧

我认为主要有以下几个关键点吧:

  1. 要做到根据窗口的宽度,大小自适应,避免硬编码
  2. 显示信息可配置
  3. 纵轴坐标文本需要计算宽度,过长时以省略号截尾
  4. 鼠标移动到柱状条上时,能够显示相应的文本,同时,背景高亮

废话不多说,直接上代码了,ui文件就不放了,界面都是通过painter绘制的,没有任何控件。

BarGraphStatistic.h

#ifndef BARGRAPHSTATISTIC_H
#define BARGRAPHSTATISTIC_H

#include <QWidget>
#include <QVector>
#include <QPair>

namespace Ui {
class BarGraphStatistic;
}

class BarGraphStatistic : public QWidget
{
    Q_OBJECT

public:
    explicit BarGraphStatistic(const QVector<QPair<QString, int>>& choices, QWidget *parent = nullptr);
    ~BarGraphStatistic();

protected:
    void paintEvent(QPaintEvent* e);
    void mouseMoveEvent(QMouseEvent* e);
    void resizeEvent(QResizeEvent* e);

private:
    void UpdateBarRect();

private:
    Ui::BarGraphStatistic *ui;
    QVector<QPair<QString, int>> m_choices;
    QString m_longestStr{""};
    int m_maxCnt{0};


    int m_fontSize{12};
    int m_padding{20};
    int m_itemHeight{45};
    int m_maxTxtWidth{200};
    int m_txtWidth;
    int m_txtHeight;
    int m_spaceHorizon{2};

    int m_axisXHeight{20};

    int m_winHeight;
    int m_barStartX;

    QVector<QRect> m_barRects;
    int m_curBar = -1;
    QPoint m_mousePos;
};


#endif // BARGRAPHSTATISTIC_H

BarGraphStatistic.cpp

#include "BarGraphStatistic.h"
#include "ui_BarGraphStatistic.h"
#include <QPainter>
#include <QMouseEvent>
#include <QToolTip>
#include <QTextOption>

BarGraphStatistic::BarGraphStatistic(const QVector<QPair<QString, int>>& choices, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::BarGraphStatistic),
    m_choices(choices)
{
    ui->setupUi(this);
    setMouseTracking(true);

    for (int i = 0; i < m_choices.size(); ++i)
    {
        if (m_choices[i].first.size() > m_longestStr.size())
            m_longestStr = m_choices[i].first;

        if (m_choices[i].second > m_maxCnt)
            m_maxCnt = m_choices[i].second;
    }

    QFont f;
    f.setPixelSize(m_fontSize);
    QFontMetrics fm(f);
    auto tr = fm.boundingRect(m_longestStr);
    m_txtWidth = qMin(tr.width(), 100);
    m_txtHeight = tr.height();

    m_winHeight = m_padding * 2 + m_itemHeight * m_choices.size() + m_axisXHeight;
    m_barStartX = m_padding + m_txtWidth;
    setFixedHeight(m_winHeight);


    m_barRects.resize(m_choices.size());
    UpdateBarRect();
}

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

void BarGraphStatistic::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);

    {
        // draw background with white color
        painter.fillRect(this->rect(), QBrush(Qt::white));
    }

    {
        // draw axis
        painter.save();
        QPen pen;
        pen.setColor(Qt::gray);
        painter.setPen(pen);

        {
            int x0 = m_padding + m_txtWidth + m_spaceHorizon;
            int y0 = m_padding;
            int x1 = x0;
            int y1 = m_winHeight - m_padding - m_axisXHeight;
            painter.drawLine(x0, y0, x1, y1);
        }
        {
            int x0 = m_padding + m_txtWidth + m_spaceHorizon;
            int y0 = m_winHeight - m_padding - m_axisXHeight;
            int x1 = width() - m_padding;
            int y1 = y0;
            painter.drawLine(x0, y0, x1, y1);
        }
        {
            int x = m_padding + m_txtWidth + m_spaceHorizon;
            int y = m_padding + m_itemHeight * m_choices.size();
            int w = width() - x - m_padding;
            int h = m_axisXHeight;
            painter.drawText(x, y, w, h, Qt::AlignLeft, "0");

            painter.drawText(x, y, w, h, Qt::AlignRight, QString::number(m_maxCnt + 1));
        }
        painter.restore();
    }


    {
        painter.save();
        QFont f;
        f.setPixelSize(m_fontSize);
        painter.setFont(f);

        for (int i = 0; i < m_choices.size(); ++i)
        {
            {
                int x = m_padding;
                int y = m_padding + i * m_itemHeight;
                int w = m_txtWidth;
                int h = m_itemHeight;

                QFontMetrics fm(f);
                auto txt = fm.elidedText(m_choices[i].first, Qt::ElideRight, w);

                painter.drawText(QRect(x, y, w, h), Qt::AlignVCenter|Qt::AlignRight, txt);
            }

            {
                if (m_curBar == i)
                {
                    painter.fillRect(m_barRects[i], QColor(10, 108, 255, 20));
                }

                int x = m_padding + m_txtWidth + m_spaceHorizon;
                int y = m_padding + i * m_itemHeight + m_itemHeight/4;
                int w = (width() - m_padding - m_txtWidth - m_spaceHorizon - m_padding) * m_choices[i].second / (m_maxCnt + 1);
                int h = m_itemHeight/2;
                painter.fillRect(x, y, w, h, QColor(10, 108, 255));

                x = x + w + m_spaceHorizon;
                y = m_padding + i * m_itemHeight;
                w = (width() - m_padding - m_txtWidth - m_spaceHorizon - m_padding) - w;
                h = m_itemHeight;
                painter.drawText(x, y, w, h, Qt::AlignVCenter|Qt::AlignLeft, QString::number(m_choices[i].second));
            }
        }

        if (m_curBar >=0 && m_curBar < m_choices.size())
        {
            painter.save();
            auto text = QString("%1: %2").arg(m_choices[m_curBar].first).arg(m_choices[m_curBar].second);

            auto font = painter.font();
            font.setPixelSize(12);
            QFontMetrics fm(font);
            auto br = fm.boundingRect(text);

            const int maxWidth = 500;
            auto w = qMin(maxWidth, br.width()+10);
            auto h = br.width() * br.height()/w +20;

            QRect rect;
            if (m_mousePos.x() + w + 10  > width())
                rect = QRect(m_mousePos + QPoint(-10-w, 10), QSize(w, h));
            else
                rect = QRect(m_mousePos + QPoint(10, 10), QSize(w, h));

            painter.setFont(font);
            QTextOption tOpt(Qt::AlignTop|Qt::AlignLeft);
            tOpt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
            painter.drawText(rect, text, tOpt);
            painter.restore();
        }
        painter.restore();
    }

}

void BarGraphStatistic::mouseMoveEvent(QMouseEvent *e)
{
    int cur = -1;
    for (int i = 0; i < m_barRects.size(); ++i)
    {
        if (m_barRects[i].contains(e->pos()))
        {
            cur = i;
            break;
        }
    }

//    if (cur != m_curBar)
    {
        m_curBar = cur;
        m_mousePos = e->pos();
        update();
    }
}

void BarGraphStatistic::resizeEvent(QResizeEvent *e)
{
    UpdateBarRect();
}

void BarGraphStatistic::UpdateBarRect()
{
    for (int i = 0; i < m_choices.size(); ++i)
    {
        int x = m_padding + m_txtWidth + m_spaceHorizon;
        int y = m_padding + i * m_itemHeight;
        int w = width() - m_padding - m_txtWidth - m_spaceHorizon - m_padding;
        int h = m_itemHeight;

        m_barRects[i] = QRect{x, y, w, h};
    }
}

下面是我实现的最终效果,是不是还挺像的,如果感觉那里不好,大家就自己去修改源码吧。

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

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

相关文章

集成显卡安装Pytorch

1、安装pytorch的第一步需要先安装Anaconda&#xff0c;安装步骤可看我这篇教程【Anaconda的下载与安装】 2、安装完Anaconda之后&#xff0c;第二步查看自己电脑的显卡类型&#xff0c;是集成显卡还是独立显卡&#xff0c;我的电脑是集成显卡&#xff0c;若你的是独立显卡&am…

磁盘满了对日志打印(Logback)的影响

背景 我们生产环境有一个服务半夜报警&#xff1a;磁盘剩余空间不足10%&#xff0c;请及时处理。排查后发现是新上线的一个功能&#xff0c;日志打太多导致的&#xff0c;解决方法有很多&#xff0c;就不赘述了。领导担心报警不及时、或者报警遗漏&#xff0c;担心磁盘满了对线…

OSPF的7大状态和5大报文详讲

- Down OSPF的初始状态 - Init 初始化——我刚刚给别人发Hello报文 我们可以将OSPF邻居建立的过程理解为&#xff1a;我和你打招呼&#xff0c;你和我打招呼&#xff0c;然后咱俩成了邻居 比如&#xff1a; R1和R2要建立OSPF邻居 R1给R2发送了Hello报文&#xff0c;但是R1此时…

如何提升和扩展 PostgreSQL — 从共享缓冲区到内存数据网格

利用共享缓存和操作系统缓存利用 RAM Postgres 是一个基于磁盘的数据库&#xff0c;即使您的整个架构是围绕磁盘访问设计的&#xff0c;利用 RAM 也很重要。如果按照人类规模的延迟来判断&#xff0c;这可以将延迟从几天缩短到几分钟&#xff08;图 1&#xff09;。只需看一下…

【SV中的多线程fork...join/join_any/join_none】

SV中fork_join/fork_join_any/fork_join_none 1 一目了然1.1 fork...join1.2 fork...join_any1.3 fork...join_none 2 总结 SV中fork_join和fork_join_any和fork_join_none; Note: fork_join在Verilog中也有&#xff0c;只有其他的两个是SV中独有的&#xff1b; 1 一目了然 1.…

CCF CSP认证 历年题目自练Day26

题目一 试题编号&#xff1a; 202012-1 试题名称&#xff1a; 期末预测之安全指数 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 题目分析&#xff08;个人理解&#xff09; 还是先看输出&#xff0c;此题简单的离谱&#xff0c;第一行输入小菜有几个测评依据…

1.3 第一个JAVA程序

1.3 第一个JAVA程序 **1.3 第一个Java程序** **前言:** 在1.2节中&#xff0c;我们学习了如何搭建Java开发环境。本节将带你编写并执行你的第一个Java程序。 **1. 编写Java源文件** - 在JDK的bin文件夹下新建文本文档&#xff0c;并重命名为HelloWorld.java。 - 使用记…

Linux命令笔记

终端命令格式&#xff1a; bash command [-options] [parameter] 7个常见Linux命令&#xff1a; 01 ls | list | 查看当前文件夹下的内容 02 pwd | print work directory | 查看当前所在文件夹 03 cd [目录名] | change directory | 切换文件夹 04 touch [文件名] | touc…

使用ChatGPT和MindShow一分钟生成PPT模板

对于最近学校组织的实习答辩&#xff0c;由于时间太短了&#xff0c;而且小编也特别的忙&#xff0c;于是就用ChatGPT结合MindShow一分钟快速生成PPT&#xff0c;确实很实用。只要你跟着小编后面&#xff0c;你也可以快速制作出这个PPT&#xff0c;下面小编就来详细介绍一下&am…

idea(添加jsp文件模板)

代码模板 初始化&#xff1a; <%-- Created by IntelliJ IDEA. User: ${USER} Date: ${DATE} Time: ${TIME} To change this template use File | Settings | File Templates. --%> <% page contentType"text/html;charsetUTF-8" language&…

微服务09-Sentinel的入门

微服务中的雪崩现象 首先&#xff0c;我们介绍一下微服务中雪崩现象&#xff1a;因为微服务中服务是互相调用的&#xff0c;错综复杂&#xff0c;当一个服务D出现问题时&#xff0c;那么调用D的服务请求就会失败&#xff0c;当请求累积到一定的量时&#xff0c;请求D的服务也会…

常见排序算法Java版(待续)

冒泡排序O(n^2) public class Main {public static void main(String[] args) {Random random new Random();int[] nums new int[]{random.nextInt(100), random.nextInt(100), random.nextInt(100), random.nextInt(100), random.nextInt(100), random.nextInt(100)};for (i…

抽象轻松的java-mybatis简单入门

第一步&#xff1a;用IDEA新建一个java包 第二步&#xff1a;在IDEA中添加数据库&#xff08;ps&#xff1a;自己百度&#xff09; 点击数据库 第二步&#xff0c;新建数据库 选择你使用的数据库 用户与密码根据自己的设置进行配置 为了更方便的查看数据库&#xff0c;可以像图…

一篇短小精悍的文章让你彻底明白KMP算法中next数组的原理

以后保持每日一更&#xff0c;由于兴趣较多&#xff0c;更新内容不限于数据结构&#xff0c;计算机组成原理&#xff0c;数论&#xff0c;拓扑学......&#xff0c;所谓&#xff1a;深度围绕职业发展&#xff0c;广度围绕兴趣爱好。往下看今日内容 一.什么是KMP算法 KMP&#x…

C#对字典容器Dictionary<TKey, TValue>内容进行XML序列化或反序列化报错解决方法

一、问题描述 在使用C#对字典容器Dictionary<TKey, TValue>内容进行XML序列化报错【System.Exception:“不支持类型 System.Collections.Generic.Dictionary2[[System.String, mscorlib, Version2.0.0.0, Cultureneutral, PublicKeyTokenb77a5c561934e089],[System.Strin…

力扣刷题 day39:10-09

1.统计有序矩阵中的负数 给你一个 m * n 的矩阵 grid&#xff0c;矩阵中的元素无论是按行还是按列&#xff0c;都以非递增顺序排列。 请你统计并返回 grid 中 负数 的数目。 方法一&#xff1a;二分法 #方法一&#xff1a;二分法 def countNegatives(grid):res0for nums in…

Altium Designer实用系列(二)----PCB绘图小技巧

一、技巧总结 1.1 丝印大小 在导入PCB之后&#xff0c;元器件的丝印一般都是strock font&#xff0c;个人感觉比较大&#xff0c;也不美观&#xff0c;但是一个个修改成true type又比较麻烦。简便方法是使用相似查找全部修改:   此时会选中所有stroke 类型的丝印&#xff…

提高工作效率!本地部署Stackedit Markdown编辑器,并实现远程访问

文章目录 1. docker部署Stackedit2. 本地访问3. Linux 安装cpolar4. 配置Stackedit公网访问地址5. 公网远程访问Stackedit6. 固定Stackedit公网地址 StackEdit是一个受欢迎的Markdown编辑器&#xff0c;在GitHub上拥有20.7k Star&#xff01;&#xff0c;它支持将Markdown笔记保…

git命令笔记

git命令笔记 前言&#xff1a;git对于软件开发和协作的重要性不言而喻&#xff0c;在企业开发中&#xff0c;git命令和linux命令的使用同样重要。作为开发者&#xff0c;需要牢记并熟练使用常见的git命令 git工作流程图 命令如下&#xff1a; clone&#xff08;克隆&#xf…

论文解析——异构多芯粒神经网络加速器

作者 朱郭益, 马胜&#xff0c;张春元, 王波&#xff08;国防科技大学计算机学院&#xff09; 摘要 随着神经网络技术的快速发展, 出于安全性等方面考虑, 大量边缘计算设备被应用于智能计算领域。首先&#xff0c;设计了可应用于边缘计算的异构多芯粒神经网络加速器其基本结构…