QT串口调试助手V2.0(源码全开源)--上位机+多通道波形显示+数据保存(优化波形显示控件)

news2024/11/25 15:57:08

首先关于Qt的安装和基本配置这里就不做重复说明了,注:本文在Qt5.14基础上完成 完整的项目开源仓库链接在文章末尾

图形控件——qcustomplot

QCustomPlot是一个基于Qt框架的开源绘图库,用于创建高质量的二维图表和数据可视化。

QCustomPlot的主要功能:
绘制多种图表类型:包括折线图、散点图、柱状图、面积图
交互性:支持图表的缩放、平移、数据点选择等交互操作
多轴支持:可以在图表中添加多个X轴和Y轴,以便绘制复杂的多轴图表
定制化:提供丰富的样式和属性设置,用户可以自定义图表的外观,包括颜色、线条样式、标记
高性能:针对大数据量绘图进行了优化,能够处理大量数据点而不影响性能

主要组件:
QCustomPlot:主绘图控件,所有的绘图操作都在这个控件上进行。
QCPGraph:用于绘制常见的折线图和散点图。
QCPAxis:表示图表的轴,可以自定义轴的范围、标签、刻度等。
QCPItem:图表中的各种辅助元素,如直线、文本标签等。
QCPPlottable:可绘制对象的基类,所有具体的绘图类型都继承自这个类。

该控件使用方法
将qcustomplot的源码一个cpp一个h文件添加到项目工程中,并添加头文件和编译链接就可以便捷的在源码中使用了。注意在放置显示控件时需要先放置一个qt内置的QWidget控件,然后通过右键控件,升格,选中头文件和类名称,设置为QCustomPlot,然后源代码中就可以快乐使用了。具体设置的效果可以参考文末完整的项目链接。
在这里插入图片描述

绘制曲线数据

主要使用到了曲线控件customPlot->addGraph();

这个控件的使用方法整体上和Qt自带的控件差别不大,主要这个控件的视觉效果和长时间大数据绘制效果更好一些

在使用控件之前要先初始化相关的控件内容:

// 绘图图表初始化
void MainWindow::QPlot_init(QCustomPlot *customPlot)
{
    // 创建定时器,用于定时生成曲线坐标点数据
    QTimer *timer = new QTimer(this);
    timer->start(10);
    connect(timer, SIGNAL(timeout()), this, SLOT(Plot_TimeData_Update()));

    // 图表添加两条曲线
    pGraph1_1 = customPlot->addGraph();
    pGraph1_2 = customPlot->addGraph();

    // 设置曲线颜色
    pGraph1_1->setPen(QPen(Qt::red));
    pGraph1_2->setPen(QPen(Qt::black));

    // 设置坐标轴名称
    customPlot->xAxis->setLabel("X-Times");
    customPlot->yAxis->setLabel("Amplitude of channel");

    // 设置y坐标轴显示范围
    customPlot->yAxis->setRange(-2, 2);

    // 显示图表的图例
    customPlot->legend->setVisible(true);
    // 添加曲线名称
    pGraph1_1->setName("Channel1");
    pGraph1_2->setName("Channel2");

    // 设置波形曲线的复选框字体颜色
    ui->checkBox_1->setStyleSheet("QCheckBox{color:rgb(255,0,0)}"); // 设定前景颜色,就是字体颜色
    // 允许用户用鼠标拖动轴范围,用鼠标滚轮缩放,点击选择图形:
    customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
}

当向曲线添加新数据的时候可以同时控制范围窗口内的坐标轴尺寸,以及打印一些绘图相关的属性数据

void MainWindow::Plot_Show_Update(QCustomPlot *customPlot, double n1, double n2)
{
    cnt++;
    // 给曲线添加数据
    pGraph1_1->addData(cnt, n1);
    pGraph1_2->addData(cnt, n2);

    // 设置x坐标轴显示范围,使其自适应缩放x轴
    customPlot->xAxis->setRange( 0, (pGraph1_1->dataCount() > 1000) ? (pGraph1_1->dataCount()) : 1000);
    // 更新绘图,这种方式在高填充下太浪费资源。rpQueuedReplot,可避免重复绘图。
    customPlot->replot(QCustomPlot::rpQueuedReplot);

    static QTime time(QTime::currentTime());
    double key = time.elapsed() / 1000.0; // 开始到现在的时间,单位秒
    //计算帧数
    static double lastFpsKey;
    static int frameCount;
    frameCount++;
    if (key - lastFpsKey > 1) // 每1秒求一次平均值
    {
        // 帧数和数据总数
        ui->statusbar->showMessage(
            QString("Refresh rate: %1 FPS, Total data volume: %2")
                .arg(frameCount / (key - lastFpsKey), 0, 'f', 0)
                .arg(customPlot->graph(0)->data()->size() + customPlot->graph(1)->data()->size()),
            0);
        lastFpsKey = key;
        frameCount = 0;
    }
}

曲线的绘制效果:
在这里插入图片描述
要实现上面的多通道效果还需要自行适配串口解析部分,然后匹配对应的数据后将数值传入到目标曲线对象。

完整的项目源码如下,包含数据解析、串口控制部分、同时实现数据保存到csv文件(代码中只保存了数组前两位的数值,需要保存多少数据可以自行修改代码实现)

项目Cpp文件:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 给widget绘图控件,设置个别名,方便书写
    pPlot1 = ui->widget_1;

    // 状态栏指针
    sBar = statusBar();

    // 初始化图表1
    QPlot_init(pPlot1);
    cnt = 0;

    setWindowTitle("数据采集系统");
    serialport = new QSerialPort;
    find_port();                    //查找可用串口
    timerserial = new QTimer();
    QObject::connect(serialport,&QSerialPort::readyRead, this, &MainWindow::serial_timerstart);
    QObject::connect(timerserial,SIGNAL(timeout()), this, SLOT(Read_Date()));


    ui->close_port->setEnabled(false);//设置控件不可用
}


// 析构函数
MainWindow::~MainWindow()
{
    delete ui;
}

//查找串口
void MainWindow::find_port()
{
    //查找可用的串口
    bool fondcom = false;
    ui->com->clear();
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        QSerialPort serial;
        serial.setPort(info);   //设置串口
        if(serial.open(QIODevice::ReadWrite))
        {
            //
            ui->com->addItem(serial.portName());        //显示串口name
            fondcom = true;
            QString std = serial.portName();
            QByteArray comname = std.toLatin1();
            //QMessageBox::information(this,tr("SerialFond"),tr((const char *)comname.data()),QMessageBox::Ok);
            serial.close();
            ui->open_port->setEnabled(true);
        }
    }
    if(fondcom==false)
    {
        QMessageBox::information(this,tr("Error"),tr("Serial Not Fond!Plase cheak Hardware port!"),QMessageBox::Ok);
    }
}

/* 打开并设置串口参数 */
void MainWindow::on_open_port_clicked()
{
    update();
    //find_port();     //重新查找com
     //初始化串口
         serialport->setPortName(ui->com->currentText());        //设置串口名
         if(serialport->open(QIODevice::ReadWrite))              //打开串口成功
         {
             serialport->setBaudRate(ui->baud->currentText().toInt());       //设置波特率
             switch(ui->bit->currentIndex())                   //设置数据位数
             {
                 case 8:serialport->setDataBits(QSerialPort::Data8);break;
                 default: break;
             }
             switch(ui->jiaoyan->currentIndex())                   //设置奇偶校验
             {
                 case 0: serialport->setParity(QSerialPort::NoParity);break;
                 default: break;
             }
             switch(ui->stopbit->currentIndex())                     //设置停止位
             {
                 case 1: serialport->setStopBits(QSerialPort::OneStop);break;
                 case 2: serialport->setStopBits(QSerialPort::TwoStop);break;
                 default: break;
             }
             serialport->setFlowControl(QSerialPort::NoFlowControl);     //设置流控制
             // 设置控件可否使用
            ui->close_port->setEnabled(true);
            ui->open_port->setEnabled(false);
            ui->refresh_port->setEnabled(false);
         }
         else    //打开失败提示
         {
            // Sleep(100);

             QMessageBox::information(this,tr("Erro"),tr("Open the failure"),QMessageBox::Ok);
         }
}


/* 关闭串口并禁用关联功能 */
void MainWindow::on_close_port_clicked()
{
    serialport->clear();        //清空缓存区
    serialport->close();        //关闭串口

    ui->open_port->setEnabled(true);
    ui->close_port->setEnabled(false);
    ui->refresh_port->setEnabled(true);
}

/* 开始接收数据
 * */
void MainWindow::on_recive_data_clicked()
{
    QString str = "START_SEND_DATA\r\n";
    QByteArray str_utf8 = str.toUtf8();
    if(serialport->isOpen())serialport->write(str_utf8);
    else QMessageBox::information(this,tr("ERROE"),tr("串口未连接,请先检查串口连接"),QMessageBox::Ok);
}

void MainWindow::serial_timerstart()
{
    timerserial->start(1);
    serial_bufferClash.append(serialport->readAll());
}

//串口接收数据帧格式为:帧头'*' 帧尾'#' 数字间间隔符号',' 符号全为英文格式
void MainWindow::Read_Date()
{
    QString string;
    QStringList serialBuferList;
    int list_length = 0;//帧长
    QString str = ui->Receive_text_window->toPlainText();
    timerserial->stop();//停止定时器
//    qDebug()<< "[Serial LOG]serial read data:" <<serial_bufferClash;

    QByteArray bufferbegin = "*";   //帧头
    int index=0;
    QByteArray bufferend = "#";     //帧尾
    int indexend = 1;
    QByteArray buffercashe;
    index = serial_bufferClash.indexOf(bufferbegin,index);
    indexend = serial_bufferClash.indexOf(bufferend,indexend);
//    qDebug()<< index<< indexend;
    int bufferlens=0;
    if((index<serial_bufferClash.size())&&(indexend<serial_bufferClash.size()))
    {
        bufferlens = indexend - index-1;
        buffercashe = serial_bufferClash.mid(index+1,bufferlens);
        qDebug()<< "[Serial LOG]serial chack data:" <<buffercashe;
        string.prepend(buffercashe);
        serialBuferList = string.split(" ");      //数据分割
        list_length=serialBuferList.count();    //帧长
        if (list_length>1)
        {
            clash.data1 = serialBuferList[0].toDouble();
            clash.data2 = serialBuferList[1].toDouble();
            plot_buffer.push_back(clash);
            clash.data1 = serialBuferList[2].toDouble();
            clash.data2 = serialBuferList[3].toDouble();
            plot_buffer.push_back(clash);
            clash.data1 = serialBuferList[4].toDouble();
            clash.data2 = serialBuferList[5].toDouble();
            plot_buffer.push_back(clash);
        }
    }
    else
    {
        qDebug()<< "[Serial LOG][ERROR]recive data:" <<serial_bufferClash;
    }
    str+="succeed:"+buffercashe;
    str += "  ";
    ui->Receive_text_window->clear();
    ui->Receive_text_window->append(str);
    serial_bufferClash.clear();
}

/*  刷新串口按键的按钮槽函数
 * */
void MainWindow::on_refresh_port_clicked()
{
    find_port();
}



// 绘图图表初始化
void MainWindow::QPlot_init(QCustomPlot *customPlot)
{

    // 创建定时器,用于定时生成曲线坐标点数据
    QTimer *timer = new QTimer(this);
    timer->start(10);
    connect(timer, SIGNAL(timeout()), this, SLOT(Plot_TimeData_Update()));

    // 图表添加两条曲线
    pGraph1_1 = customPlot->addGraph();
    pGraph1_2 = customPlot->addGraph();

    // 设置曲线颜色
    pGraph1_1->setPen(QPen(Qt::red));
    pGraph1_2->setPen(QPen(Qt::black));

    // 设置坐标轴名称
    customPlot->xAxis->setLabel("X-Times");
    customPlot->yAxis->setLabel("Channel Data");

    // 设置y坐标轴显示范围
    customPlot->yAxis->setRange(-2, 2);

    // 显示图表的图例
    customPlot->legend->setVisible(true);
    // 添加曲线名称
    pGraph1_1->setName("Channel1");
    pGraph1_2->setName("Channel2");

    // 设置波形曲线的复选框字体颜色
    ui->checkBox_1->setStyleSheet("QCheckBox{color:rgb(255,0,0)}"); // 设定前景颜色,就是字体颜色
    // 允许用户用鼠标拖动轴范围,用鼠标滚轮缩放,点击选择图形:
    customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
}

int data_lens = 0;
// 定时器溢出处理槽函数。用来生成曲线的坐标数据。
void MainWindow::Plot_TimeData_Update()
{

    int lens = plot_buffer.size();
    if (lens > data_lens)
    {
        for(int i=data_lens;i<lens;i++)
        {
            Plot_Show_Update(pPlot1, plot_buffer[i].data1, plot_buffer[i].data2);
            data_lens++;
            qDebug()<<"[Plot LOG]data_lens:"<<data_lens<< "size:"<<lens;
        }
    }
}

// 曲线更新绘图
void MainWindow::Plot_Show_Update(QCustomPlot *customPlot, double n1, double n2)
{
    cnt++;
    // 给曲线添加数据
    pGraph1_1->addData(cnt, n1);
    pGraph1_2->addData(cnt, n2);

    // 设置x坐标轴显示范围,使其自适应缩放x轴
    customPlot->xAxis->setRange( 0, (pGraph1_1->dataCount() > 100) ? (pGraph1_1->dataCount()) : 100);
    // 更新绘图,这种方式在高填充下太浪费资源。rpQueuedReplot,可避免重复绘图。
    customPlot->replot(QCustomPlot::rpQueuedReplot);

    static QTime time(QTime::currentTime());
    double key = time.elapsed() / 1000.0; // 开始到现在的时间,单位秒
    //计算帧数
    static double lastFpsKey;
    static int frameCount;
    frameCount++;
    if (key - lastFpsKey > 1) // 每1秒求一次平均值
    {
        // 帧数和数据总数
        ui->statusbar->showMessage(
            QString("Refresh rate: %1 FPS, Total data volume: %2")
                .arg(frameCount / (key - lastFpsKey), 0, 'f', 0)
                .arg(customPlot->graph(0)->data()->size() + customPlot->graph(1)->data()->size()),
            0);
        lastFpsKey = key;
        frameCount = 0;
    }
}

/* 清空缓存数据
 * */
void MainWindow::on_clean_data_clicked()
{
    qDebug()<< "[Clean Data]";
    plot_buffer.clear();
    data_lens = 0;
    cnt = 0;
    pGraph1_1->data().data()->clear();
    pGraph1_2->data().data()->clear();
    pPlot1->graph(0)->data().clear();
    pPlot1->graph(1)->data().clear();
}

// setVisible设置可见性属性,隐藏曲线,不会对图例有任何影响。推荐使用。
void MainWindow::on_checkBox_1_stateChanged(int arg1)
{
    if (arg1)
    {
        pGraph1_1->setVisible(true);
    }
    else
    {
        pGraph1_1->setVisible(false); // void QCPLayerable::setVisible(bool on)
    }
    pPlot1->replot();
}

void MainWindow::on_checkBox_2_stateChanged(int arg1)
{
    if (arg1)
    {
        pGraph1_2->setVisible(true);
    }
    else
    {
        pGraph1_2->setVisible(false); // void QCPLayerable::setVisible(bool on)
    }
    pPlot1->replot();
}

// 保存缓冲区数据为csv文件
void MainWindow::on_savedata_csv_clicked()
{
     if(plot_buffer.size()<1)
     {
         QMessageBox::information(this, "提示","当前数据为空");
         return;
     }
     serialport->clear();        //清空缓存区
     timerserial->stop();
     serialport->close();        //关闭串口
     ui->open_port->setEnabled(true);
     ui->close_port->setEnabled(false);
     QString csvFile = QFileDialog::getExistingDirectory(this);
     QDateTime current_date_time =QDateTime::currentDateTime();
     QString current_date =current_date_time.toString("yyyy_MM_dd_hh_mm");
     csvFile += tr("/sensor_Save_%1.csv").arg(current_date);
     if(csvFile.isEmpty())
     {
        QMessageBox::information(this,tr("警告"),tr("文件路径错误,无法打开文件,请重试"),QMessageBox::Ok);
     }
     else
     {
         qDebug()<< csvFile;
         QFile file(csvFile);
         if ( file.exists())
         {
                 //如果文件存在执行的操作,此处为空,因为文件不可能存在
         }
         file.open( QIODevice::ReadWrite | QIODevice::Text );
         QTextStream out(&file);
         out<<tr("data1,")<<tr("data2,\n");     //写入表头
         // 创建 CSV 文件
         for (const auto &data : plot_buffer) {
             out << QString("%1,%2").arg(data.data1).arg(data.data2) << "\n";
         }
         file.close();
         QMessageBox::information(this, "提示","数据保存成功");
     }
     serialport->open(QIODevice::ReadWrite);        //打开串口
     ui->open_port->setEnabled(false);
     ui->close_port->setEnabled(true);
}

完整项目链接->完整的项目工程Github链接

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

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

相关文章

IO-源码阅读 glibc 2.35

文章目录 参考缓存机制IO_FILE_PLUSfopenfopen_internal_IO_no_init_IO_old_init _IO_new_file_init_internal_IO_link_in _IO_new_file_fopen_IO_file_open fread_IO_fread_IO_sgetn_IO_doallocbuf_IO_file_doallocate_IO_file_stat_IO_setb __underflow_IO_new_file_underflo…

windows软件手动设置开机自启

博主需求 由于很多线上课程使用outlook进行教学&#xff0c;课程链接都关联到outlook日历中了&#xff0c;只要保持outlook是打开的状态就能收到上课提醒&#xff0c;非常方便。 但是有时候会忘记打开outlook查看&#xff0c;我偶尔会错过一些提醒QAQ。 所以如何让outlook常…

Qwen2大模型微调入门实战(完整代码)

Qwen2是通义千问团队的开源大语言模型&#xff0c;由阿里云通义实验室研发。以Qwen2作为基座大模型&#xff0c;通过指令微调的方式实现高准确率的文本分类&#xff0c;是学习大语言模型微调的入门任务。 指令微调是一种通过在由&#xff08;指令&#xff0c;输出&#xff09;对…

解读下/etc/network/interfaces配置文件

/etc/network/interfaces 是一个常见的网络配置文件&#xff0c;通常在 Debian 及其衍生版本的 Linux 发行版中使用。该文件用于配置网络接口和网络连接参数&#xff0c;允许用户手动设置网络连接的属性&#xff0c;包括 IP 地址、子网掩码、网关、DNS 服务器等。 以下是一个可…

LeetCode热题100—链表(二)

19.删除链表的倒数第N个节点 题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; 输入&#xff1a;head [1], n 1 …

人工智能程序员应该有什么职业素养?

人工智能程序员应该有什么职业素养&#xff1f; 面向企业需求去学习AI必备技能实战能力实战能力提升策略 面向企业需求去学习 如果想要应聘AI相关的岗位&#xff0c;就需要知道HR和管理层在招聘时需要考察些什么&#xff0c;面向招聘的需求去学习就能具备AI程序员该有的职业素…

Golang发送邮件如何验证身份?有哪些限制?

Golang发送邮件需要哪些库&#xff1f;怎么设置邮件发送的参数&#xff1f; 对于开发者而言&#xff0c;使用Golang发送邮件是一种常见需求。然而&#xff0c;在发送邮件的过程中&#xff0c;验证身份是一个至关重要的环节&#xff0c;它确保了邮件的可靠性和安全性。A将探讨G…

linux业务代码性能优化点

planning优化的一些改动----------> 减少值传递&#xff0c;多用引用来传递 <---------- // ----------> 减少值传递&#xff0c;多用引用来传递 <---------- // 例1&#xff1a; class A{}; std::vector<A> v; // for(auto elem : v) {} // 不建议&#xff…

FreeRTOS实时系统 在任务中增加数组等相关操作 导致单片机起不来或者挂掉

在调试串口任务中增加如下代码&#xff0c;发现可以用keil进行仿真&#xff0c;但是烧录程序后&#xff0c;调试串口没有打印&#xff0c;状态灯也不闪烁&#xff0c;单片机完全起不来 博主就纳了闷了&#xff0c;究竟是什么原因&#xff0c;这段代码可是公司永流传的老代码了&…

【小白专用24.6.8】c#异步方法 async task调用及 await运行机制

await是C#中用于等待异步操作完成的关键字。它通常用于异步方法内部&#xff0c;使得在等待异步操作期间&#xff0c;线程可以继续执行其他操作&#xff0c;从而保持程序的响应性。 在使用await时&#xff0c;需要注意以下几点&#xff1a; 1. async修饰符&#xff1a; 使用…

Mac环境下,简单反编译APK

一、下载jadx包 https://github.com/skylot/jadx/releases/tag/v1.4.7 下载里面的这个&#xff1a;下载后&#xff0c;找个干净的目录解压&#xff0c;我是放在Downloads下面 二、安装及启动 下载和解压 jadx&#xff1a; 下载 jadx-1.4.7.zip 压缩包。将其解压到你希望的目…

C++ 史上首次超越 C,跃至榜二

TIOBE 公布了 2024 年 6 月的编程语言排行榜。 C在本月的TIOBE指数中成功超越了C&#xff0c;成为新的第二名。它是一种被广泛应用于嵌入式系统、游戏开发和金融交易软件等领域的编程语言。这次的排名是C在TIOBE指数中的历史最高位&#xff0c;同时也是C语言的历史最低位。 T…

排序-读取数据流并实时返回中位数

目录 一、问题描述 二、解题思路 1.顺序表排序法 2.使用大根堆、小根堆 三、代码实现 1.顺序表排序法实现 2.大根堆、小根堆法实现 四、刷题链接 一、问题描述 二、解题思路 1.顺序表排序法 &#xff08;1&#xff09;每次读取一个数就对列表排一次序&#xff0c;对排…

使用Python批量处理Excel的内容

正文共&#xff1a;1500 字 10 图&#xff0c;预估阅读时间&#xff1a;1 分钟 在前面的文章中&#xff08;如何使用Python提取Excel中固定单元格的内容&#xff09;&#xff0c;我们介绍了如何安装Python环境和PyCharm工具&#xff0c;还利用搭好的环境简单测试了一下ChatGPT提…

CPP入门:CPP的内存管理模式

一.new和delete操作自定义类型 1.1C语言的内存管理 在传统的C语言中&#xff0c;malloc和free是无法调用类的构造和析构函数的 #include <iostream> using namespace std; class kuzi { public:kuzi(int a, int b):_a(a), _b(b){cout << "我构造啦" &…

卷积的计算过程

卷积的计算过程 flyfish 包括手动计算&#xff0c;可视化使用torch.nn.Conv2d实现 示例 import torch import torch.nn as nn# 定义输入图像 input_image torch.tensor([[1, 2, 3, 0, 1],[0, 1, 2, 3, 4],[2, 3, 0, 1, 2],[1, 2, 3, 4, 0],[0, 1, 2, 3, 4] ], dtypetorch.f…

20240609如何查询淘宝的历史价格

20240609如何查询淘宝的历史价格 2024/6/9 18:39 百度&#xff1a;淘宝历史价格 淘宝历史价格查询网站 https://zhuanlan.zhihu.com/p/670972171 30秒学会淘宝商品历史价格查询&#xff01; https://item.taobao.com/item.htm?id693104421622&pidmm_29415502_2422500430_1…

AI菜鸟向前飞 — LangChain系列之十七 - 剖析AgentExecutor

AgentExecutor 顾名思义&#xff0c;Agent执行器&#xff0c;本篇先简单看看LangChain是如何实现的。 先回顾 AI菜鸟向前飞 — LangChain系列之十四 - Agent系列&#xff1a;从现象看机制&#xff08;上篇&#xff09; AI菜鸟向前飞 — LangChain系列之十五 - Agent系列&#…

sqlilabs靶场安装

05-sqllabs靶场安装 1 安装 1 把靶场sqli-labs-master.zip上传到 /opt/lampp/htdocs 目录下 2 解压缩 unzip sqli-labs-master.zip3 数据库配置 找到配置文件,修改数据库配置信息 用户名密码&#xff0c;修改为你lampp下mysql的用户名密码&#xff0c;root/123456host:la…

GraphQL(6):认证与中间件

下面用简单来讲述GraphQL的认证示例 1 实现代码 在代码中添加过滤器&#xff1a; 完整代码如下&#xff1a; const express require(express); const {buildSchema} require(graphql); const grapqlHTTP require(express-graphql).graphqlHTTP; // 定义schema&#xff0c;…