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

文章目录

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql高级(尚硅谷-夏磊)

目录 内容介绍 Linux下MySQL的安装与使用 Mysql逻辑架构 Mysql存储引擎 Sql预热 索引简介 内容介绍 1、Linux下MySQL的安装与使用 2、逻辑架构 3、sql预热 Linux下MySQL的安装与使用 1、docker安装docker run -d \-p 3309:3306 \-v /atguigu/mysql/mysql8/conf:/etc/my…

一百四十二、Linux——查看Linux服务器架构的版本类型

一、目的 查看已经安装好的Linux服务器架构的版本类型&#xff0c;看服务器版本是32位还是64位 而且可以区分出是kettle的文件x86或x86_64&#xff0c;x86是32位&#xff0c;而x86_64是64位 注意&#xff1a; 32位的查询结果为i386、i686 64位的查询结果为x86_64 二、Linu…

Java阶段五Day20

Java阶段五Day20 文章目录 Java阶段五Day20项目推进完成订单OrderServerService && MessageTransSenderRepositoryMessageTransRepoImplLocalTransactionLisetner 结算订单业务流程图远程调用 画时序图 项目推进 完成订单 通过分布式消息事务解决本地事务和发消息的一…

python+django+mysql项目实践三(用户管理)

python项目实践 环境说明: Pycharm 开发环境 Django 前端 MySQL 数据库 Navicat 数据库管理 用户列表展示 urls view models html <!DOCTYPE html> <html

MySQL 重置root 密码

5.7 版本 首先要把服务mysql57 关闭 net stop MySQL57 在安装的mysql57的程序的bin中 运行cmd&#xff08;管理员运行&#xff09; mysqld --defaults-file‘mysql存放数据的位置\my.ini’ --skip-grant-tables 上图 错误 注意&#xff1a;如果遇到mysqld: Can’t change dir…

答辩PPT怎么做?在线PPT软件哪个好?

又是一年毕业季&#xff0c;相信很多毕业生都开始准备论文答辩&#xff0c;有些同学正在为论文奋夜苦战&#xff0c;有些则是为论文答辩PPT而烦恼。做PPT要用什么软件好呢&#xff1f;这篇文章就来告诉你。 当下有很多PPT制作工具&#xff0c;其中自然也包括Office三件套。这些…

vue- form动态表单验证规则-表单验证

前言 以element官网的form表单的-动态增减表单项为例讲解表单验证规则 动态的功能就是v-model配合push v-for 便利来实现的 我们需要熟知2个知识点prop表单验证需要跟v-model绑定的值是一样的&#xff0c; 如果是一个数组便利的表单&#xff0c;那就需要绑定这个数组每一项…

【基础类】—CSS盒模型的全面认识

一、基本概念&#xff1a;标准IE模型 盒模型&#xff1a;margin border padding content 标准模型&#xff1a;将元素的宽度和高度仅计算为内容区域的尺寸&#xff08;content-box&#xff0c;默认&#xff09; 当CSS盒模型为 标准盒模型 &#xff08;box-sizing: conten…

交通运输安全大数据分析解决方案

当前运输市场竞争激烈&#xff0c;道路运输企业受传统经营观念影响&#xff0c;企业管理者安全意识淡薄&#xff0c;从业人员规范化、流程化的管理水平较低&#xff0c;导致制度规范在落实过程中未能有效监督与管理&#xff0c;执行过程中出现较严重的偏差&#xff0c;其营运车…

CSS图片放到<div>里面,自适应宽高全部显示,点击图片跳到新页面预览,点击旋转按钮图片可旋转

有一个需求是图片放到一个固定宽高的<div>里面&#xff0c;不管是横图还是竖图&#xff0c;都要全部显示出来并且保持图片的长宽比例不变形&#xff0c;点击图片可以跳到一个新页面预览&#xff0c;代码如下&#xff1a; <!DOCTYPE html> <html> <head>…

harbor搭建

回到目录 Harbor 是 VMware 公司开源的企业级 Docker Registry 项目&#xff0c;其目标是帮助用户迅速搭建一个企业级的 Docker Registry 服务 通俗的讲&#xff0c;harbor是一个私人镜像存储服务器 1 下载安装 进入官网&#xff0c;下载一个离线安装包,harbor官网下载 这…

Java基础面试题3

Java基础面试题 1&#xff1a;https://cloud.fynote.com/share/d/qPGzAVr5 2&#xff1a;https://cloud.fynote.com/share/d/MPG9AVsAG 3&#xff1a;https://cloud.fynote.com/share/d/qPGHKVsM 一、JavaWeb专题 1.HTTP响应码有哪些 1、1xx&#xff08;临时响应&#xf…

企业微信v4.1.8 版本新功能介绍

一、效率工具与基础体验优化 邮件 1、邮件安全 当邮件发件人、邮件内容存在风险时&#xff0c;将提示风险并自动隐藏邮件中的图片&#xff0c;避免轻信邮件内容。 对存在伪造风险的昵称将直接以发件地址代替展示&#xff0c;减少误导。 对邮件中的包含钓鱼欺诈、病毒或恶意软…

MySQL索引2——索引的类型

目录 索引分类总结 BTree索引结构根据存储形式分类 聚集索引&#xff08;Clustered Index&#xff09; 二级索引&#xff08;Secondary Index&#xff09; 根据索引特征分类 主键索引——一定是聚集索引 唯一索引——可以是聚集索引&#xff0c;也可以是二级索引 常规索…

【雷达通信】非相干多视处理(CSA)(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

MVC配置原理

如果你想保存springboot的mvc配置并且还想自己添加自己的配置就用这个。 视图解析器原理&#xff0c;它会从IOC容器里获取配置好视图解析器的配置类里的视图解析器集合&#xff0c; 然后遍历集合&#xff0c;生成一个一个的视图对象&#xff0c;放入候选 视图里&#xff0c;…

Spring Boot集成单元测试调用dao,service

文章目录 Spring Boot集成单元测试调用dao&#xff0c;service1 添加相关依赖2 新建测试类 Spring Boot集成单元测试调用dao&#xff0c;service 1 添加相关依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-st…

理解 CSS 中的 Containing Block

前言 在开始本文之前先来看一个例子&#xff0c;下面一段简单的 html 代码&#xff0c;布局很简单&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"w…

Clickhouse 优势与部署

一、clickhouse简介 1.1clickhouse介绍 ClickHouse的背后研发团队是俄罗斯的Yandex公司&#xff0c;2011年在纳斯达克上市&#xff0c;它的核心产品是搜索引擎。我们知道&#xff0c;做搜索引擎的公司营收非常依赖流量和在线广告&#xff0c;所以做搜索引擎的公司一般会并行推…

IDEA用Gradle构建项目时,lombok插件无效的解决办法

Lombok 可用来帮助开发人员消除 Java 的重复代码&#xff0c;尤其是对于简单的 Java 对象&#xff08;POJO&#xff09;&#xff0c;比如说getter/setter/toString等方法的编写。它通过注解实现这一目的。 正确使用姿势 一、安装Lombok插件 菜单栏File -> Settings ->…