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

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

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

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

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

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

BarGraphStatistic.h

cpp 复制代码
#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

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};
    }
}

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

相关推荐
Icomi_1 小时前
【神经网络】0.深度学习基础:解锁深度学习,重塑未来的智能新引擎
c语言·c++·人工智能·python·深度学习·神经网络
不知道取啥耶2 小时前
C++ 滑动窗口
数据结构·c++·算法·leetcode
刘大猫262 小时前
一、MyBatis简介:MyBatis历史、MyBatis特性、和其它持久化层技术对比、Mybatis下载依赖包流程
人工智能·数据挖掘·数据分析
zephyr_zeng3 小时前
VsCode + EIDE + OpenOCD + STM32(野火DAP) 开发环境配置
c语言·c++·vscode·stm32·单片机·嵌入式硬件·编辑器
秀儿还能再秀4 小时前
淘宝母婴购物数据可视化分析(基于脱敏公开数据集)
python·数据分析·学习笔记·数据可视化
Stack Overflow?Tan904 小时前
c++实现在同一台主机两个程序实现实时通信
开发语言·c++
计算机老学长4 小时前
基于Python的商品销量的数据分析及推荐系统
开发语言·python·数据分析
@@永恒5 小时前
map&set
c++
水瓶丫头站住5 小时前
Qt的QGraphicsView控件的样式设置
qt·样式设置
小鹏编程5 小时前
【C++教程】C++中的基本数据类型
开发语言·c++·教程·少儿编程