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

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

相关推荐
小俊俊的博客1 小时前
海康RGBD相机使用C++和Opencv采集图像记录
c++·opencv·海康·rgbd相机
7yewh1 小时前
嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目
linux·开发语言·arm开发·驱动开发·qt·opencv·嵌入式linux
_WndProc1 小时前
C++ 日志输出
开发语言·c++·算法
薄荷故人_1 小时前
从零开始的C++之旅——红黑树及其实现
数据结构·c++
m0_748240021 小时前
Chromium 中chrome.webRequest扩展接口定义c++
网络·c++·chrome
qq_433554541 小时前
C++ 面向对象编程:+号运算符重载,左移运算符重载
开发语言·c++
努力学习编程的伍大侠1 小时前
基础排序算法
数据结构·c++·算法
yuyanjingtao2 小时前
CCF-GESP 等级考试 2023年9月认证C++四级真题解析
c++·青少年编程·gesp·csp-j/s·编程等级考试
終不似少年遊*2 小时前
pyecharts
python·信息可视化·数据分析·学习笔记·pyecharts·使用技巧
陆沙2 小时前
生物信息学导论-北大-RNA-Seq数据分析
数据分析·生物信息·生信