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

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

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

Qt实战 数据统计柱状图显示_第1张图片

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

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

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

BarGraphStatistic.h

#ifndef BARGRAPHSTATISTIC_H
#define BARGRAPHSTATISTIC_H

#include 
#include 
#include 

namespace Ui {
class BarGraphStatistic;
}

class BarGraphStatistic : public QWidget
{
    Q_OBJECT

public:
    explicit BarGraphStatistic(const QVector>& 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> 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 m_barRects;
    int m_curBar = -1;
    QPoint m_mousePos;
};


#endif // BARGRAPHSTATISTIC_H

BarGraphStatistic.cpp

#include "BarGraphStatistic.h"
#include "ui_BarGraphStatistic.h"
#include 
#include 
#include 
#include 

BarGraphStatistic::BarGraphStatistic(const QVector>& 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};
    }
}

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

Qt实战 数据统计柱状图显示_第2张图片

你可能感兴趣的:(qt,c++,数据分析)