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));
}
相关推荐
用户805533698037 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner8 小时前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz5 天前
QML Hello World 入门示例
qt
xcyxiner8 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner9 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner9 天前
DicomViewer (添加模型类)3
qt
xcyxiner10 天前
DicomViewer (目录调整) 2
qt
xcyxiner10 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能12 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G12 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt