QML实现数据可视化

发布于:2025-08-16 ⋅ 阅读:(15) ⋅ 点赞:(0)

界面样式

项目开发流程

1.通过QtCreator创建一个Qt Quick插件,插件命名为CarPanMod;

2.通过QtCreator创建一个Qt  Quick Application,命名为QmlPro;

3.在插件CarPanMod中实现条形图,折线图和饼状图的绘制;

4.在应用程序QmlPro中,添加插件的导入路径;

5.在应用程序中,通过import CarPanMod 1.0导入Qml文件即可访问插件中实现的各种图表.

C++代码实现

条形图实现

barchartitem.h

#ifndef BARCHARTITEM_H
#define BARCHARTITEM_H

#include <QQuickPaintedItem>
#include <QVector>
#include <QPropertyAnimation>

class BarChartItem : public QQuickPaintedItem {
    Q_OBJECT
    Q_PROPERTY(QVariantList data READ data WRITE setData NOTIFY dataChanged)
    Q_PROPERTY(QStringList labels READ labels WRITE setLabels NOTIFY labelsChanged)
    Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
    Q_PROPERTY(QColor barColor READ barColor WRITE setBarColor NOTIFY barColorChanged)

public:
    explicit BarChartItem(QQuickItem *parent = nullptr);
    void paint(QPainter *painter) override;

    // 属性访问器
    QVariantList data() const;
    void setData(const QVariantList &data);
    QStringList labels() const;
    void setLabels(const QStringList &labels);
    QString title() const;
    void setTitle(const QString &title);
    QColor barColor() const;
    void setBarColor(const QColor &color);

protected:
    void hoverMoveEvent(QHoverEvent *event) override;
    void hoverLeaveEvent(QHoverEvent *event) override;

signals:
    void dataChanged();
    void labelsChanged();
    void titleChanged();
    void barColorChanged();

private:
    struct BarItem {
        QRectF rect;
        qreal value;
        bool highlighted = false;
        QColor color;
    };

    void updateChartLayout();
    void animateBar(int index, bool highlight);

    QVariantList m_data = {
        6324, 6793, 7476, 7892,
        8734, 9337, 9860, 10485,
        11230, 12100
    };
    QStringList m_labels = {
        "2014", "2015", "2016", "2017",
        "2018", "2019", "2020", "2021",
        "2022", "2023"
    };
    QString m_title = "全国平均房价(元/㎡)";
    QColor m_barColor = QColor("#3498db");
    QVector<BarItem> m_bars;
    int m_hoveredIndex = -1;
    QPropertyAnimation *m_animation;
};


#endif // BARCHARTITEM_H

barchartitem.cpp

#include "barchartitem.h"

#include <QPainter>
#include <QFontMetrics>

BarChartItem::BarChartItem(QQuickItem *parent)
    : QQuickPaintedItem(parent), m_animation(new QPropertyAnimation(this)) {
    setAcceptHoverEvents(true);
    setFlag(ItemHasContents, true);
    setAntialiasing(true);
    m_animation->setPropertyName("barColor");
    m_animation->setDuration(300);
}

void BarChartItem::paint(QPainter *painter) {
    painter->setRenderHint(QPainter::Antialiasing);

    // 绘制背景
//    painter->fillRect(boundingRect(), QColor("#f5f5f5"));
    painter->fillRect(boundingRect(),QColor("transparent"));

    // 计算布局
    updateChartLayout();

    // 绘制标题
    QFont titleFont = painter->font();
    titleFont.setPointSize(14);
    titleFont.setBold(true);
    painter->setFont(titleFont);
    painter->setPen(Qt::black);
    painter->drawText(QRectF(0, 10, width(), 30), Qt::AlignCenter, m_title);

    // 绘制坐标轴
    const qreal axisMargin = 50;
    const QLineF xAxis(axisMargin, height() - axisMargin, width() - axisMargin, height() - axisMargin);
    const QLineF yAxis(axisMargin, axisMargin, axisMargin, height() - axisMargin);
    painter->setPen(QPen(Qt::black, 2));
    painter->drawLine(xAxis);
    painter->drawLine(yAxis);

    // 绘制刻度
    QFont tickFont = painter->font();
    tickFont.setPointSize(8);
    painter->setFont(tickFont);

    // Y轴刻度
    qreal maxValue = 0;
    for (const auto &v : m_data) {
        maxValue = qMax(maxValue, v.toReal());
    }
    const int yTicks = 5;
    for (int i = 0; i <= yTicks; ++i) {
        qreal y = yAxis.y1() - (yAxis.length() / yTicks) * i;
        qreal value = maxValue / yTicks * i;
        painter->drawLine(QPointF(yAxis.x1() - 5, y), QPointF(yAxis.x1(), y));
        painter->drawText(QRectF(0, y - 10, yAxis.x1() - 10, 20),
                         Qt::AlignRight | Qt::AlignVCenter,
                         QString::number(value, 'f', 0));
    }

    // 绘制条形
    for (int i = 0; i < m_bars.size(); ++i) {
        const BarItem &bar = m_bars[i];
        QColor color = bar.highlighted ? bar.color.lighter(130) : bar.color;

        // 条形阴影
        painter->setPen(Qt::NoPen);
        painter->setBrush(QColor(0, 0, 0, 50));
        painter->drawRect(bar.rect.adjusted(3, 3, 3, 3));

        // 条形主体
        painter->setBrush(color);
        painter->setPen(QPen(Qt::white, 1));
        painter->drawRect(bar.rect);

        // 数值标签
        painter->setPen(Qt::black);
        painter->drawText(QRectF(bar.rect.x(), bar.rect.y() - 25,
                                bar.rect.width(), 20),
                         Qt::AlignCenter,
                         QString::number(bar.value, 'f', 0));

        // X轴标签
        if (i < m_labels.size()) {
            painter->drawText(QRectF(bar.rect.x(), height() - axisMargin + 5,
                                    bar.rect.width(), 20),
                             Qt::AlignCenter, m_labels[i]);
        }
    }

#if 0
    // 绘制图例
    if (!m_labels.isEmpty()) {
        QRectF legendRect(width() - 150, 40, 140, 30);
        painter->setBrush(m_barColor);
        painter->drawRect(legendRect.adjusted(0, 0, -110, 0));
        painter->drawText(legendRect, Qt::AlignRight | Qt::AlignVCenter, "房价趋势");
    }
#endif
}

QVariantList BarChartItem::data() const
{
    return  m_data;
}

void BarChartItem::setData(const QVariantList &data)
{
    if(m_data != data){
        m_data = data;
        update();
        emit dataChanged();
    }
}

QStringList BarChartItem::labels() const
{
    return m_labels;
}

void BarChartItem::setLabels(const QStringList &labels)
{
    if (m_labels != labels) {
        m_labels = labels;
        update();
        emit labelsChanged();
    }
}

QString BarChartItem::title() const
{
    return m_title;
}

void BarChartItem::setTitle(const QString &title)
{
    if(m_title != title){
        m_title = title;
        update();
        emit titleChanged();
    }
}

QColor BarChartItem::barColor() const
{
    return m_barColor;
}

void BarChartItem::setBarColor(const QColor &color)
{
    if(m_barColor != color){
        m_barColor = color;
        update();
        emit barColorChanged();
    }
}

void BarChartItem::updateChartLayout() {
    if (m_data.isEmpty()) return;

    const qreal axisMargin = 50;
    const qreal chartWidth = width() - axisMargin * 2;
    const qreal chartHeight = height() - axisMargin * 2;
    const qreal barSpacing = 10;
    const qreal barWidth = (chartWidth - barSpacing * (m_data.size() - 1)) / m_data.size();

    qreal maxValue = 0;
    for (const auto &v : m_data) {
        maxValue = qMax(maxValue, v.toReal());
    }

    m_bars.resize(m_data.size());
    for (int i = 0; i < m_data.size(); ++i) {
        qreal value = m_data[i].toReal();
        qreal barHeight = (value / maxValue) * chartHeight;

        m_bars[i].rect = QRectF(
            axisMargin + i * (barWidth + barSpacing),
            height() - axisMargin - barHeight,
            barWidth,
            barHeight
        );
        m_bars[i].value = value;
        m_bars[i].color = m_barColor;
        m_bars[i].highlighted = (i == m_hoveredIndex);
    }
}

void BarChartItem::animateBar(int index, bool highlight) {
    if (index < 0 || index >= m_bars.size()) return;

    m_animation->setStartValue(m_bars[index].color);
    m_animation->setEndValue(highlight ? m_barColor.lighter(130) : m_barColor);
    m_animation->setDuration(1000);
    m_animation->start();
}

void BarChartItem::hoverMoveEvent(QHoverEvent *event) {
    const QPointF pos = event->pos();
    for (int i = 0; i < m_bars.size(); ++i) {
        if (m_bars[i].rect.contains(pos)) {
            if (m_hoveredIndex != i) {
                m_hoveredIndex = i;
//                animateBar(i, true);
                update();
            }
            return;
        }
    }

    if (m_hoveredIndex != -1) {
        m_hoveredIndex = -1;
        update();
    }
}

void BarChartItem::hoverLeaveEvent(QHoverEvent *) {
    if (m_hoveredIndex != -1) {
//        animateBar(m_hoveredIndex, false);
        m_hoveredIndex = -1;
        update();
    }
}

折线图实现

linechartitem.h


网站公告

今日签到

点亮在社区的每一天
去签到