qt自绘制,蜂巢网格,感觉没什么用

cpp 复制代码
// Copyright (C) 2016 The Qt Company Ltd. // 版权所有 (C) 2016 Qt 公司。
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // SPDX-许可证标识符:LicenseRef-Qt-Commercial 或 LGPL-3.0-only 或 GPL-2.0-only 或 GPL-3.0-only

#ifndef HEXAGONWIDGET_H // 如果未定义 HEXAGONWIDGET_H。
#define HEXAGONWIDGET_H // 定义 HEXAGONWIDGET_H。

#include <QWidget> // 包含 QWidget 类,它是所有用户界面对象的基类。
#include <QVector> // 包含 QVector 类,提供动态数组。
#include <QPointF> // 包含 QPointF 类,表示浮点精确度的点。
#include <QMap> // 用于存储六边形中心点和其对应的多边形。

//动画类
#include <QPropertyAnimation>


class HexagonWidget : public QWidget // HexagonWidget 类声明,继承自 QWidget。
{
    Q_OBJECT // 宏,用于启用 Qt 的元对象系统,支持信号与槽。
    //放大
    Q_PROPERTY(qreal Center_Hexagon READ Center_Hexagon WRITE setCenter_Hexagon NOTIFY Center_HexagonChanged FINAL)
    Q_PROPERTY(qreal Edge_Hexagon READ Edge_Hexagon WRITE setEdge_Hexagon NOTIFY Edge_HexagonChanged FINAL)

public:
    explicit HexagonWidget(QWidget *parent = nullptr); // 显式构造函数,允许指定父部件。
    void setHexagonSize(double size); // 设置六边形的基准边长。
    void setHexagonSpacing(double spacing); // 设置六边形之间的间距。

    //执行动画
    void startAnimation(); // 启动六边形放大动画。



    qreal Center_Hexagon() const;
    void setCenter_Hexagon(qreal newCenter_Hexagon);

    qreal Edge_Hexagon() const;
    void setEdge_Hexagon(qreal newEdge_Hexagon);

signals:
    void Center_HexagonChanged();

    void Edge_HexagonChanged();

protected:
    void paintEvent(QPaintEvent *event) override; // 覆盖 paintEvent,处理部件的绘制事件。
    void mouseMoveEvent(QMouseEvent *event) override; // 新增:鼠标移动事件处理函数。
    void leaveEvent(QEvent *event) override;          // 新增:鼠标离开部件事件处理函数。

    QPolygonF createHexagon(QPointF center, double currentHexagonSize) const; // 修改:增加一个参数来控制当前绘制的六边形大小。根据中心点和给定大小创建六边形多边形。

private:
    double m_hexagonSize;    // 六边形基准边长。
    double m_hexagonSpacing; // 六边形间距。
    double m_hexagonWidth;   // 六边形宽度 (两相对边距离)。
    double m_hexagonHeight;  // 六边形高度 (两相对顶点距离)。

    QPointF m_hoveredHexagonCenter; // 存储当前鼠标悬停的六边形中心点。
    bool m_isHovering;               // 标记是否正在悬停在某个六边形上。


    void calculateHexagonDimensions(); // 计算六边形的内部尺寸(宽度和高度)。
    // 根据鼠标位置查找哪个六边形被悬停。
    QPointF getHoveredHexagonCenter(const QPoint &pos);
    // 获取指定中心点的六边形的所有相邻六边形的中心点。
    QVector<QPointF> getNeighborHexagonCenters(QPointF center) const;


    qreal m_Center_Hexagon; // 当前中心六边形的大小。
    qreal m_Edge_Hexagon; // 当前边缘六边形的大小。
};

#endif // HEXAGONWIDGET_H // 结束 HEXAGONWIDGET_H 头文件保护。
cpp 复制代码
// Copyright (C) 2016 The Qt Company Ltd. // 版权所有 (C) 2016 Qt 公司。
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // SPDX-许可证标识符:LicenseRef-Qt-Commercial 或 LGPL-3.0-only 或 GPL-2.0-only 或 GPL-3.0-only

#include "hexagonwidget.h" // 包含 HexagonWidget 的头文件。
#include <QPainter> // 包含 QPainter 类,用于在部件上进行绘制。
#include <QtMath> // 包含 Qt 数学函数,例如 qSqrt, qDegreesToRadians。
#include <QMouseEvent> // 确保包含 QMouseEvent 头文件,用于处理鼠标事件。

// HexagonWidget 类的构造函数。
HexagonWidget::HexagonWidget(QWidget *parent)
    : QWidget(parent) // 调用基类 QWidget 的构造函数。
    , m_hexagonSize(50.0) // 初始化六边形边长。
    , m_hexagonSpacing(5.0) // 初始化六边形间距。
    , m_isHovering(false) // 初始状态为未悬停。
{
    setMouseTracking(true); // 启用鼠标跟踪,即使没有按键也会触发 mouseMoveEvent。
    calculateHexagonDimensions(); // 计算六边形的尺寸(宽度和高度)。
}

// 设置六边形边长的方法。
void HexagonWidget::setHexagonSize(double size)
{
    if (size > 0 && m_hexagonSize != size) // 如果新尺寸有效且与当前尺寸不同。
    {
        m_hexagonSize = size; // 更新六边形边长。
        calculateHexagonDimensions(); // 重新计算六边形尺寸。
        update(); // 触发重绘以更新显示。
    }
}

// 设置六边形间距的方法。
void HexagonWidget::setHexagonSpacing(double spacing)
{
    if (spacing >= 0 && m_hexagonSpacing != spacing) // 如果新间距有效且与当前间距不同。
    {
        m_hexagonSpacing = spacing; // 更新六边形间距。
        calculateHexagonDimensions(); // 重新计算六边形尺寸。
        update(); // 触发重绘以更新显示。
    }
}

void HexagonWidget::startAnimation()
{
    QPropertyAnimation* m_animation1 = new QPropertyAnimation(this, "Center_Hexagon"); // 创建一个新的动画,目标属性为 Center_Hexagon。
    m_animation1->setDuration(350); // 设置动画持续时间为 1000 毫秒(1 秒)。
    m_animation1->setStartValue(1.0); // 设置动画起始值为 1.0。
    m_animation1->setEndValue(1.2); // 设置动画结束值为 2.0。
    m_animation1->setEasingCurve(QEasingCurve::InOutQuad); // 设置缓动曲线为 InOutQuad,使动画平滑过渡。


    QPropertyAnimation * m_animation2 = new QPropertyAnimation(this, "Edge_Hexagon"); // 创建另一个动画,目标属性为 Edge_Hexagon。
    m_animation2->setDuration(350); // 设置动画持续时间为 1000 毫秒(1 秒)。
    m_animation2->setStartValue(1.0); // 设置动画起始值为 1.0。
    m_animation2->setEndValue(0.6); // 设置动画结束值为 0.7。
    m_animation2->setEasingCurve(QEasingCurve::InOutQuad); // 设置缓动曲线为 InOutQuad,使动画平滑过渡。


    m_animation1->start(QAbstractAnimation::DeleteWhenStopped); // 启动动画。
    m_animation2->start(QAbstractAnimation::DeleteWhenStopped); // 启动第二个动画。



}



// 计算六边形宽度和高度的方法。
void HexagonWidget::calculateHexagonDimensions()
{
    m_hexagonWidth = m_hexagonSize * qSqrt(3.0); // 计算六边形宽度(对边距离)。
    m_hexagonHeight = 2.0 * m_hexagonSize; // 计算六边形高度(顶点到对边顶点的距离)。
}

// 修改 createHexagon 以便接收当前绘制的六边形大小。
// 根据中心点和六边形大小创建六边形的 QPolygonF 对象。
QPolygonF HexagonWidget::createHexagon(QPointF center, double currentHexagonSize) const
{
    QPolygonF hexagon; // 创建一个 QPolygonF 对象。
    for (int i = 0; i < 6; ++i) // 循环 6 次,为六边形的每个顶点。
    {
        double angle_deg = 60 * i + 30; // 计算当前顶点的角度(度),加30度是为了使六边形平放。
        double angle_rad = qDegreesToRadians(angle_deg); // 将角度从度转换为弧度。
        hexagon << QPointF(center.x() + currentHexagonSize * qCos(angle_rad), // 计算顶点 X 坐标。
                           center.y() + currentHexagonSize * qSin(angle_rad)); // 计算顶点 Y 坐标。
    }
    return hexagon; // 返回构建好的六边形多边形。
}

// 新增:查找哪个六边形被悬停。
// 根据鼠标位置查找被悬停的六边形的中心点。
QPointF HexagonWidget::getHoveredHexagonCenter(const QPoint &pos)
{
    // 计算实际的六边形宽度和高度,考虑间距。
    double hexGridWidth = m_hexagonWidth + m_hexagonSpacing; // 计算网格中六边形的水平总宽度(包括间距)。
    double hexGridHeight = m_hexagonHeight * 0.75 + m_hexagonSpacing; // 计算网格中六边形的垂直总高度(包括间距)。

    // 初始偏移量。
    double xOffset = m_hexagonWidth / 2.0; // 六边形第一列的 X 偏移。
    double yOffset = m_hexagonSize; // 六边形第一行的 Y 偏移。

    // 我们可以反向计算鼠标点可能落在哪一行哪一列。
    // 这是一个简化的方法,更精确的需要考虑六边形形状。
    // 粗略判断鼠标点在哪个"单元格"内。

    // 考虑y轴偏移来找到最近的行。
    int estimatedRow = qRound((pos.y() - yOffset) / hexGridHeight); // 估算鼠标所在行。
    if (estimatedRow < 0) estimatedRow = 0; // 确保行号非负。

    // 根据行数判断x轴偏移。
    double currentXOffset = xOffset; // 默认 X 偏移。
    if (estimatedRow % 2 != 0) { // 如果是奇数行。
        currentXOffset += hexGridWidth / 2.0; // 奇数行会向右偏移半个网格宽度。
    }

    // 考虑x轴偏移来找到最近的列。
    int estimatedCol = qRound((pos.x() - currentXOffset) / hexGridWidth); // 估算鼠标所在列。
    if (estimatedCol < 0) estimatedCol = 0; // 确保列号非负。

    // 遍历鼠标点附近的几个六边形,判断精确的悬停。
    // 遍历周围的 3x3 区域,以确保找到正确的六边形。
    for (int r_offset = -1; r_offset <= 1; ++r_offset) {
        for (int c_offset = -1; c_offset <= 1; ++c_offset) {
            int row = estimatedRow + r_offset; // 计算实际行。
            int col = estimatedCol + c_offset; // 计算实际列。

            if (row < 0 || col < 0) continue; // 避免负索引,跳过无效的行或列。

            double x = col * hexGridWidth + xOffset; // 计算当前六边形的 X 坐标。
            double y = row * hexGridHeight + yOffset; // 计算当前六边形的 Y 坐标。

            if (row % 2 != 0) { // 奇数行交错。
                x += hexGridWidth / 2.0; // 如果是奇数行,X 坐标需要额外偏移。
            }

            QPointF center(x, y); // 构成当前六边形的中心点。
            // 检查鼠标点是否在当前计算出的六边形内部。
            QPolygonF hexPolygon = createHexagon(center, m_hexagonSize); // 根据中心点和默认大小创建六边形多边形。
            if (hexPolygon.containsPoint(pos, Qt::OddEvenFill)) { // 判断鼠标点是否在该多边形内。
                return center; // 找到被悬停的六边形中心,并返回。
            }
        }
    }
    return QPointF(); // 没有六边形被悬停,返回空 QPointF。
}

// 新增:获取指定六边形的所有相邻六边形中心点。
// 根据给定的六边形中心点,计算并返回其所有相邻六边形的中心点列表。
QVector<QPointF> HexagonWidget::getNeighborHexagonCenters(QPointF center) const
{
    QVector<QPointF> neighbors; // 用于存储相邻六边形中心的向量。
    double hexGridWidth = m_hexagonWidth + m_hexagonSpacing; // 计算网格中六边形的水平总宽度。
    double hexGridHeight = m_hexagonHeight * 0.75 + m_hexagonSpacing; // 计算网格中六边形的垂直总高度。

    // 相邻偏移量 (相对一个六边形的中心)。
    // 这是一个简化的表示,需要根据六边形中心推算其相邻六边形中心。
    // 考虑六边形网格的两种相邻模式:水平方向和对角线方向。

    // 直接水平相邻。
    neighbors.append(QPointF(center.x() + hexGridWidth, center.y())); // 右侧相邻。
    neighbors.append(QPointF(center.x() - hexGridWidth, center.y())); // 左侧相邻。

    // 上下对角线相邻 (左上, 右上, 左下, 右下)。
    // 这里的偏移量需要精确计算。
    double x_offset_half = hexGridWidth / 2.0; // 水平偏移量的一半。
    double y_offset_quarter_height = m_hexagonHeight * 0.75; // 六边形堆叠的垂直步长。

    // 上方两相邻。
    neighbors.append(QPointF(center.x() + x_offset_half, center.y() - y_offset_quarter_height - m_hexagonSpacing)); // 右上方相邻。
    neighbors.append(QPointF(center.x() - x_offset_half, center.y() - y_offset_quarter_height - m_hexagonSpacing)); // 左上方相邻。

    // 下方两相邻。
    neighbors.append(QPointF(center.x() + x_offset_half, center.y() + y_offset_quarter_height + m_hexagonSpacing)); // 右下方相邻。
    neighbors.append(QPointF(center.x() - x_offset_half, center.y() + y_offset_quarter_height + m_hexagonSpacing)); // 左下方相邻。

    return neighbors; // 返回所有相邻六边形的中心点列表。
}

// 绘制事件处理函数,负责绘制六边形网格。
void HexagonWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event); // 标记 event 未使用,避免编译警告。
    QPainter painter(this); // 创建 QPainter 对象,在当前部件上绘制。
    painter.setRenderHint(QPainter::Antialiasing); // 启用抗锯齿,使图形更平滑。
    painter.setPen(QPen(Qt::black, 1)); // 设置画笔为黑色,宽度为 1。

    double hexGridWidth = m_hexagonWidth + m_hexagonSpacing; // 计算网格中六边形的水平总宽度。
    double hexGridHeight = m_hexagonHeight * 0.75 + m_hexagonSpacing; // 计算网格中六边形的垂直总高度。

    double xOffset = m_hexagonWidth / 2.0; // 六边形第一列的 X 偏移。
    double yOffset = m_hexagonSize; // 六边形第一行的 Y 偏移。

    // 获取相邻六边形的中心列表。
    QVector<QPointF> neighborCenters; // 存储相邻六边形中心点的向量。
    if (m_isHovering && !m_hoveredHexagonCenter.isNull())
    { // 如果鼠标悬停且悬停中心点有效。
        neighborCenters = getNeighborHexagonCenters(m_hoveredHexagonCenter); // 获取悬停六边形的相邻中心点。
    }

    // 增加绘制范围,确保覆盖放大和缩小的六边形。
    // 计算需要绘制的列数和行数,增加一些冗余以确保覆盖。
    int numCols = qCeil((width() + m_hexagonSize * 2) / hexGridWidth) + 3;
    int numRows = qCeil((height() + m_hexagonSize * 2) / hexGridHeight) + 3;

    // 遍历所有可能的六边形位置。
    for (int row = 0; row < numRows; ++row) {
        for (int col = 0; col < numCols; ++col) {
            double x = col * hexGridWidth + xOffset; // 计算当前六边形的 X 坐标。
            double y = row * hexGridHeight + yOffset; // 计算当前六边形的 Y 坐标。

            if (row % 2 != 0) { // 如果是奇数行。
                x += hexGridWidth / 2.0; // 奇数行向右偏移。
            }

            QPointF currentHexagonCenter(x, y); // 当前六边形的中心点。

            // 只有当六边形完全或部分在可见区域内时才绘制,并考虑放大后的尺寸。
            // 检查六边形是否在可见区域内。
            if (x - m_hexagonSize * 2 < width() && x + m_hexagonSize * 2 > 0 &&
                y - m_hexagonSize * 2 < height() && y + m_hexagonSize * 2 > 0) {

                double currentDrawSize = m_hexagonSize; // 默认绘制大小为 m_hexagonSize。
                QBrush currentBrush = QBrush(Qt::cyan); // 默认填充颜色为青色。

                if (m_isHovering) // 如果正在悬停。
                {
                    if (currentHexagonCenter == m_hoveredHexagonCenter) { // 如果是悬停的六边形。
                        currentDrawSize = m_hexagonSize * m_Center_Hexagon; // 放大1.3倍。
                        currentBrush = QBrush(Qt::red); // 悬停六边形颜色为红色。
                    } else if (neighborCenters.contains(currentHexagonCenter)) { // 如果是悬停六边形的相邻六边形。
                        currentDrawSize = m_hexagonSize * m_Edge_Hexagon; // 缩小0.7倍。
                        currentBrush = QBrush(Qt::darkCyan); // 相邻六边形颜色为深青色。
                    }
                }
                painter.setBrush(currentBrush); // 设置画刷。
                painter.drawPolygon(createHexagon(currentHexagonCenter, currentDrawSize)); // 绘制六边形。
            }
        }
    }
}

// 新增:鼠标移动事件处理。
void HexagonWidget::mouseMoveEvent(QMouseEvent *event)
{
    QPointF oldHoverCenter = m_hoveredHexagonCenter; // 记录旧的悬停中心。
    m_hoveredHexagonCenter = getHoveredHexagonCenter(event->pos()); // 获取新的悬停中心。

    if (m_hoveredHexagonCenter.isNull() && m_isHovering)
    {
        // 鼠标移出所有六边形,或从一个六边形移到空白区域。
        m_isHovering = false; // 设置未悬停状态。
        update(); // 触发重绘,恢复所有六边形到默认大小。
    }
    else if (!m_hoveredHexagonCenter.isNull() && m_hoveredHexagonCenter != oldHoverCenter)
    {
        // 鼠标移到新的六边形上。
        m_isHovering = true; // 设置悬停状态。

        startAnimation(); // 启动放大动画。

    }
    QWidget::mouseMoveEvent(event); // 调用基类的事件处理。
}

// 新增:鼠标离开控件事件处理。
void HexagonWidget::leaveEvent(QEvent *event)
{
    if (m_isHovering)
    { // 如果之前处于悬停状态。
        m_isHovering = true; // 设置未悬停状态。
        m_hoveredHexagonCenter = QPointF(); // 清空悬停中心。

    }
    QWidget::leaveEvent(event); // 调用基类的事件处理。
}

qreal HexagonWidget::Center_Hexagon() const
{
    return m_Center_Hexagon;
}

void HexagonWidget::setCenter_Hexagon(qreal newCenter_Hexagon)
{
    if (qFuzzyCompare(m_Center_Hexagon, newCenter_Hexagon))
        return;
    m_Center_Hexagon = newCenter_Hexagon;
    update(); // 更新绘制以反映新的中心六边形大小。
    emit Center_HexagonChanged();
}

qreal HexagonWidget::Edge_Hexagon() const
{
    return m_Edge_Hexagon;
}

void HexagonWidget::setEdge_Hexagon(qreal newEdge_Hexagon)
{
    if (qFuzzyCompare(m_Edge_Hexagon, newEdge_Hexagon))
        return;
    m_Edge_Hexagon = newEdge_Hexagon;
    emit Edge_HexagonChanged();
}
cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "hexagonwidget.h"
#include <QSlider>
#include <QLabel>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void onSizeSliderChanged(int value);
    void onSpacingSliderChanged(int value);

private:
    HexagonWidget *m_hexagonWidget;
    QSlider *m_sizeSlider;
    QSlider *m_spacingSlider;
    QLabel *m_sizeLabel;
    QLabel *m_spacingLabel;
};

#endif // MAINWINDOW_H
cpp 复制代码
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QWidget>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // 创建中心Widget和布局
    QWidget *centralWidget = new QWidget(this);
    setCentralWidget(centralWidget);
    QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);

    // 添加 HexagonWidget
    m_hexagonWidget = new HexagonWidget(this);
    mainLayout->addWidget(m_hexagonWidget);

    // 添加控制滑块
    QHBoxLayout *controlsLayout = new QHBoxLayout();

    // 边长控制
    m_sizeLabel = new QLabel("边长: 50", this);
    m_sizeSlider = new QSlider(Qt::Horizontal, this);
    m_sizeSlider->setRange(10, 100);
    m_sizeSlider->setValue(50);
    connect(m_sizeSlider, &QSlider::valueChanged, this, &MainWindow::onSizeSliderChanged);
    controlsLayout->addWidget(m_sizeLabel);
    controlsLayout->addWidget(m_sizeSlider);

    // 间距控制
    m_spacingLabel = new QLabel("间距: 5", this);
    m_spacingSlider = new QSlider(Qt::Horizontal, this);
    m_spacingSlider->setRange(0, 30);
    m_spacingSlider->setValue(5);
    connect(m_spacingSlider, &QSlider::valueChanged, this, &MainWindow::onSpacingSliderChanged);
    controlsLayout->addWidget(m_spacingLabel);
    controlsLayout->addWidget(m_spacingSlider);

    mainLayout->addLayout(controlsLayout);

    setWindowTitle("蜂巢网格");
    resize(800, 600);
}

MainWindow::~MainWindow()
{
}

void MainWindow::onSizeSliderChanged(int value)
{
    m_hexagonWidget->setHexagonSize(static_cast<double>(value));
    m_sizeLabel->setText(QString("边长: %1").arg(value));
}

void MainWindow::onSpacingSliderChanged(int value)
{
    m_hexagonWidget->setHexagonSpacing(static_cast<double>(value));
    m_spacingLabel->setText(QString("间距: %1").arg(value));
}
相关推荐
赵民勇1 天前
Qt QML Component.onCompleted 和 Component.onDestruction 详解
qt
我不是8神1 天前
Qt 知识点全面总结
开发语言·qt
Lhan.zzZ1 天前
基于Qt的UDP广播发现与TCP连接系统的设计与实现
qt·tcp/ip·udp
leiming61 天前
c++ QT 开发第二天,用ui按钮点亮实体led
开发语言·qt·ui
hqwest1 天前
码上通QT实战04--主窗体布局
开发语言·css·qt·布局·widget·layout·label
leiming61 天前
c++ qt开发第一天 hello world
开发语言·c++·qt
赵民勇1 天前
QML Base Type 详解
qt
hqwest1 天前
码上通QT实战07--主窗体消息栏设计
开发语言·qt·qt事件·主窗体·stackedwidget
hqwest1 天前
码上通QT实战06--导航按钮事件
开发语言·qt·mousepressevent·qfont·qpainter·qlineargradient·setbrush
CC.GG1 天前
【Qt】常用控件----容器类控件(QGroupBox、QTabWidget )以及布局管理器
开发语言·qt