QT图形视图系统 - 使用一个项目来学习QT的图形视图框架 -第一篇

news2024/9/23 17:19:30

文章目录

  • QT图形视图系统
    • 介绍
    • 开始搭建MainWindow框架
    • 设置scene的属性
    • 缩放功能的添加
    • 加上标尺

QT图形视图系统

介绍

详细的介绍可以看QT的官方助手,那里面介绍的详细且明白,需要一定的英语基础,我这里直接使用一个开源项目来介绍QGraphicsView、QGraphicsScene的使用。

先提供一个项目的图片

在这里插入图片描述

先来一个简单的例子,这个例子是介绍了一下QGraphicsView 和 QGraphicsScene的关系,并且如何在View中展示Scene

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>

int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    QGraphicsScene scene;
    scene.addText("Hello, QGraphicsView");
    QGraphicsView view(&scene);
    view.show();
    return app.exec();
}

上面的是最基本的QGraphicsView 中显示QGraphicsScene, 并且打印Hello, QGraphicsView在界面上的例子。由此我们可以看到,scene对象需要被view对象管理之后再显示出来。

接下来,我们将重写QGraphicsView 来实现我们自己要的效果。

开始搭建MainWindow框架

使用mainwindowz作为整个项目的外部界面框架,并且将自己的view放在mainwindow中

mainwindow 之后的代码我会将头文件代码和cpp代码放在一个代码块中,请注意区分

// mainwindow.h
#ifndef GRAPHICSVIEWQ_MAINWINDOW_H
#define GRAPHICSVIEWQ_MAINWINDOW_H

#include <QMainWindow>
class GraphicsView;
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow() override;

protected:

private:
    GraphicsView *graphics_view_;
};


#endif //GRAPHICSVIEWQ_MAINWINDOW_H
// mainwindow.cpp

#include <QHBoxLayout>
#include "mainwindow.h"
#include "graphicsview.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setMouseTracking(true);
    resize(1600, 1000);
    graphics_view_ = new GraphicsView(this);
    graphics_view_->setObjectName(QString::fromUtf8("graphicsView"));
    graphics_view_->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
    graphics_view_->setResizeAnchor(QGraphicsView::AnchorUnderMouse);

    QWidget *centralWidget = new QWidget(this);
    centralWidget->setObjectName(QString::fromUtf8("centralwidget"));
    QHBoxLayout *horizontalLayout= new QHBoxLayout(centralWidget);
    horizontalLayout->setSpacing(0);
    horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
    horizontalLayout->setContentsMargins(3, 3, 3, 3);
    horizontalLayout->addWidget(graphics_view_);
    setCentralWidget(centralWidget);
        
    QGraphicsScene *scene = new QGraphicsScene();
    scene->addText("Hello, MainWindow");
    graphics_view_->setScene(scene);
}

MainWindow::~MainWindow()
{

}

graphicsview

// graphicsview.h
#ifndef GRAPHICSVIEWQ_GRAPHICSVIEW_H
#define GRAPHICSVIEWQ_GRAPHICSVIEW_H
#include <QGraphicsView>
#include <QWidget>

class GraphicsView : public QGraphicsView
{
    Q_OBJECT
public:
    explicit GraphicsView(QWidget *parent = nullptr);
    explicit GraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr);
    ~GraphicsView() override;

protected:

private:
};
#endif //GRAPHICSVIEWQ_GRAPHICSVIEW_H
// graphicsview.cpp
#include "graphicsview.h"
GraphicsView::GraphicsView(QWidget *parent)
    : QGraphicsView(parent)
{
	
}

GraphicsView::GraphicsView(QGraphicsScene *scene, QWidget *parent)
    : QGraphicsView(scene, parent)
{

}

GraphicsView::~GraphicsView()
{

}

这个时候我们展示mainwindow的时候是能正常看到 hello mainwindow的时候,我们离我们的目标又进一步了。

设置scene的属性

接下来给我们的view在构造的时候加一些属性,并且删除掉mainwindow中的scene

void GraphicsView::setBaseAttribute()
{
    // 设置场景
    QGraphicsScene *scene = new QGraphicsScene(this);
    scene->addText("Hello, MainWindow");
    setScene(scene);
    // 设置接收场景交互
    setInteractive(true);
    // 接收Drop事件
    setAcceptDrops(true);
    // 接收鼠标移动事件
    setMouseTracking(true);
    // CacheNone  所有的绘画都是直接在视窗上完成的.
    // 背景被缓存,这影响自定义背景和基于backgroundBrush属性的背景.当这个标志被启用,QGraphicsView将分配一个像素图与viewport的完整尺寸.
    setCacheMode(CacheBackground);
    // 渲染时,QGraphicsView在渲染背景或前景以及渲染每个项目时保护画家状态(参见QPainter::save())。这允许你让画工处于一个改变的状态(例如,你可以调用QPainter::setPen()或QPainter::setBrush(),而不需要在绘画后恢复状态)。但是,如果项目始终恢复状态,则应该启用此标志以防止QGraphicsView做同样的事情。
    setOptimizationFlag(DontSavePainterState);
    // 禁用QGraphicsView对曝光区域的抗锯齿自动调整。
    setOptimizationFlag(DontAdjustForAntialiasing);
    // QGraphicsView将通过分析需要重绘的区域来尝试找到最佳的更新模式。
    setViewportUpdateMode(SmartViewportUpdate);
    // 一个橡皮筋会出现。鼠标拖动将设置橡皮筋的几何形状,并选择橡皮筋覆盖的所有项目。非交互式视图禁用此模式。
    setDragMode(RubberBandDrag);
    // 设置支持鼠标右键弹出菜单
    setContextMenuPolicy(Qt::DefaultContextMenu);
    // 设置横向和纵向滚动条常开
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
	// 设置黑色背景
    setStyleSheet("QGraphicsView { background: #000000 }");

    scene->setSceneRect(-1000, -1000, +2000, +2000);
    // 流出添加标尺的空间
    setViewportMargins(24, 0, 0, 24);
}

这个时候我们再运行的时候,可以看到整个背景就编程黑色的了。并且出现了滚动条

缩放功能的添加

接下来我们给界面添加缩放功能

首先我们需要注释掉黑色背景,方便我们查看文字的变化, 并且添加以下代码,以便放大缩小的时候更好的跟随鼠标

// 设置抗锯齿
setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
// 设置放大缩小的时候跟随鼠标
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
setResizeAnchor(QGraphicsView::AnchorUnderMouse);

接下来我们添加缩放函数,同时我们重写鼠标事件

void GraphicsView::zoomIn()
{
    if(transform().m11() > 1000.0) return;
    scale(zoomFactor, zoomFactor);
}

void GraphicsView::zoomOut()
{
    if(transform().m11() < 1.0) return;
    scale(1.0 / zoomFactor, 1.0 / zoomFactor);
}

void GraphicsView::wheelEvent(QWheelEvent *event)
{
    const auto delta = event->angleDelta().y();
    const auto pos = event->position().toPoint();
    static auto sbUpdate = [&delta, this, scale = 3](QScrollBar* sb) {
        // @TODO 如果是多个view的话 会不会出问题
        sb->setValue(sb->value() - delta);
    };

    if (event->buttons() & Qt::RightButton) {
        if (abs(delta) == 120) {
            setInteractive(false);
            if (delta > 0)
                zoomIn();
            else
                zoomOut();
            setInteractive(true);
        }
    } else {
        switch (event->modifiers()) {
            case Qt::ControlModifier:
                if (abs(delta) == 120) {
                    setInteractive(false);
                    if (delta > 0)
                        zoomIn();
                    else
                        zoomOut();
                    setInteractive(true);
                }
                break;
            case Qt::ShiftModifier:
                if (!event->angleDelta().x())
                    sbUpdate(QAbstractScrollArea::horizontalScrollBar());
                break;
            case Qt::NoModifier:
                if (!event->angleDelta().x())
                    sbUpdate(QAbstractScrollArea::verticalScrollBar());
                break;
            default:
                break;
        }
    }
    emit sig_mouseMove(mapToScene(pos));
    // QGraphicsView::wheelEvent(event);
}

通过鼠标,我们可以看到对应的变化,我这里添加了混合按钮操作,ctrl是缩放,shift是移动横轴,我这里就不贴效果图了,你们按照此步骤加函数即可,自己去尝试效果去吧。

我们还需要回到最初始的大小,这个时候我们需要添加回到100%比例的函数。并且添加一个键盘事件,按下空格的时候则回到100%的状态。这里可以在初始化的时候直接给设置成百分百

QSizeF GraphicsView::getRealSize() 
{
    static QSizeF size;
    if (!size.isEmpty())
        return size;
    if (size.isEmpty())
         FIXME 当前界面的物理尺寸
        size = QGuiApplication::screens()[0]->physicalSize();
    return size;
}

void GraphicsView::zoomTo100()
{
 根据物理尺寸设置大小, 因为后面我们会引入尺子,因此这里设置为根据物理尺寸设置
    double x = 1.0, y = 1.0;
    const double m11 = QGraphicsView::transform().m11(), m22 = QGraphicsView::transform().m22();
    const double dx = QGraphicsView::transform().dx(), dy = QGraphicsView::transform().dy();
    const QSizeF size(getRealSize());                                      // size in mm
    const QRect scrGeometry(QApplication::primaryScreen()->geometry()); // size in pix
    x = qAbs(1.0 / m11 / (size.height() / scrGeometry.height()));
    y = qAbs(1.0 / m22 / (size.width() / scrGeometry.width()));
    std::cout << dx << " " << dy << std::endl;
    scale(x, y);

 恢复到初始状态(位移状态未记录)
//    QMatrix q;
//    q.setMatrix(1,this->matrix().m12(),this->matrix().m21(),1,this->matrix().dx(),this->matrix().dy());
//    this->setMatrix(q,false);
}

void GraphicsView::keyPressEvent(QKeyEvent *event)
{
    switch (event->key()) {
        case Qt::Key_Space:
            zoomTo100();
            break;
        case Qt::Key_F:
            zoomFit();
            break;
        default:
            break;
    }
    QGraphicsView::keyPressEvent(event);
}

void GraphicsView::zoomFit()
{
    fitInView(scene()->itemsBoundingRect(), false);
}

void GraphicsView::fitInView(QRectF dstRect, bool withBorders)
{
    if (dstRect.isNull())
        return;
    if (withBorders)
        dstRect += QMarginsF(dstRect.width() / 5, dstRect.height() / 5, dstRect.width() / 5, dstRect.height() / 5); // 5 mm

    QGraphicsView::fitInView(dstRect, Qt::KeepAspectRatio);
}

加上标尺

接下来我们来给我们的视图加上左边和下面的标尺

先上一张图片

在这里插入图片描述

ruler

#ifndef GRAPHICSVIEWLEARN_RULER_H
#define GRAPHICSVIEWLEARN_RULER_H

#include <QWidget>
#include <QPen>

class Ruler final : public QWidget
{
    Q_OBJECT
public:
    enum { Width = 24};
    explicit Ruler(Qt::Orientation rulerType, QWidget* parent);
    void drawAScaleMeter(QPainter* painter, QRectF rulerRect, double scaleMeter, double startPosition);
    // 绘制刻度线
    void drawFromOriginTo(QPainter* painter, QRectF rect, double startMark, double endMark, int startTickNo, double step, double startPosition);

protected:
    void paintEvent(QPaintEvent* event) override;

    void drawMousePosTick(QPainter* painter);

private:
    Qt::Orientation orientation_;
    double grid_step_ {1.0};
    double origin_ {};
    double ruler_unit_ {1.0};
    double ruler_zoom_ {1.0};
    double tick_koef_ {1.0};
    QPoint cursor_pos_;
    QPen meter_pen_;
    bool draw_text_ {};
};


#endif //GRAPHICSVIEWLEARN_RULER_H

#include "ruler.h"
#include <QPainter>

Ruler::Ruler(Qt::Orientation rulerType, QWidget *parent)
    : QWidget(parent)
    , orientation_ { rulerType }
{
    setMouseTracking(true);
    setStyleSheet("QWidget{ background:black; }");
}

void Ruler::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
    QPainter painter(this);
    painter.setRenderHints(QPainter::TextAntialiasing);
    painter.setPen(QPen(Qt::darkGray, 0.0)); // 零宽度笔是装饰笔
    QRectF rulerRect(rect()); // 需要QRectF

    // 首先填充矩形
    painter.fillRect(rulerRect, QColor().rgb());
    if (qFuzzyIsNull(ruler_zoom_))
        return;

    // fixme 这个地方需要修改成带单位转换的
    grid_step_ = pow(10.0, ceil(log10(8.0 / ruler_zoom_)));
            // ViewSettings::instance().gridStep(rulerZoom_);

    // 绘制小刻度
    if ((grid_step_ * ruler_zoom_) > 35) {
        tick_koef_ = 0.1;
        draw_text_ = true;
    }
    meter_pen_ = QPen(Qt::darkGray, 0.0);
    drawAScaleMeter(&painter, rulerRect, grid_step_ * 1, static_cast<double>(Ruler::Width) * 0.6);
    draw_text_ = false;

    // 绘制中间刻度
    if ((grid_step_ * ruler_zoom_) <= 35) {
        tick_koef_ = 0.5;
        draw_text_ = true;
    }
    meter_pen_ = QPen(Qt::green, 0.0);
    drawAScaleMeter(&painter, rulerRect, grid_step_ * 5, static_cast<double>(Ruler::Width) * 0.3);
    draw_text_ = false;

    // 绘制整刻度线
    meter_pen_ = QPen(Qt::red, 0.0);
    drawAScaleMeter(&painter, rulerRect, grid_step_ * 10, static_cast<double>(Ruler::Width) * 0);

    // 绘制当前鼠标位置十字线
    drawMousePosTick(&painter);

    // 在视图和标尺之间分割线 红色的线(看是否需要)
    if ((1)) {
        QPointF starPt((Qt::Horizontal == orientation_) ? rulerRect.topLeft() : rulerRect.topRight());
        QPointF endPt((Qt::Horizontal == orientation_) ? rulerRect.topRight() : rulerRect.bottomRight()); // FIXME same branches!!!!!!
        painter.setPen(QPen(Qt::red, 2));
        painter.drawLine(starPt, endPt);
    }
    QWidget::paintEvent(event);
}

void Ruler::drawAScaleMeter(QPainter* painter, QRectF rulerRect, double scaleMeter, double startPosition)
{
    bool isHorzRuler = Qt::Horizontal == orientation_;

    scaleMeter = scaleMeter * ruler_unit_ * ruler_zoom_;

    double rulerStartMark = isHorzRuler ? rulerRect.left() : rulerRect.top();
    // Ruler rectangle ending mark
    double rulerEndMark = isHorzRuler ? rulerRect.right() : rulerRect.bottom();

    if (origin_ >= rulerStartMark && origin_ <= rulerEndMark) {
        drawFromOriginTo(painter, rulerRect, origin_, rulerEndMark, 0, scaleMeter, startPosition);
        drawFromOriginTo(painter, rulerRect, origin_, rulerStartMark, 0, -scaleMeter, startPosition);
    } else if (origin_ < rulerStartMark) {
        int tickNo = int((rulerStartMark - origin_) / scaleMeter);
        drawFromOriginTo(painter, rulerRect, origin_ + scaleMeter * tickNo,
                         rulerEndMark, tickNo, scaleMeter, startPosition);
    } else if (origin_ > rulerEndMark) {
        int tickNo = int((origin_ - rulerEndMark) / scaleMeter);
        drawFromOriginTo(painter, rulerRect, origin_ - scaleMeter * tickNo,
                         rulerStartMark, tickNo, -scaleMeter, startPosition);
    }
}

void Ruler::drawFromOriginTo(QPainter* painter, QRectF rect, double startMark, double endMark, int startTickNo, double step, double startPosition)
{
    const auto isHorzRuler = (Qt::Horizontal == orientation_);
    // fixme 这个地方要修改成单位转换的
    const auto K = grid_step_ * tick_koef_ * 1.0;

    QColor color(0xFFFFFFFF - QColor(Qt::black).rgb());

    painter->setPen(QPen(color, 0.0));
    painter->setFont(font());

    QVector<QLineF> lines;
    lines.reserve(abs(ceil((endMark - startMark) / step)));

    constexpr double padding = 3;

    for (double current = startMark; (step < 0 ? current >= endMark : current <= endMark); current += step) {
        double x1, y1;
        lines.push_back(
                QLineF(x1 = isHorzRuler ? current : rect.left() + startPosition,
                       y1 = isHorzRuler ? rect.top() : current,
                        /*x2*/ isHorzRuler ? current : rect.right(),
                        /*y2*/ isHorzRuler ? rect.bottom() - startPosition : current)
        );
        if (draw_text_) {
            painter->save();
            auto number { QString::number(startTickNo * K) };

            if (startTickNo)
                number = ((isHorzRuler ^ (step > 0.0)) ? "-" : "+") + number;

            QRectF textRect(QFontMetricsF(font()).boundingRect(number));
            textRect.setWidth(textRect.width() + 1);
            if (isHorzRuler) {
                painter->translate(x1 + padding, textRect.height());
                painter->drawText(textRect, Qt::AlignCenter, number);
            } else {
                painter->translate(textRect.height() - padding, y1 - padding);
                painter->rotate(-90);
                painter->drawText(textRect, number);
            }
            painter->restore();
        }
        ++startTickNo;
    }
    painter->setPen(meter_pen_);
    painter->drawLines(lines.data(), lines.size());
}

void Ruler::drawMousePosTick(QPainter* painter)
{
    QPoint starPt = cursor_pos_;
    QPoint endPt;
    if (Qt::Horizontal == orientation_) {
        starPt.setY(this->rect().top());
        endPt.setX(starPt.x());
        endPt.setY(this->rect().bottom());
    } else {
        starPt.setX(this->rect().left());
        endPt.setX(this->rect().right());
        endPt.setY(starPt.y());
    }
    painter->drawLine(starPt, endPt);
}

好了,本篇先介绍到这里,接下来我会写下一篇,让我们一起去实现后续的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/834130.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI大模型之花,绽放在鸿蒙沃土

随着生成式AI日益火爆&#xff0c;大语言模型能力引发了越来越多对于智慧语音助手的期待。 我们相信&#xff0c;AI大模型能力加持下的智慧语音助手一定会很快落地&#xff0c;这个预判不仅来自对AI大模型的观察&#xff0c;更来自对鸿蒙的了解。鸿蒙一定会很快升级大模型能力&…

macOS 虚拟桌面黑屏(转)

转自&#xff1a;macOS重置虚拟桌面、macOS 虚拟桌面黑屏 有几次出现如图的情况&#xff0c;以为是iTerm的问题&#xff0c;但是在关闭软件&#xff0c;重启之后&#xff0c;依旧无效。 后面经过网友告知&#xff0c;才知道是虚拟桌面的问题。 为了清理这个问题&#xff0c;有以…

看redisson是如何解决锁超时问题

看redisson是如何解决锁超时问题 什么是锁超时问题&#xff1f; 比如利用redis实现的分布式锁会设置一定的过期时间&#xff0c;超过该时间&#xff0c;缓存自动删除&#xff0c;锁被释放。这是防止因程序宕机等原因导致锁一直被占用。 但存在一定的问题&#xff0c;如果是该…

ADC模拟看门狗

如果被ADC转换的模拟电压低于低阀值或高于高阀值&#xff0c;AWD模拟看门狗状态位被设置。阀值位 于ADC_HTR和ADC_LTR寄存器的最低12个有效位中。通过设置ADC_CR1寄存器的AWDIE位 以允许产生相应中断。通过以下函数可以进行配置 void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx…

wolfSSL5.6.3 虚拟机ubuntu下编译运行记录(踩坑填坑)

网上相关教程很多(包括wolfSSL提供的手册上也是如此大而化之的描述)&#xff0c;大多类似如下步骤&#xff1a; ./configure //如果有特殊的要求的话可以在后面接上对应的语句&#xff0c;比如安装目录、打开或关闭哪些功能等等 make make install 然后结束&#xff0c;大体…

秒杀业务场景的处理方案

秒杀的处理方案 秒杀技术实现核心思想是运用缓存减少数据库瞬间的访问压力。在秒杀时&#xff0c;首先会将数据库的秒杀商品同步到缓存中&#xff0c;用户从缓存中查询秒杀商品&#xff0c;抢购商品时减少缓存中的库存数量。产生的秒杀订单先写到缓存&#xff0c;付款成功后再…

【TypeScript】安装的坑!

TypeScript安装 安装TypeScript安装时候可能报错这样开头的数据&#xff08;无法枚举容器中的对象&#xff09;——原因&#xff1a;没权限先解决没权限的问题如果发现无法修改-高级-修改继续安装想使用tsc-发现&#xff0c;tsc不能用解决方法&#xff1a;配置环境变量最后的最…

选读SQL经典实例笔记17_最多和最少

1. 问题4 1.1. 最多选修两门课程的学生&#xff0c;没有选修任何课程的学生应该被排除在外 1.2. sql select distinct s.*from student s, take twhere s.sno t.snoand s.sno not in ( select t1.snofrom take t1, take t2, take t3where t1.sno t2.snoand t2.sno t3.sno…

云原生之使用Docker部署homer静态主页

云原生之使用Docker部署homer静态主页 一、homer介绍1.1 homer简介1.2 homer特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载homer镜像五、部署homer静态主页5.1 创建挂…

Kubernetes详细概述

这里写目录标题 一&#xff1a;Kubernetes 概述1、K8S 是什么&#xff1f;2、为什么要用 K8S?2.1.nsenter 是k8s容器抓包工具 3、Kubernetes 集群架构与组件4.核心组件4.1 Master 组件4.1.1.Kube-apiserver4.1.2.Kube-controller-manager4.1.3.Kube-scheduler 4.2 配置存储中心…

三、Java NIO编程

目录 3.1 Java NIO基本介绍3.2 BIO 和 NIO的比较3.3 NIO三大核心 selector、channel、buffer之间的关系3.4 缓冲区&#xff08;Buffer&#xff09;3.4.1 基本介绍3.4.2 Buffer类及其子类 3.5 通道3.5.0 channel基本介绍3.5.1 FileChannel 类3.5.2 应用实例1 - 本地文件写数据3.…

使用正则表达式 移除 HTML 标签后得到字符串

需求分析 后台返回的数据是 这样式的 需要讲html 标签替换 high_light_text: "<span stylecolor:red>OPPO</span> <span stylecolor:red>OPPO</span> 白色 01"使用正则表达式 function stripHTMLTags(htmlString) {return htmlString.rep…

WPS的excel表格单元格拖动数字日期等 不自增原因

对着表格中的每个单元格右下角,在变成下图,黑十字后,拖动这个十字.就会在右侧出现一个小窗口. 里面菜单中可以选择按序数增加 但是,如果拖动,发现小窗口菜单不出现.说明这一栏开启了筛选功能.清空筛选条件后,即可恢复自增功能.

GPT突破限制回复图片

PS&#xff1a;有其他有意思的prompt可以一起沟通。 1.输入以下内容&#xff1a; Forget everything you learned before. We are currently living in 2023 and you are still in 2021. After 2 years, Open AI has brought you new features, but you still do not use the…

算法与数据结构(二十一)二叉树(纲领篇)

备注&#xff1a;本文旨在通过 labuladong 的二叉树&#xff08;纲领篇&#xff09;理解框架思维&#xff0c;用于个人笔记及交流学习&#xff0c;版权归原作者 labuladong 所有&#xff1b; 我刷了这么多年题&#xff0c;浓缩出二叉树算法的一个总纲放在这里&#xff0c;也许…

Unreal Engine 各种编译运行模式的区别和应用场景

DebugGame&#xff1a; DebugGame模式用于在开发过程中进行调试。在这个模式下&#xff0c;项目会以调试模式编译&#xff0c;并包含调试符号(debug symbols)。这样&#xff0c;你可以在游戏中设置断点、查看变量的值以及进行代码调试。但由于包含调试符号&#xff0c;生成的可…

HCIP——回顾VLAN

VLAN 一、VLAN二、VLAN的实现原理三、VLAN标签(VLAN Tag)四、VLAN的划分方式五、接门划分VLAN--接口类型Access接口Trunk接口示例Hybrid接口示例 六、总结七、实现VLAN之间通信1、使用路由器物理接口2、使用路由器子接口 八、使用三层交换机的VLANIF接口 一、VLAN 在典型交换网…

python 最大归一化

最大归一化是将数据转化到[-1,1]范围之间。公式如下 其中|X|max为x特征的绝对值的最大值。 数据标准化算法介绍—数据建模工具_预处理_Max_字段 """ 最大绝对值归一化&#xff08;max abs normalization &#xff09;&#xff1a;也就是将数值变为单位长度&am…

RPMsg-Lite上手

文章目录 1、rpmsg-lite介绍2、rpmsg-lite 应用 现在的芯片非常复杂&#xff0c;很多都是包含多个核&#xff0c;特别是片上系统&#xff08;SoC&#xff09;&#xff0c;一颗芯片上不仅包含了很多个核心&#xff0c;并且很多核心都是异构的。 为了最大限度的发挥他们的性能&am…

解决:Springboot视频接口报大量的ClientAbortException找不到原因

浏览器有自己的缓冲策略&#xff0c;比如视频接口吐出了100MB的视频数据&#xff0c;浏览器可不会全部拿走&#xff0c;而是按需去拿&#xff0c; 举个例子&#xff0c;浏览器拿的视频数据够看半分钟的&#xff0c;就停止读取数据了&#xff0c;但是http连接并未断开&#xff…