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

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

相关推荐
RichardK.4 小时前
CCF-CSP第27次认证第1题 --《如此编码》
c++·学习
追烽少年x5 小时前
Qt 元对象系统
qt
星夜钢琴手5 小时前
C/C++ 实现由用户通过键盘输入自然数并判断其是不是素数(带清空缓冲区等考虑)
c语言·开发语言·c++·c/c++
_GR6 小时前
2019年蓝桥杯第十届C&C++大学B组真题及代码
c语言·数据结构·c++·算法·蓝桥杯
WW_千谷山4_sch6 小时前
MYOJ_4204:迷宫(图论-网格图基础,dfs,bfs在网格图中应用)
数据结构·c++·深度优先·图论·广度优先
weixin_307779137 小时前
PySpark实现导出两个包含多个Parquet数据文件的S3目录里的对应值的差异值分析
python·数据分析·spark·云计算
郭涤生7 小时前
在线程间共享数据_第三章_《C++并发编程》笔记
c++·笔记·算法
边城梦溪8 小时前
《深入理解Linux:高效崩溃分析与实时栈回溯技巧》
linux·服务器·c++·后端·面试
TsuanS8 小时前
C++ MySQL 常用接口(基于 MySQL Connector/C++)
开发语言·c++·mysql
PiKaMouse.8 小时前
Qt串口通信开发教程:Linux下的串口调试工具实现
linux·开发语言·c++·qt