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

工作中遇到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";elseqDebug()<<"[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";elseqDebug()<<"[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");  // 脚本名称,不带.pyif(!pModule)  // 脚本加载成功与否qDebug()<<"[db:] pModule fail";elseqDebug()<<"[db:] pModule success";// 创建函数指针PyObject* pFunc= PyObject_GetAttrString(pModule,"write_file");  // 方法名称if(!pFunc || !PyCallable_Check(pFunc))  // 函数是否创建成功qDebug()<<"[db:] pFunc fail";elseqDebug()<<"[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";elseqDebug()<<"[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");  // 脚本名称,不带.pyif(!pModule)  // 脚本加载成功与否qDebug()<<"[db:] pModule fail";elseqDebug()<<"[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_lenPyObject* 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 dataprocessed_strVal = strVal.upper()processed_intVal = intVal + 1processed_floatVal = floatVal ** 2sub_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_lenPyObject* 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 dataprocessed_strVal = strVal.upper()processed_intVal = intVal + 1processed_floatVal = floatVal ** 2sub_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";elseqDebug()<<"[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");  // 脚本名称,不带.pyif(!pModule)  // 脚本加载成功与否qDebug()<<"[db:] pModule fail";elseqDebug()<<"[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_lenPyObject* 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.uiDESTDIR = $$PWD/../qt_outputINCLUDEPATH += -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 npdef 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 dataprocessed_strVal = strVal.upper()processed_intVal = intVal + 1processed_floatVal = floatVal ** 2sub_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.mzph.cn/news/167949.shtml

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

相关文章

electron windows robotjs 安装教程

Robotjs 安装 前言第一步 : 安装python第二步 : 安装Visual Studio 2022第三步 : 安装robotjs 前言 robotjs可以控制鼠标键盘&#xff0c;获取屏幕内容&#xff0c;配合electron可做很多自动化操作。windows下配置环境有很多坑&#xff0c;很多文章都太旧了。试了很多次发现了…

ky10 server x86 auditd安装(日志审计系统)

概述 Auditd工具可以帮助运维人员审计Linux&#xff0c;分析发生在系统中的发生的事情。Linux 内核有用日志记录事件的能力&#xff0c;包括记录系统调用和文件访问。管理员可以检查这些日志&#xff0c;确定是否存在安全漏洞&#xff08;如多次失败的登录尝试&#xff0c;或者…

golang学习笔记——接口和继承比较2

接口和继承 现在有一个需要要求大学生和足球运动员掌握英语技能&#xff0c;请问怎么实现? 给运动员和学生结构体添加studyEnglish方法显示是可以的&#xff0c;但是篮球动员和中学生也学习了英语&#xff0c;显示不行。这时&#xff0c;我们可以直接给足球运动员和大学生添加…

跳转应用市场详情页market

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 未经允许不得转载 目录 一、导读二、概览三、跳转到各大厂商应…

播放器开发(四):多线程解复用与解码模块实现

学习课题&#xff1a;逐步构建开发播放器【QT5 FFmpeg6 SDL2】 前言 根据第一章内容&#xff0c;我们首先可以先把解复用和解码模块完成&#xff0c;其中需要使用到多线程以及队列&#xff0c;还需要使用FFmpeg进行解复用和解码动作的实现。 创建BaseQueue基类 BaseQueue.h…

亚马逊两步验证有哪些验证方法?

亚马逊通常提供多种两步验证的方式&#xff0c;包括短信&#xff08;通过手机接收验证码&#xff09;和认证器应用程序&#xff08;如Google Authenticator、Authy等&#xff09;。选择你偏好的方式。 短信验证&#xff1a; 如果选择短信验证&#xff0c;需要将你的手机号码关联…

YOLOv8改进 | 2023 | LSKAttention大核注意力机制助力极限涨点

论文地址&#xff1a;官方论文地址 代码地址&#xff1a;官方代码地址 一、本文介绍 在这篇文章中&#xff0c;我们将讲解如何将LSKAttention大核注意力机制应用于YOLOv8&#xff0c;以实现显著的性能提升。首先&#xff0c;我们介绍LSKAttention机制的基本原理&#xff0c;…

定制手机套餐---python序列

if __name__ __main__:print("定制手机套餐")print("")#定义电话时长&#xff1a;字典callTimeOptions{1:0分钟,2:50分钟,3:100分钟,4:300分钟,5:不限量}keyinput("请输入电话时长的选择编号&#xff1a;")valuecallTimeOptions.get(key)if val…

代码随想录算法训练营第五十四天|392.判断子序列 115.不同的子序列

文档讲解&#xff1a;代码随想录 视频讲解&#xff1a;代码随想录B站账号 状态&#xff1a;看了视频题解和文章解析后做出来了 392.判断子序列 class Solution:def isSubsequence(self, s: str, t: str) -> bool:dp [[0] * (len(t)1) for _ in range(len(s)1)]for i in ra…

RabbitMq使用与整合

MQ基本概念 MQ概述 MQ全称 Message Queue&#xff08;[kjuː]&#xff09;&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。 &#xff08;队列是一种容器&#xff0c;用于存放数据的都是容器&#xff0c;存…

优秀的时间追踪软件Timemator for Mac轻松管理时间!

在现代社会&#xff0c;时间管理成为了我们工作和生活中的一大挑战。如果你经常感到时间不够用&#xff0c;无法高效地完成任务&#xff0c;那么Timemator for Mac将成为你的得力助手。 Timemator for Mac是一款出色的时间追踪软件&#xff0c;它可以帮助你精确记录和管理你的…

Linux的基本指令 ( 一 )

目录 前言 Linux基本指令 快速认识五个指令 ls指令 补充内容 pwd指令 补充内容 cd指令 补充内容 重新认识指令 指令的本质 which指令 alias指令 最后 一个文件的三种时间 tree指令及安装 tree指令 前言 关于Linux操作系统的桌面&#xff0c;在学校教学中我们…

实用高效 无人机光伏巡检系统助力电站可持续发展

近年来&#xff0c;我国光伏发电行业规模日益壮大&#xff0c;全球领先地位愈发巩固。为解决光伏电站运维中的难题&#xff0c;浙江某光伏电站与复亚智能达成战略合作&#xff0c;共同推出全自动无人机光伏巡检系统&#xff0c;旨在提高发电效率、降低运维成本&#xff0c;最大…

Spark---SparkCore(一)

一、术语与宽窄依赖 1、术语解释 1、Master(standalone):资源管理的主节点&#xff08;进程&#xff09; 2、Cluster Manager:在集群上获取资源的外部服务(例如&#xff1a;standalone,Mesos,Yarn) 3、Worker Node(standalone):资源管理的从节点(进程)或者说管理本机资源的…

用Python写一个浏览器集群框架

更多Python学习内容&#xff1a;ipengtao.com 在分布式爬虫和大规模数据采集的场景中&#xff0c;使用浏览器集群是一种有效的方式&#xff0c;可以提高数据采集的速度和效率。本文将介绍如何用Python编写一个简单但强大的浏览器集群框架&#xff0c;以应对需要使用多个浏览器实…

WebGL/threeJS面试题扫描与总结

什么是 WebGL&#xff1f;什么是 Three.js&#xff1f;请解释three.js中的WebGL和Canvas的区别&#xff1f; WebGL(全写Web Graphics Library)是一种3D绘图协议&#xff0c;这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起&#xff0c;通过增加OpenGL ES 2.0的一个…

思科模拟器操作命令

模式 思科模拟器常见的模式有 用户模式 能够操作的命令比较少 特权模式特权模式下面可以操作的比较多 全局模式 接口模式 用户模式进入特权模式: 命令enable 特权模式进行全局模式命令: configure terminal 退出命令 exit命令&#xff1a;返回上一层&#xff0c;即一步一步…

RocketMQ 消息中间件 知识点汇总

目录 RocketMQ1、什么是RocketMQ?常用术语:2、为什么需要消息队列3、什么是异步处理4、什么是服务解耦5、什么是流量控制6、消息队列两种模型队列模型:发布/订阅模型:总结:7、怎么保证消息不丢失8、如何处理消息被重复消费**出现消息重复的情况:****解决方法:**9、如何保…

流量分析-PhishingEmail_WriteUp

一、题目问题 问题1&#xff1a;黑客的email名称 问题2&#xff1a;黑客向几人发送了钓鱼邮件 问题3&#xff1a;黑客传输的木马文件名 问题4&#xff1a;下载并运行了木马文件的人的email名称和ip地址&#xff0c;用“-”连接 问题5&#xff1a;黑客用于反弹shell的主机i…

什么葡萄酒会适用这种双重滗析方法呢?

滗析有两个主要目的&#xff0c;一种是去除陈年或未经过滤的葡萄酒中的沉淀物。虽然沉淀物不会对你造成任何伤害&#xff0c;但当喝葡萄酒满嘴都是葡萄沉淀物时是一件很糟糕的事。其次&#xff0c;倾析葡萄酒是可以让葡萄酒“呼吸”与氧气接触的&#xff0c;氧气可以软化单宁&a…