基于QCustomPlot实现色条(ColorBar)

news2024/11/13 9:21:44

一、简介

通过QCustomPlot实现ColorBar,直观显示各个位置的异常情况。实现效果如下,
在这里插入图片描述

二、源码

  1. CPColorBar.hpp
// CPColorBar.hpp
#pragma once
#include "qcustomplot.h"

#include <QHash>

class QCP_LIB_DECL CPColorBarData {
public:
    CPColorBarData() : key(0), value(0) {}

    CPColorBarData(double key, double value) : key(key), value(value) {}

    inline double sortKey() const {
        return key;
    }
    inline static CPColorBarData fromSortKey(double sortKey) {
        return CPColorBarData{sortKey, 0};
    }
    inline static bool sortKeyIsMainKey() {
        return true;
    }

    inline double mainKey() const {
        return key;
    }
    inline double mainValue() const {
        return value;
    }

    inline QCPRange valueRange() const {
        return QCPRange{value, value};
    }

    double key, value;
};
Q_DECLARE_TYPEINFO(CPColorBarData, Q_PRIMITIVE_TYPE);


typedef QCPDataContainer<CPColorBarData> QCPColorBarDataContainer;

class QCP_LIB_DECL CPColorBar : public QCPAbstractPlottable1D<CPColorBarData> {
    Q_OBJECT
    Q_PROPERTY(double width READ width WRITE setWidth)

public:
    explicit CPColorBar(QCPAxis* keyAxis, QCPAxis* valueAxis);
    virtual ~CPColorBar() Q_DECL_OVERRIDE;

    // getters
    double width() const {
        return mWidth;
    }
    QSharedPointer<QCPColorBarDataContainer> data() const {
        return mDataContainer;
    }

    // setters
    void setData(QSharedPointer<QCPColorBarDataContainer> data);
    void setData(const QVector<double>& keys, const QVector<double>& values, bool alreadySorted = false);
    void setWidth(double width);
    void setColor(const QHash<int, QColor>& color);

    // 非属性方法
    void addData(const QVector<double>& keys, const QVector<double>& values, bool alreadySorted = false) const;
    void addData(double key, double value);

    // 重新实现的虚方法
    virtual QCPDataSelection selectTestRect(const QRectF& rect, bool onlySelectable) const Q_DECL_OVERRIDE;
    virtual double selectTest(
        const QPointF& pos, bool onlySelectable, QVariant* details = nullptr) const Q_DECL_OVERRIDE;
    virtual QCPRange getKeyRange(bool& foundRange, QCP::SignDomain inSignDomain = QCP::sdBoth) const Q_DECL_OVERRIDE;
    virtual QCPRange getValueRange(bool& foundRange, QCP::SignDomain inSignDomain = QCP::sdBoth,
        const QCPRange& inKeyRange = QCPRange()) const Q_DECL_OVERRIDE;
    virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE;

protected:
    // 属性成员
    double mWidth;
    QHash<int, QColor> mColor;

    virtual void draw(QCPPainter* painter) Q_DECL_OVERRIDE;
    virtual void drawLegendIcon(QCPPainter* painter, const QRectF& rect) const Q_DECL_OVERRIDE;

    void getVisibleDataBounds(
        QCPColorBarDataContainer::const_iterator& begin, QCPColorBarDataContainer::const_iterator& end) const;

    friend class QCustomPlot;

private:
    void getOptimizedBarData(QVector<CPColorBarData>* barData, const QCPColorBarDataContainer::const_iterator& begin,
        const QCPColorBarDataContainer::const_iterator& end) const;

    bool mAdaptiveSampling; // 自适应采样
};

  1. CPColorBar.cpp
// CPColorBar.cpp
#include "CPColorBar.hpp"

#include <algorithm>
#include <limits>

CPColorBar::CPColorBar(QCPAxis* keyAxis, QCPAxis* valueAxis)
    : QCPAbstractPlottable1D<CPColorBarData>(keyAxis, valueAxis), mWidth{1}, mAdaptiveSampling{true} {
    // 修改从抽象绘图表继承的属性
    mPen.setColor(Qt::blue);
    mPen.setStyle(Qt::SolidLine);
    mBrush.setColor(QColor(40, 50, 255, 30));
    mBrush.setStyle(Qt::SolidPattern);
    mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));
}

CPColorBar::~CPColorBar() = default;

void CPColorBar::setData(QSharedPointer<QCPColorBarDataContainer> data) {
    mDataContainer = data;
}

void CPColorBar::setData(const QVector<double>& keys, const QVector<double>& values, bool alreadySorted) {
    mDataContainer->clear();
    addData(keys, values, alreadySorted);
}

// 设置条的宽度
void CPColorBar::setWidth(double width) {
    mWidth = width;
}

void CPColorBar::setColor(const QHash<int, QColor>& color) {
    mColor = color;
}

void CPColorBar::addData(const QVector<double>& keys, const QVector<double>& values, bool alreadySorted)const {
    if (keys.size() != values.size())
        qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
    const int n = qMin(keys.size(), values.size());
    QVector<CPColorBarData> tempData(n);
    QVector<CPColorBarData>::iterator it = tempData.begin();
    const QVector<CPColorBarData>::iterator itEnd = tempData.end();
    int i = 0;
    while (it != itEnd) {
        it->key = keys[i];
        it->value = values[i];
        ++it;
        ++i;
    }
    mDataContainer->add(tempData, alreadySorted); // 请勿修改 tempData 以防止写入时复制
}

void CPColorBar::addData(double key, double value) {
    mDataContainer->add(CPColorBarData(key, value));
}

QCPDataSelection CPColorBar::selectTestRect(const QRectF& rect, bool onlySelectable) const {
    QCPDataSelection result;
    return result;
}

double CPColorBar::selectTest(const QPointF& pos, bool onlySelectable, QVariant* details) const {
    return -1;
}

QCPRange CPColorBar::getKeyRange(bool& foundRange, QCP::SignDomain inSignDomain) const {
    return mDataContainer->keyRange(foundRange, inSignDomain);
}

QCPRange CPColorBar::getValueRange(bool& foundRange, QCP::SignDomain inSignDomain, const QCPRange& inKeyRange) const {
    return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
}

QPointF CPColorBar::dataPixelPosition(int index) const {
    if (index >= 0 && index < mDataContainer->size()) {
        QCPAxis* keyAxis = mKeyAxis.data();
        QCPAxis* valueAxis = mValueAxis.data();
        if (!keyAxis || !valueAxis) {
            qDebug() << Q_FUNC_INFO << "invalid key or value axis";
            return {};
        }

        const QCPDataContainer<CPColorBarData>::const_iterator it = mDataContainer->constBegin() + index;
        const double valuePixel = valueAxis->coordToPixel(it->value);
        const double keyPixel = keyAxis->coordToPixel(it->key);
        if (keyAxis->orientation() == Qt::Horizontal)
            return {keyPixel, valuePixel};
        else
            return {valuePixel, keyPixel};
    } else {
        qDebug() << Q_FUNC_INFO << "Index out of bounds" << index;
        return {};
    }
}

/* 从基类继承文档 */
void CPColorBar::draw(QCPPainter* painter) {
    if (!mKeyAxis || !mValueAxis) {
        qDebug() << Q_FUNC_INFO << "invalid key or value axis";
        return;
    }
    if (mDataContainer->isEmpty())
        return;

    QCPColorBarDataContainer::const_iterator begin, end;
    getVisibleDataBounds(begin, end);

    // 绘制片段
    mDataContainer->limitIteratorsToDataRange(begin, end, QCPDataRange(0, dataCount()));

    if (begin == end || begin + 1 == end)
        return;

    QVector<CPColorBarData> barData;

    getOptimizedBarData(&barData, begin, end);
    if (barData.empty())
        return;

    begin = barData.constBegin();
    end = barData.constEnd();

    QCPAxis* keyAxis = mKeyAxis.data();
    QCPAxis* valueAxis = mValueAxis.data();
    const QCPRange valueRange = valueAxis->range();
    const int top = valueAxis->coordToPixel(valueRange.upper);
    const int bottom = valueAxis->coordToPixel(valueRange.lower);
    double currentStartKey{begin->key};
    int currentStartValue{static_cast<int>(begin->value)};
    for (QCPColorBarDataContainer::const_iterator it = begin; it != end; ++it) {
        // 如果设置了标志,则检查数据有效性:
#ifdef QCUSTOMPLOT_CHECK_DATA
        if (QCP::isInvalidData(it->key, it->value))
            qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range invalid."
                     << "Plottable name:" << name();
#endif
        const bool value_changed = static_cast<int>(it->value) != currentStartValue;
        if (value_changed || it + 1 == end) {
            const double endMiddleKey = value_changed ? ((it - 1)->key + it->key) * 0.5 : it->key; // 取跃变的中间位置
            // 着色绘制
            const int startPix = keyAxis->coordToPixel(currentStartKey);
            const int endPix = keyAxis->coordToPixel(endMiddleKey);

            if (endPix - startPix >= 1) {
                const auto iter = mColor.find(currentStartValue);
                const QColor color = iter != mColor.cend() ? iter.value() : Qt::black;
                painter->setPen(color);
                painter->setBrush(QBrush(color));
                painter->drawRect(startPix, top, endPix - startPix, bottom - top);
            }

            currentStartKey = endMiddleKey;
            currentStartValue = static_cast<int>(it->value);

            if (value_changed && it + 1 == end) {
                --it;
            }
        }
    }

    // 绘制不只是线条/散点笔和画笔的其他选择装饰
    if (mSelectionDecorator)
        mSelectionDecorator->drawDecoration(painter, selection());
}

void CPColorBar::getOptimizedBarData(QVector<CPColorBarData>* barData,
    const QCPColorBarDataContainer::const_iterator& begin, const QCPColorBarDataContainer::const_iterator& end) const {
    if (!barData)
        return;
    QCPAxis* keyAxis = mKeyAxis.data();
    QCPAxis* valueAxis = mValueAxis.data();
    if (!keyAxis || !valueAxis) {
        qDebug() << Q_FUNC_INFO << "invalid key or value axis";
        return;
    }
    if (begin == end)
        return;

    const int dataCount = static_cast<int>(end - begin);
    int maxCount = (std::numeric_limits<int>::max)();
    if (mAdaptiveSampling) {
        // 所选key区间所对应的像素宽度
        const double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key) - keyAxis->coordToPixel((end - 1)->key));
        if (2 * keyPixelSpan + 2 < static_cast<double>((std::numeric_limits<int>::max)()))
            maxCount = static_cast<int>(2 * keyPixelSpan + 2); // 最大点数为区间像素的两倍
    }

    barData->reserve(maxCount * 1.05);
    if (mAdaptiveSampling && dataCount >= maxCount) { // 仅当平均每个像素至少有两个点时才使用自适应采样
        QCPColorBarDataContainer::const_iterator it = begin;
        double maxValue = it->value;
        QCPColorBarDataContainer::const_iterator currentIntervalFirstPoint = it;
        const int reversedFactor = keyAxis->pixelOrientation(); // 用于计算 keyEpsilon 像素到正确的方向
        const int reversedRound = reversedFactor == -1 ? 1 : 0;
        double currentIntervalStartKey =
            keyAxis->pixelToCoord(static_cast<int>(keyAxis->coordToPixel(begin->key) + reversedRound));
        double lastIntervalEndKey = currentIntervalStartKey;
        // 映射到绘图键坐标时屏幕上一个像素的间隔
        double keyEpsilon =
            qAbs(currentIntervalStartKey
                 - keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey) + 1.0 * reversedFactor));
        // 指示是否需要在每个间隔后更新 keyEpsilon(对于对数轴)
        const bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic;
        int intervalDataCount = 1;
        ++it; // 将迭代器提前到第二个数据点,因为自适应采样在 1 点回溯中起作用
        while (it != end) {
            // 数据点仍在同一像素内,因此请跳过它并在必要时扩展此集群的值范围
            if (it->key < currentIntervalStartKey + keyEpsilon) {
                if (it->value > maxValue)
                    maxValue = it->value;
                ++intervalDataCount;
            } else { // 新的像素间隔开始
                if (intervalDataCount >= 2) { // 最后一个像素有多个数据点,将它们合并到一个集群中
                    // 最后一个点更远,所以这个集群的第一个点必须是一个真实的数据点
                    if (lastIntervalEndKey < currentIntervalStartKey - keyEpsilon)
                        barData->append(CPColorBarData(
                            currentIntervalStartKey + keyEpsilon * 0.2, currentIntervalFirstPoint->value));
                    barData->append(CPColorBarData(currentIntervalStartKey + keyEpsilon * 0.75, maxValue));
                    // 新像素开始远离前一个集群,因此请确保集群的最后一个点位于真实数据点
                    if (it->key > currentIntervalStartKey + keyEpsilon * 2)
                        barData->append(CPColorBarData(currentIntervalStartKey + keyEpsilon * 0.8, (it - 1)->value));
                } else
                    barData->append(CPColorBarData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));
                lastIntervalEndKey = (it - 1)->key;
                maxValue = it->value;
                currentIntervalFirstPoint = it;
                currentIntervalStartKey =
                    keyAxis->pixelToCoord(static_cast<int>(keyAxis->coordToPixel(it->key) + reversedRound));
                if (keyEpsilonVariable)
                    keyEpsilon = qAbs(
                        currentIntervalStartKey
                        - keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey) + 1.0 * reversedFactor));
                intervalDataCount = 1;
            }
            ++it;
        }
        // 处理最后一个间隔
        if (intervalDataCount >= 2) { // 最后一个像素有多个数据点,将它们合并到一个集群中
            // 最后一个点不是一个集群,所以这个集群的第一个点必须是一个真实的数据点
            if (lastIntervalEndKey < currentIntervalStartKey - keyEpsilon)
                barData->append(
                    CPColorBarData(currentIntervalStartKey + keyEpsilon * 0.2, currentIntervalFirstPoint->value));
            barData->append(CPColorBarData(currentIntervalStartKey + keyEpsilon * 0.75, maxValue));
        } else
            barData->append(CPColorBarData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));
    } else { // 不使用自适应采样算法,将点从数据容器一对一传输到输出
        barData->resize(dataCount);
        std::copy(begin, end, barData->begin());
    }
}

/* 从基类继承文档 */
void CPColorBar::drawLegendIcon(QCPPainter* painter, const QRectF& rect) const {
    // 绘制填充矩形:
    applyDefaultAntialiasingHint(painter);
    painter->setBrush(mBrush);
    painter->setPen(mPen);
    QRectF r = QRectF(0, 0, rect.width() * 0.67, rect.height() * 0.67);
    r.moveCenter(rect.center());
    painter->drawRect(r);
}

/*!  内部的

  由draw调用以确定在当前键轴范围设置下哪个数据(键)范围可见,因此只需要处理。 它还考虑了条形宽度。

  begin 返回一个迭代器,指向绘图时需要考虑的最低数据点。 请注意,为了得到一个干净的绘图,
  一直到轴矩形的边缘,较低的可能仍然刚好在可见范围之外。

  end 返回一个比最高可见数据点高一的迭代器。 和以前一样,end 也可能位于可见范围之外。

  如果绘图表不包含数据,则起点和终点都指向 constEnd。
*/
void CPColorBar::getVisibleDataBounds(
    QCPColorBarDataContainer::const_iterator& begin, QCPColorBarDataContainer::const_iterator& end) const {
    if (!mKeyAxis) {
        qDebug() << Q_FUNC_INFO << "invalid key axis";
        begin = mDataContainer->constEnd();
        end = mDataContainer->constEnd();
        return;
    }
    if (mDataContainer->isEmpty()) {
        begin = mDataContainer->constEnd();
        end = mDataContainer->constEnd();
        return;
    }

    QCPAxis* keyAxis = mKeyAxis.data();
    QCPAxis* valueAxis = mValueAxis.data();
    // 获取可见数据范围
    begin = mDataContainer->findBegin(keyAxis->range().lower);
    end = mDataContainer->findEnd(keyAxis->range().upper);
    // 将下限/上限限制为 rangeRestriction
    // mDataContainer->limitIteratorsToDataRange(begin, end, QCPDataRange(0, dataCount()));
}

三、示例

#include "CPColorBar.hpp"

#include <QApplication>

int main(int argc, char* argv[]) {
    QApplication a(argc, argv);

    // 生成数据
    QVector<double> x{
        -1, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0};
    QVector<double> y{0, 3, 2, 1, 0, 0, 3, 0, 3, 0, 2, 2, 2, 0, 0, 1, 0, 1, 0, 1, 0};
    // 创建图表
    QCustomPlot* customPlot = new QCustomPlot();
    auto* color_bar = new CPColorBar(customPlot->xAxis, customPlot->yAxis);
    color_bar->setColor({{0, Qt::gray}, {1, Qt::blue}, {2, Qt::yellow}, {3, Qt::red}});
    color_bar->setData(x, y);
    // 设置坐标轴范围
    customPlot->xAxis->setRange(-1, 1);
    customPlot->yAxis->setRange(-1, 1);
    customPlot->yAxis->setVisible(false);
    customPlot->replot();
    customPlot->show();
    customPlot->resize(600, 100);

    return a.exec();
}

运行效果如下:
在这里插入图片描述

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

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

相关文章

使用 MRI 构建的大脑连接网络预测帕金森病萎缩进展模式| 文献速递-基于深度学习的乳房、前列腺疾病诊断系统

Title 题目 Brain Connectivity Networks Constructed Using MRI for Predicting Patterns of Atrophy Progression in Parkinson Disease 使用 MRI 构建的大脑连接网络预测帕金森病萎缩进展模式 Background 背景 Whether connectome mapping of structural and across …

全志T527-TP9930-Camera

一、简介 1、TP9930 TP9930 驱动模块主要实现将 4 路的 Camera 的数据转换为 BT656/BT1120 数据&#xff0c;从而实现在 T527 端来对数据进行处理和送显。 2、BT656/BT1120简介 BT656主要是针对PAL/NTSC等标清视频。随着高清视频的发展需要&#xff0c;又推出了BT1120标准&…

AI + Coding:可以有多少种玩法?

在当今快速发展的科技时代&#xff0c;人工智能&#xff08;AI&#xff09;和编程已经成为不可分割的两大领域。AI赋予了计算机更多的智能&#xff0c;使其能够处理复杂的数据、执行高级任务&#xff0c;而编程是实现这一切的基础。当AI与编程结合在一起时&#xff0c;会带来无…

图片懒加载与预加载(原生)

1、懒加载。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head>…

【开端】JAVA Mono<Void>向前端返回没有登陆或登录超时 暂无权限访问信息组装

一、绪论 JAVA接口返回信息ServerHttpResponse response 等登录接口token过期时需要给前端返回相关状态码和状态信息 二、Mono<Void>向前端返回没有登陆或登录超时 暂无权限访问信息组装 返回Mono对象 public abstract class Mono<T> implements CorePublisher…

2024最新Mysql事务原理与优化最佳实践

概述 我们的数据库一般都会并发执行多个事务&#xff0c;多个事务可能会并发的对相同的一批数据进行增删改查操作&#xff0c;可能就会导致我们说的脏写、脏读、不可重复读、幻读这些问题。 这些问题的本质都是数据库的多事务并发问题&#xff0c;为了解决多事务并发问题&…

在java中通过subString方法来截取字符串中的文本

1、subString&#xff08;&#xff09;常规用法可以通过下标来进行获取&#xff0c;在java中是从0开始&#xff0c;前包括后不包括。 String str “Hello Java World!”; 用法一: substring(int beginIndex) 返回从起始位置&#xff08;beginIndex&#xff09;至字符串末尾…

供应链库存管理面临什么问题?全面解析安全库存和周转库存!

在当今这个快速变化的商业世界中&#xff0c;供应链管理已成为企业获取竞争优势的核心领域。库存管理&#xff0c;作为供应链中的关键环节&#xff0c;直接关系到企业的成本控制、客户服务水平以及市场响应速度。然而&#xff0c;面对市场竞争的加剧和客户需求的多变&#xff0…

事务性邮件调用接口如何配置灵活调用策略?

事务性邮件调用接口性能怎么优化&#xff1f;如何使用接口调用&#xff1f; 如何配置灵活调用策略&#xff0c;不仅可以提升邮件发送的效率和可靠性&#xff0c;还能增强用户体验。AokSend将详细介绍事务性邮件调用接口的配置方法和策略&#xff0c;以便企业在实际应用中取得最…

深度学习读书笔记(1)--机器学习、人工智能、深度学习的关系

声明&#xff1a;本文章是根据网上资料&#xff0c;加上自己整理和理解而成&#xff0c;仅为记录自己学习的点点滴滴。可能有错误&#xff0c;欢迎大家指正。 阅读的书籍主要为《UnderstandingDeepLearning》《动手学深度学习》 1956 年提出 AI 概念&#xff0c;短短3年后&…

【初阶数据结构题目】14.随机链表的复制

随机链表的复制 点击链接做题 思路&#xff1a; 浅拷贝&#xff1a;拷贝值 深拷贝&#xff1a;拷贝空间 在原链表的基础上继续复制链表置random指针复制链表和原链表断开 代码&#xff1a; /*** Definition for a Node.* struct Node {* int val;* struct Node *next…

【开发踩坑】windows查看jvm gc信息

windows查看jvm gc信息 EZ 找出java进程PID 控制面板----搜索任务管理器---- 任务管理器----搜索 java----详细信息 这里PID是4856 cmd jstat gc面板 reference&#xff1a; jstat命令

【Redis】缓存三大问题与缓存一致性问题

缓存三大问题 缓存穿透 缓存穿透是指用户查询的数据在缓存和数据库中都不存在&#xff0c;导致每次请求都会直接落到数据库上&#xff0c;增加数据库负载。 解决方案 1&#xff09;参数校验 一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于…

【letcod-c++】128.最长连续序列

一、题目 二、分析 第一想法是像“242字母异位词”那样用哈希数组&#xff0c;但是这个数组元素的范围比较广&#xff0c;元素又比较分散&#xff0c;用数组太浪费空间&#xff0c;不合适。 于是考虑用哈希set(unordered_set),这个时候忽然想到前几天学习到set它能自动排序且自…

MySQL笔记(九):存储引擎

一、介绍 二、演示 Memory的使用场景&#xff1a; 例如网吧&#xff0c;用户再次上线时会更新状态 #表类型和存储引擎-- 查看所有的存储引擎SHOW ENGINES; -- 1、innodb 支持事务&#xff0c;外键&#xff0c;行级锁-- 2、myisam CREATE TABLE t31(id INT,name VARCHAR(32)) …

十二、享元模式

文章目录 1 基本介绍2 案例2.1 Digit 接口2.2 Color 枚举2.3 BigDigit 类2.4 DigitFactory 类2.5 Client 类2.6 Client 类的测试结果2.7 总结 3 各角色之间的关系3.1 角色3.1.1 Flyweight ( 抽象享元 )3.1.2 ConcreteFlyweight ( 具体享元 )3.1.3 UnsharedFlyweight ( 非享元 )…

2023/8/7 英语每日一段

There is unintended usefulness in this gentle enforcement of empathy. A mere news story makes it easy to deploy the defensive mechanism social scientists call “othering” which dismisses the victim, freak or dupe. But if it’s someone you have watched or …

文件上传绕过最新版安全狗

本文来源无问社区&#xff0c;更多实战内容&#xff0c;渗透思路可前往查看http://www.wwlib.cn/index.php/artread/artid/9960.html http分块传输绕过 http分块传输⼀直是⼀个很经典的绕过⽅式&#xff0c;只是在近⼏年分块传输⼀直被卡的很死&#xff0c;很多waf都开始加 …

数据科学 - 数据可视化(持续更新)

1. 前言​​​​​​​ 数据可视化能够将复杂的数据集转化为易于理解的图形、图表或图像。这种直观的表现形式使得人们能够更快地理解数据的分布、趋势、异常值以及数据之间的关系&#xff0c;从而更深入地洞察数据背后的信息。 数据可视化在数据分析和决策制定过程中具有不可…

【LLM基础知识】LLMs-Attention知识总结笔记v4.0

Attention机制 【1】简要介绍Attention机制 提出Attention的论文**&#xff1a;**Attention Is All You Need 论文地址&#xff1a;https://arxiv.org/pdf/1706.03762.pdf 提出Attention的背景&#xff1a;RNN处理序列数据时&#xff0c;token是逐个喂给模型的。比如在a3的位…