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.pswp.cn/news/167949.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/167949.shtml
英文地址,請注明出處:http://en.pswp.cn/news/167949.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【 Kubernetes 風云錄 】- Istio 應用多版本流量控制

文章目錄 原理實現DeploymentVirtualServiceDestinationRule 約束部署 目的: 根據不同的引擎版本&#xff0c;可以把請求發送到指定的引擎上。可以實現版本降級。 原理 Istio通過VirtualService和DestinationRule兩個資源對象來實現流量管理&#xff0c;其中VirtualService用于…

LeetCode Hot100 98.驗證二叉搜索樹

題目&#xff1a; 給你一個二叉樹的根節點 root &#xff0c;判斷其是否是一個有效的二叉搜索樹。 有效 二叉搜索樹定義如下&#xff1a; 節點的左子樹只包含 小于 當前節點的數。節點的右子樹只包含 大于 當前節點的數。所有左子樹和右子樹自身必須也是二叉搜索樹。 方法一…

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;…

loginctl - 控制 systemd 登錄管理器

loginctl loginctl用途loginctl安裝開啟loginctl服務session操作user操作管理用戶服務 loginctl - Control the systemd login manager Redhat/centos平臺使用loginctl管理登錄用戶與session loginctl用途 控制 systemd 登錄管理器管理當前登錄的用戶和session loginctl安裝…

Peter算法小課堂—高精度加法

指針與數組 看看以下代碼&#xff0c;請預測答案 #include <bits/stdc.h> using namespace std; int x[10]{0,1,2,3,4,5,6,7,8,9}; int main(){cout<<x<<endl;cout<<x3<<endl;cout<<*x<<endl;cout<<*(x7)<<endl;cout&…

定制手機套餐---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…

Java 關于批量插入遇到的問題 -sqlserver

序言&#xff1a; 我們在做項目的時候&#xff0c;經常會遇到&#xff0c;對數據的新增動作&#xff0c;如果數據量很少的情況下&#xff0c;單個新增對性能還好&#xff0c;但是一旦涉及到 大數據量&#xff0c;如十萬&#xff0c;百萬&#xff0c;千萬&#xff0c;這個時候如…

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;以應對需要使用多個瀏覽器實…