Qt C++中调用python,并将软件打包发布,python含第三方依赖

news2024/12/27 13:16:10

工作中遇到qt c++调用我的python 代码,并且想要一键打包,这里我根据参考的以及个人实践的结果来简单实现一下。

环境:windows系统,QT Creater 4.5, python 3.8(anaconda虚拟环境)

1. 简单QT调用python程序

1.创建QT工程

中间省略3个步骤图。创建完成后,如图。

首先提示各位从python过来的同仁,QT中有时候对项目“重新构建”,项目并不真正的重新构建,如果这样的话,我们需要在工程文件夹下找到对应的构建后的项目,即比较长的这个(对应的是debug模式下的编译构建),删除掉,再点击重新构建。

2. 配置python 环境

使用QT 调用python需要加载Python.h头文件,我们在Headers/mainwindow.h里面引入Python.h。但原始配置是找不到Python.h的,所以首先我们需要将安装好的python路径配置到QT的配置文件(.pro)中。 

打开(项目名.pro)文件,按照如下格式填写。这里我将一个python 环境的DLLs,include,Lib,libs和python3.dll, python38.dll 以及vcruntime.dll 复制过来,为该项目单独做个python环境。

参考在QT C++中调用 Python并将软件打包发布(裸机可运行)_互联网集市

我是创建一个python_38的python环境,拷贝了miniconda3/envs/cat虚拟环境中的DLLs,include,Lib,libs和python3.dll, python38.dll 以及vcruntime140.dll (这个python环境要能够支撑后面的python代码的运行,就是在原来的虚拟环境中,下面的python代码也可以执行的)

INCLUDEPATH += -I D:\output\envs\python_38\include  # python.h
LIBS += -LD:\output\envs\python_38\libs -lpython38  # python38.lib

其中 INCLUDEPATH 里面配置的是python.h的路径,LIBS配置的是python38.lib的路径。(参考Qt调用Python详细图文过程记录_python_脚本之家)

问题1:出现C2059错误

解决办法:在object.h中把slots改成slots1。Python将slots作为变量,而Qt将slots作为关键字,所以冲突了,再次编译该问题就没有了(参考Qt调用Python详细图文过程记录_python_脚本之家)

问题2 

如果出现找不到python38_d.lib是因为系统默认我们采用的是debug模式编译的(图片左下角所示)

我们可以

1在D:\output\envs\python_38\libs 复制python38.lib,粘贴成python38_d.lib

2 将编译模式修改成release模式。

以下操作在release模式进行

再次编译,如果不报错,则表示编译成功,点击运行,出现弹窗。

为了方便调试我们的程序是否成功,我们在mainwindow.h中加入QDebug

然后再mainwindow.cpp中编写如下,运行。(参考C++调用python脚本 - 知乎)

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

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // 初始化python解释器.C/C++中调用Python之前必须先初始化解释器
    Py_Initialize();
    // 判断python解析器的是否已经初始化完成
    if(!Py_IsInitialized())
        qDebug()<<"[db:] Py_Initialize fail";
    else
        qDebug()<<"[db:] Py_Initialize success";
    // 执行 python 语句
    PyRun_SimpleString("print('hello world') ");
    // 并销毁自上次调用Py_Initialize()以来创建并为被销毁的所有子解释器。
    Py_Finalize();


}

MainWindow::~MainWindow()
{
    delete ui;
}

代码能够解释执行python语句,并输出hello world,表示我们配置运行成功。

3. 调用python脚本

把python脚本嵌入近c++语句中,肯定不是我们想要的,我们想要的是QT C++能够调用执行python脚本的。

我们写一个简单的python脚本py_test.py,为了证明调用成功,我们使用python写一个空文件,内容如下。

def write_file():
    with open("a.txt", "w") as f:
        f.write("test")

将其放到py_scripts文件夹下,py_scripts与build-simple_test-Desktop_Qt_5_10_0_MSVC2015_64bit-Release文件夹的相对位置如下所示,即同属于./qt文件夹下

修改mainwindow.cpp内容,如下

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

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // 初始化python解释器.C/C++中调用Python之前必须先初始化解释器
    Py_Initialize();
    // 判断python解析器的是否已经初始化完成
    if(!Py_IsInitialized())
        qDebug()<<"[db:] Py_Initialize fail";
    else
        qDebug()<<"[db:] Py_Initialize success";
    // 执行 python 语句
    PyRun_SimpleString("print('hello world') ");

    // 导入sys模块设置模块地址,以及python脚本路径
    PyRun_SimpleString("import sys");
    // 该相对路径是以build...为参考的
    PyRun_SimpleString("sys.path.append('../py_scripts')");
    // 加载 python 脚本
    PyObject *pModule = PyImport_ImportModule("py_test");  // 脚本名称,不带.py
    if(!pModule)  // 脚本加载成功与否
        qDebug()<<"[db:] pModule fail";
    else
        qDebug()<<"[db:] pModule success";

    // 创建函数指针
    PyObject* pFunc= PyObject_GetAttrString(pModule,"write_file");  // 方法名称
    if(!pFunc || !PyCallable_Check(pFunc))  // 函数是否创建成功
        qDebug()<<"[db:] pFunc fail";
    else
        qDebug()<<"[db:] pFunc success";
    // 调用函数
    PyObject_CallObject(pFunc, NULL);   // 无参调用
    // 并销毁自上次调用Py_Initialize()以来创建并为被销毁的所有子解释器。
    Py_Finalize();

}

MainWindow::~MainWindow()
{
    delete ui;
}

执行完成后,会在build-simple_test-Desktop_Qt_5_10_0_MSVC2015_64bit-Release文件夹下生成一个a.txt文件。

2. 有参调用

以上为对python的无参调用,这里我们使用对python的有参调用。

因为python 是没有显性定义的,而C++是有定义的,我们要简单了解下python与C++的数据的类型 。类型对应参考(如何在C++中使用一个Python类-[PyImport_ImportModule、PyModule_GetDict、PyDict_GetItemString、PyObject_CallFuncti]-CSDN博客),简单来说就是s对应字符串,i对应整型,f对应float。使用方法可以参考(Qt项目中C++调用Python函数传多参问题_qt调用python_平头猿小哥的博客-CSDN博客)

这里就复制粘贴使用方法参考的文档,稍作修改,连带返回值和列表的使用都有了。

QT C++源码如下

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

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // 初始化python解释器.C/C++中调用Python之前必须先初始化解释器
    Py_Initialize();
    // 判断python解析器的是否已经初始化完成
    if(!Py_IsInitialized())
        qDebug()<<"[db:] Py_Initialize fail";
    else
        qDebug()<<"[db:] Py_Initialize success";
    // 执行 python 语句
    PyRun_SimpleString("print('hello world') ");

    // 导入sys模块设置模块地址,以及python脚本路径
    PyRun_SimpleString("import sys");
    // 该相对路径是以build...为参考的
    PyRun_SimpleString("sys.path.append('../py_scripts')");
    // 加载 python 脚本
    PyObject *pModule = PyImport_ImportModule("py_test");  // 脚本名称,不带.py
    if(!pModule)  // 脚本加载成功与否
        qDebug()<<"[db:] pModule fail";
    else
        qDebug()<<"[db:] pModule success";

    // 创建函数指针,有参调用
    PyObject* pFunc= PyObject_GetAttrString(pModule, "process_data");  // 有参调用的
    // 定义一个随机器
    QRandomGenerator generator;
    // 创建一个定长元组,用来存放传入参数
    PyObject* pyArgs = PyTuple_New(20);
    // 每个元组类似于结构体,包含字符串,整型和浮点类型数据
    // 填充元组
    for (int i = 0; i < 20; ++i) {
        PyObject* pyTuple = PyTuple_New(3);  //元组由三部分组成
        // 组合下字符串
        QString qst = "test string " + QString::number(i);
        QByteArray baq = qst.toLatin1();

        PyTuple_SetItem(pyTuple, 0, Py_BuildValue("s", baq.data()));  // 字符串
        PyTuple_SetItem(pyTuple, 1, Py_BuildValue("i", generator.generate() % 100));  // 整型
        PyTuple_SetItem(pyTuple, 2, Py_BuildValue("f", 3.14f));  // 浮点型
        PyTuple_SetItem(pyArgs, i, pyTuple);  // 将结构体填充到列表中
     }
    // 调用python函数
     PyObject* pyResult = PyObject_CallObject(pFunc, pyArgs);

     int list_len = PyObject_Size(pyResult);// 计算返回过来的列表长度
     qDebug() << list_len;

     // 判单是否成功
     if (pyResult == NULL) {        PyErr_Print();    }
     else {
         // 解析返回值
         for (int i = 0; i < 20; ++i) {  // 已知列表长度有20个,预先不知道的话就使用上面定义的list_len
             PyObject* pyTuple = PyList_GetItem(pyResult, i);
             QString strVal = QString::fromUtf8(PyUnicode_AsUTF8(PyList_GetItem(pyTuple, 0)));
             int intVal = PyLong_AsLong(PyList_GetItem(pyTuple, 1));
             double floatVal = PyFloat_AsDouble(PyList_GetItem(pyTuple, 2));
             qDebug() << strVal << intVal << floatVal;	// 打印
         }
      }
      // 清理Python变量
      Py_DECREF(pyArgs);
      Py_DECREF(pFunc);
      Py_DECREF(pModule);
      Py_DECREF(pyResult);


    // 并销毁自上次调用Py_Initialize()以来创建并为被销毁的所有子解释器。
    Py_Finalize();

}

MainWindow::~MainWindow()
{
    delete ui;
}

python源码如下,文件名称仍然是 py_test.py

def process_data(*args):
    result = []
    f = open("b.txt", "w")
    for arg in args:  # 从元组中读取数据
        strVal, intVal, floatVal = arg  # 按顺序一一对应取数据
        f.write(strVal + " " + str(intVal) + '\n')  # 写文档
        # process the data
        processed_strVal = strVal.upper()
        processed_intVal = intVal + 1
        processed_floatVal = floatVal ** 2
        sub_result = [processed_strVal, processed_intVal, processed_floatVal]
        result.append(sub_result)  # 按列表格式返回数据
    f.close()
    return result

--------------------------------------------------------------------------------------------------------------------------- 

具体修改内容是

1.创建对有参函数的调用,和一个定长元组,用来存放传入参数,中间还有个随机生成器

    // 创建函数指针,有参调用
    PyObject* pFunc= PyObject_GetAttrString(pModule, "process_data");  // 有参调用的
    // 定义一个随机器
    QRandomGenerator generator;
    // 创建一个定长元组,用来存放传入参数
    PyObject* pyArgs = PyTuple_New(20);

2.填充元组数据

每个元组类似于结构体,包含字符串,整型和浮点类型数据
for (int i = 0; i < 20; ++i) {
        PyObject* pyTuple = PyTuple_New(3);  //元组由三部分组成
        // 组合下字符串
        QString qst = "test string " + QString::number(i);
        QByteArray baq = qst.toLatin1();

        PyTuple_SetItem(pyTuple, 0, Py_BuildValue("s", baq.data()));  // 字符串
        PyTuple_SetItem(pyTuple, 1, Py_BuildValue("i", generator.generate() % 100));  // 整型
        PyTuple_SetItem(pyTuple, 2, Py_BuildValue("f", 3.14f));  // 浮点型
        PyTuple_SetItem(pyArgs, i, pyTuple);  // 将结构体填充到列表中
     }

3.对mainwindow.h的修改

 因为用到了QRandomGenerator ,所以在mainwindow.h中引入#include <QRandomGenerator>头文件

#include <QMainWindow>
#include "Python.h"
#include <QDebug>
#include <QRandomGenerator>

 4.调用python函数,并输出使用返回值

注意我们传参的时候是使用元组(tuple),返回的时候使用的列表(list),这个见python代码

    // 调用python函数
     PyObject* pyResult = PyObject_CallObject(pFunc, pyArgs);

     int list_len = PyObject_Size(pyResult);// 计算返回过来的列表长度
     qDebug() << list_len;

     // 判单是否成功
     if (pyResult == NULL) {        PyErr_Print();    }
     else {
         // 解析返回值
         for (int i = 0; i < 20; ++i) {  // 已知列表长度有20个,预先不知道的话就使用上面定义的list_len
             PyObject* pyTuple = PyList_GetItem(pyResult, i);
             QString strVal = QString::fromUtf8(PyUnicode_AsUTF8(PyList_GetItem(pyTuple, 0)));
             int intVal = PyLong_AsLong(PyList_GetItem(pyTuple, 1));
             double floatVal = PyFloat_AsDouble(PyList_GetItem(pyTuple, 2));
             qDebug() << strVal << intVal << floatVal;	// 打印
         }
      }

 5.python代码的修改

仍然使用py_test文件,在文件中定义process_data函数。读取tuple内容,将结构体用list包装,并使用list 返回内容如下:

def process_data(*args):
    result = []
    f = open("b.txt", "w")
    for arg in args:  # 从元组中读取数据
        strVal, intVal, floatVal = arg  # 按顺序一一对应取数据
        f.write(strVal + " " + str(intVal) + '\n')  # 写文档
        # process the data
        processed_strVal = strVal.upper()
        processed_intVal = intVal + 1
        processed_floatVal = floatVal ** 2
        sub_result = [processed_strVal, processed_intVal, processed_floatVal]
        result.append(sub_result)  # 按列表格式返回数据
    f.close()
    return result

 执行结果是会在build-simple_test-Desktop_Qt_5_10_0_MSVC2015_64bit-Release文件夹下生成一个b.txt文件,并且qt端输出内容。

3. 打包部署

以上我们已经实现QT C++调用python的程序,现在我们要将项目部署在一个没有python环境下的机器上,QT打包发布成exe执行的。对代码的改动不多,对文件夹的修改移动比较多,注意一点。

由于.pro只修改一行,这里只附上mainwindow.cpp的源码

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

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

    // 设置 python 路径
    Py_SetPythonHome((wchar_t*)(L"./python_38"));  // 相对位置以exe为参考
    // 初始化python解释器.C/C++中调用Python之前必须先初始化解释器
    Py_Initialize();
    // 判断python解析器的是否已经初始化完成
    if(!Py_IsInitialized())
        qDebug()<<"[db:] Py_Initialize fail";
    else
        qDebug()<<"[db:] Py_Initialize success";
    // 执行 python 语句
    PyRun_SimpleString("print('hello world') ");

    // 导入sys模块设置模块地址,以及python脚本路径
    PyRun_SimpleString("import sys");
    // 该相对路径是以build...为参考的
    PyRun_SimpleString("sys.path.append('./py_scripts')");  //以exe为参考位置
    // 加载 python 脚本
    PyObject *pModule = PyImport_ImportModule("py_test");  // 脚本名称,不带.py
    if(!pModule)  // 脚本加载成功与否
        qDebug()<<"[db:] pModule fail";
    else
        qDebug()<<"[db:] pModule success";

    // 创建函数指针,有参调用
    PyObject* pFunc= PyObject_GetAttrString(pModule, "process_data");  // 有参调用的
    // 定义一个随机器
    QRandomGenerator generator;
    // 创建一个定长元组,用来存放传入参数
    PyObject* pyArgs = PyTuple_New(20);
    // 每个元组类似于结构体,包含字符串,整型和浮点类型数据
    // 填充元组
    for (int i = 0; i < 20; ++i) {
        PyObject* pyTuple = PyTuple_New(3);  //元组由三部分组成
        // 组合下字符串
        QString qst = "test string " + QString::number(i);
        QByteArray baq = qst.toLatin1();

        PyTuple_SetItem(pyTuple, 0, Py_BuildValue("s", baq.data()));  // 字符串
        PyTuple_SetItem(pyTuple, 1, Py_BuildValue("i", generator.generate() % 100));  // 整型
        PyTuple_SetItem(pyTuple, 2, Py_BuildValue("f", 3.14f));  // 浮点型
        PyTuple_SetItem(pyArgs, i, pyTuple);  // 将结构体填充到列表中
     }
    // 调用python函数
     PyObject* pyResult = PyObject_CallObject(pFunc, pyArgs);

     int list_len = PyObject_Size(pyResult);// 计算返回过来的列表长度
     qDebug() << list_len;

     // 判单是否成功
     if (pyResult == NULL) {        PyErr_Print();    }
     else {
         // 解析返回值
         for (int i = 0; i < 20; ++i) {  // 已知列表长度有20个,预先不知道的话就使用上面定义的list_len
             PyObject* pyTuple = PyList_GetItem(pyResult, i);
             QString strVal = QString::fromUtf8(PyUnicode_AsUTF8(PyList_GetItem(pyTuple, 0)));
             int intVal = PyLong_AsLong(PyList_GetItem(pyTuple, 1));
             double floatVal = PyFloat_AsDouble(PyList_GetItem(pyTuple, 2));
             qDebug() << strVal << intVal << floatVal;	// 打印
         }
      }
      // 清理Python变量
      Py_DECREF(pyArgs);
      Py_DECREF(pFunc);
      Py_DECREF(pModule);
      Py_DECREF(pyResult);


    // 并销毁自上次调用Py_Initialize()以来创建并为被销毁的所有子解释器。
    Py_Finalize();

}

MainWindow::~MainWindow()
{
    delete ui;
}

具体修改如下:

1. 修改编译输出目录(生成exe的目录),到 qt_output

在项目.pro中添加

DESTDIR = $$PWD/../qt_output

与python路径合在一起展示如下。

FORMS += \
        mainwindow.ui

DESTDIR = $$PWD/../qt_output

INCLUDEPATH += -I D:\output\envs\python_38\include  # python.h
LIBS += -LD:\output\envs\python_38\libs -lpython38  # python38.lib

编译后结果如下 ,qt_output中只有exe文件,可知这个PWD的路径是以build-simple_test-Desktop_Qt_5_10_0_MSVC2015_64bit-Release为参考的

 2.将python 环境拷贝到qt_output目录下

即将python_38文件夹复制到qt_output目录下。

3.在QT C++ 中指定python 库地址

在初始化之前,添加

Py_SetPythonHome((wchar_t*)(L"./python_38"));  // 相对位置以exe为参考
    ui->setupUi(this);

    // 设置 python 路径
    Py_SetPythonHome((wchar_t*)(L"./python_38"));  // 相对位置以exe为参考
    // 初始化python解释器.C/C++中调用Python之前必须先初始化解释器
    Py_Initialize();

4.将python脚本文件移入qt_output文件夹中,并修改相对路径

    // 导入sys模块设置模块地址,以及python脚本路径
    PyRun_SimpleString("import sys");
    // 该相对路径是以build...为参考的
    PyRun_SimpleString("sys.path.append('./py_scripts')");  //以exe为参考位置
    // 加载 python 脚本
    PyObject *pModule = PyImport_ImportModule("py_test");  // 脚本名称,不带.py

2,3,4,步执行完成后,文件夹中内容如下。

程序中运行,会在exe同文件夹下生成 b.txt

在文件夹中,点击exe文件直接运行,会出现找不到Qt5Core.dll和Qt5Widgets.dll错误。

这就用到windeployqt命令了 ,

5.开始键输入找到如图的客户端,打开后

输入windeployqt D:\workspace\qt\qt_output\simple_test.exe 具体内容根据项目路径来写,运行完成后会在qt_output文件夹中生成程序运行所需要的依赖包(具体叫啥不知道,这个是qt的东西)。

qt_output文件夹内除了以前的这些文件外,又多了些文件夹和文件(依赖库) 。

再点击simple_test.exe,则出现QT的弹窗,并且生成新的b.txt文件。

6. 经验证,还需要将python_38文件夹里面的python38.dll文件移到外面来,放在和simple.exe同一级别。

4. python 中带有第三方包的部署(忘了参考哪个了,主要是找不到参考的那个网页了)

我们首先修改下py_test.py的内容,引入numpy包,因为numpy是第三方的包。修改如下:

import numpy as np

def write_file():
    with open("a.txt", "w") as f:
        f.write("test")


def process_data(*args):
    result = []
    f = open("b.txt", "w")
    for arg in args:  # 从元组中读取数据
        strVal, intVal, floatVal = arg  # 按顺序一一对应取数据
        f.write(strVal + " " + str(intVal) + '\n')  # 写文档
        # process the data
        processed_strVal = strVal.upper()
        processed_intVal = intVal + 1
        processed_floatVal = floatVal ** 2
        sub_result = [processed_strVal, processed_intVal, processed_floatVal]
        result.append(sub_result)  # 按列表格式返回数据
    f.close()
    arr = np.array(result)
    return result

 在返回之前,生成一个并不使用的变量arr = np.array(result),这个我们就是为了测试第三方包而做的,生成的arr没有任何意义。

再次点击simple_test.exe不会生成b.txt,表示python脚本程序运行错误,第三方包调用失败。

解决方法

1.使用pyinstaller生成依赖文件

这个就需要我们python 的一个包了,需要pip(conda)安装pyinstaller。因为我这里供QT C++ 使用的python环境(D:\workspace\qt\qt_output\python_38)是从D:\miniconda3\envs\cat虚拟环境中复制出来的部分,所以我使用的是激活cat的虚拟环境,并再这里面执行pyinstaller,生成依赖文件。

(cat) PS D:\tmp> conda activate cat
(cat) PS D:\tmp> cd d:/tmp
(cat) PS D:\tmp> pyinstaller D:\workspace\qt\qt_output\py_scripts\py_test.py

1.激活环境,2.生成的依赖在那个文件夹中(随便写的一个文件夹),3.对那个python文件生成依赖

执行完成之后,会在d:/tmp中生成两个文件夹,dist 和 build 

2.我们将dist/py_test/_internal 中的所有文件(夹)全部复制到QT编译生成的qt_output文件夹中

将得到依赖包的qt_output文件夹放在在新机器上部署执行带有第三方包就没有问题了

经本人测试包括cv2包也可以,但是本人对matlablib这个包没有导入成功。

其他说明

1.qt生成的文件一定要注意他们的相对位置,是相对于哪一个文件的位置。

2.QT C++调用python 后,我没有能够进行调试,不知道是什么原因不能调试。

3.python 和C++ 的数据类型,有些QT C++数据类型传到python中不好使用,主要我对QT和C++不了解。

4.带有第三方打包的就比普通打包部署多一步,这一步需要pyinstaller生成动态链接库等文件,复制进和QT C++ 生成的exe同一个文件夹中。

参考网页

Qt调用Python详细图文过程记录_python_脚本之家

在QT C++中调用 Python并将软件打包发布(裸机可运行)_互联网集市

C++调用python脚本 - 知乎

如何在C++中使用一个Python类-[PyImport_ImportModule、PyModule_GetDict、PyDict_GetItemString、PyObject_CallFuncti]-CSDN博客

Qt项目中C++调用Python函数传多参问题_qt调用python_平头猿小哥的博客-CSDN博客

C++调用Python(混合编程)函数整理总结_jindayue的博客-CSDN博客

PyObject_CallObject, PyObject_Call, PyObject_CallFunction使用例子-CSDN博客

Qt C++ Python 混合编程测试文档 - 知乎

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

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

相关文章

pgz easyexcel如何给excel文件添加自定义属性

免费API方式 直接上传URL,自定义修改Excel 视频演示【内含接口地址】 https://www.ixigua.com/7304510132812153385 前情提示 | 功能说明 多选仅支持微软office、office365系列Excel。因为WPS宏功能需要企业版且付费生成xlsx、xlsm等文件,office和WPS均可以打开,均可以单…

红队攻防实战系列一之Cobalt Strike

他日若遂凌云志&#xff0c;敢笑黄巢不丈夫 本文首发于先知社区&#xff0c;原创作者即是本人 前言 在红队攻防中&#xff0c;需要我们拥有综合能力&#xff0c;不仅仅是web渗透的漏洞挖掘与利用&#xff0c;边界突破的方式有很多种&#xff0c;当然这需要我们拥有很强的意识…

(4)BUUCTF-web-[极客大挑战 2019]EasySQL1

前言&#xff1a; 觉得这个题目挺有意义的&#xff0c;因为最近在学数据库&#xff0c;但是不知道在现实中有什么应用&#xff0c;所以学起来也没有什么兴趣&#xff0c;做了这个题目&#xff0c;发现数据库还是挺有用处的&#xff0c;哈哈 知识点&#xff1a; mysql 中and和…

incast,拥塞控制,内存墙的秘密

数据中心 incast&#xff0c;广域网拥塞&#xff0c;内存墙都是一类问题。 我接触 incast 很久了&#xff0c;大多是帮忙查问题&#xff0c;也解过几例。 我记得有一次在业务几乎总是(在统计学上&#xff0c;几乎和总是属同义强调) tail latency 很大时&#xff0c;我建议在 …

【Python3】【力扣题】349. 两个数组的交集

【力扣题】题目描述&#xff1a; 【Python3】代码&#xff1a; 1、解题思路&#xff1a;集合的交集。两个数组都转为集合&#xff0c;获取集合的交集。 知识点&#xff1a;set(...)&#xff1a;转为集合&#xff0c;集合的元素不重复。 集合1.intersection(集合2)&#xff1a…

将 Hexo 部署到阿里云轻量服务器(保姆级教程)

将 Hexo 部署到阿里云轻量服务器(保姆级教程) 顺哥轻创 1 前言 作为有梦想的,有追求的程序员,有一个自己的个人博客简直就是必须品。你可以选择 wordpress 这种平台,直接使用,在任何地方只要有网络就能写博客。还可以选择 hexo 这种静态博客,但是发文章就没有那么随心…

基于opencv+ImageAI+tensorflow的智能动漫人物识别系统——深度学习算法应用(含python、JS、模型源码)+数据集(三)

目录 前言总体设计系统整体结构图系统流程图 运行环境爬虫模型训练实际应用 模块实现1. 数据准备1&#xff09;爬虫下载原始图片2&#xff09;手动筛选图片 2. 数据处理1&#xff09;切割得到人物脸部2&#xff09;重新命名处理后的图片3&#xff09;添加到数据集 3. 模型训练及…

webpack 打包优化

在vue.config.js中配置 下载 uglifyjs-webpack-plugin 包 const { defineConfig } require("vue/cli-service"); var path require("path");module.exports defineConfig({transpileDependencies: true,filenameHashing: false, // 去除Vue打包后.cs…

SSM手机资讯网站系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 手机资讯网站系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模…

PHP 针对mysql 自动生成数据字典

PHP 针对mysql 自动生成数据字典 确保php 可以正常使用mysqli 扩展 这里还需要注意 数据库密码 如果密码中有特殊字符 如&#xff1a; 首先&#xff0c;我们需要了解MySQL中的特殊字符包括哪些。MySQL中的特殊字符主要包括以下几类&#xff1a; 1. 单引号&#xff08;&a…

【开源】基于Vue和SpringBoot的农家乐订餐系统

项目编号&#xff1a; S 043 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S043&#xff0c;文末获取源码。} 项目编号&#xff1a;S043&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户2.2 管理员 三、系统展示四、核…

jvm优化之:OOM(out of memory)内存溢出

内存溢出 注意内存溢出不是内存泄漏&#xff01;&#xff01;这里主要是介绍如何用jdk自带的jmap工具导出进程堆空间快照。内存溢出&#xff1a; Out Of Memory&#xff0c;是指申请的堆内存空间不够用了&#xff0c;比如&#xff1a;你申请了10M空间&#xff0c;但是你要放12M…

鸿蒙开发-ArkTS 语言-循环渲染

鸿蒙开发-ArkTS 语言-状态管理 4. 渲染控制 对于 UI 渲染&#xff0c;可以基于数据结构选择一些内置方法&#xff08;例如&#xff1a;ForEach&#xff09;快速渲染 UI 结构。 4.1 if-else条件渲染 ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态&#xff0c;使…

ORA-14452: 试图创建, 变更或删除正在使用的临时表中的索引

在编写一个test存储过程中出现一个错误报告:ORA-14452: 试图创建, 变更或删除正在使用的临时表中的索引,代码如下 create or replace PROCEDURE TMP_TRANSCRIPT AS str_sql varchar2(500);v_flag number:0; --标识 begin--判断临时表是否存在SELECT COUNT(*) into v_flag FROM…

【版本管理 | Git 】Git最佳实践系列(一) —— LFS .gitignore 最佳实践,确定不来看看?

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

华大基因基因检测产品发布,助力早发冠心病风险评估

冠状动脉性心脏病&#xff0c;简称冠心病。冠心病作为导致猝死的常见原因之一&#xff0c;近年来备受关注。早发冠心病是指冠心病发病年龄男性≤55岁&#xff0c;女性≤60岁。早发冠心病是一种发病时心肌损伤严重的冠心病&#xff0c;由于心肌缺血&#xff0c;还有可能会导致急…

基于C#实现双端队列

话说有很多数据结构都在玩组合拳&#xff0c;比如说&#xff1a;块状链表&#xff0c;块状数组&#xff0c;当然还有本篇的双端队列&#xff0c;是的&#xff0c;它就是栈和队列的组合体。 一、概念 我们知道普通队列是限制级的一端进&#xff0c;另一端出的 FIFO 形式&#…

类和对象(4)——补充内容+DateOJ题

Date类型的OJ 一&#xff0c;static成员例题 二&#xff0c;DateOJ题一&#xff0c;[计算日期到天数转换](https://www.nowcoder.com/practice/769d45d455fe40b385ba32f97e7bcded?tpId37&&tqId21296&rp1&ru/activity/oj&qru/ta/huawei/question-ranking)1…

Python 自动化用处太大了!|python自动整理文件,一键完成!

随着时代的发展及人工智能的到来&#xff0c;Python 自动化办公能力几乎已成为每个岗位的必备技能&#xff01; 而且到处可见的抖音、朋友圈铺天盖地宣传 Python 可以轻松达到办公自动化&#xff0c;并且学习没门槛&#xff0c;是真的吗&#xff1f; 我很负责的告诉大家&#…

数字化转型如何赋能企业实现数字化增值?

随着科技的不断发展&#xff0c;数字化转型已经成为了企业营销的重要趋势。数字化转型不仅可以提高企业的运营效率&#xff0c;还可以更好地满足消费者的需求&#xff0c;提升企业的市场竞争力。 一、数字化转型可以提高企业营销的精准性 在传统的企业营销中&#xff0c;营销人…