Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序

1.ESP32-CAM WiFi获取视频流以及保存图像到TF卡

1.1 驱动ESP32-CAM

笔者使用Arduino编写ESP32-CAM的驱动程序,版本为1.8.19。在较新的版本中,Arduino的UI风格发生了变化,不过下面配置的功能基本保留,读者注意辨别其中的异同之处。

1.1.1 在Arduino中配置开发环境

1.首先,我们需要在Arduino中配置ESP32开发板的开发环境。打开Arduino,按如下路径依次点击:“文件” → \rightarrow “首选项”,找到“附加开发板管理器网址”,如图1.1所示。
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第1张图片

图1.1 找到“附加开发板管理器网址”

Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第2张图片

图1.2 输入附加网址的界面

3.按照界面上“一行一个”的指示,将下面两个网址输入进去:

https://dl.espressif.com/dl/package_esp32_index.json

https://github.com/espressif/arduino-esp32/releases/download/2.0.2/package_esp32_dev_index.json

然后点击“好”即可。

1.1.2 在Arduino中配置开发板

1.接下来我们需要配置开发板。按如下路径依次点击:“工具” → \rightarrow “开发板” → \rightarrow “开发板管理器”,弹出界面如图1.3:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第3张图片

图1.3 开发板管理器

2.在显示有“对搜索进行过滤…”字样的搜索框内输入“ESP32”,显示界面如图1.4:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第4张图片

图1.4 搜索ESP32开发板

3.选择版本2.0.2,然后点击“安装”,等待其安装好即可。

4.安装好后,按如下路径点击:“工具” → \rightarrow “开发板” → \rightarrow “ESP32 Arduino” → \rightarrow “AI Thinker ESP32 CAM”。至此,我们就配置好了ESP32的开发环境和开发板,可以进行下一步的开发了。

1.1.3 驱动ESP32-CAM

1.由于官方的库并不能驱动ESP32-CAM,因此在此处我参考了CSDN用户“ShemuelHe”的博客。博客链接为本节最后的参考资料当中的第二个链接。在此处,我们需要使用GitHub上大神yoursunny用户所提供的库。下载链接为本节参考资料的第一个链接。点进他的主页后,如图1.5所示:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第5张图片

图1.5 yoursunny的github主页

2.点击“Download ZIP”,将代码压缩包下载下来。然后回到Arduino,按如下路径点击:“项目” → \rightarrow “加载库” → \rightarrow “添加.ZIP库”,弹出界面如图1.6所示:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第6张图片

图1.6 加载库的界面

3.找到刚才下载的库的路径,找到.ZIP文件(该压缩包不需要解压),选中后点击“打开”。这样,这个库就添加好了。其他项目中,如果要添加非官方库,也可以通过这样的方式。

1.1.4 完整的驱动代码(WiFi接入热点、将视频流通过WiFi发送、识别TF卡、存储照片到TF卡)

完整Arduino代码(经过测试,直接复制可用)

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "cJSON.h"
#include "FS.h"
#include "esp_camera.h"

//以下改成要连接的WiFi名称和密码
const char* WIFI_SSID = "******"; 
const char* WIFI_PASS = "******";

WebServer server(80);

static auto loRes = esp32cam::Resolution::find(320, 240);
static auto hiRes = esp32cam::Resolution::find(1280, 1024);
//UXGA:分辨率为1600*1200的输出格式,SXGA(1280*1024)、XVGA(1280*960)、WXGA(1280*800)、XGA(1024*768)、SVGA(800*600)、VGA(640*480)、CIF(352*288)和QQVGA(160*120)等。

char path[] = "/1.jpg";
int order = 1;

void handleBmp()
{
  if (!esp32cam::Camera.changeResolution(loRes)) {
    Serial.println("SET-LO-RES FAIL");
  }

  auto frame = esp32cam::capture();
  if (frame == nullptr) {
    Serial.println("CAPTURE FAIL");
    server.send(503, "", "");
    return;
  }
  Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
                static_cast(frame->size()));

  if (!frame->toBmp()) {
    Serial.println("CONVERT FAIL");
    server.send(503, "", "");
    return;
  }
  Serial.printf("CONVERT OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
                static_cast(frame->size()));

  server.setContentLength(frame->size());
  server.send(200, "image/bmp");
  WiFiClient client = server.client();
  frame->writeTo(client);
}

void serveJpg()
{
  auto frame = esp32cam::capture();
  if (frame == nullptr) {
    Serial.println("CAPTURE FAIL");
    server.send(503, "", "");
    return;
  }
  Serial.printf("CAPTURE OK %dx%d %db\n", frame->getWidth(), frame->getHeight(),
                static_cast(frame->size()));

  server.setContentLength(frame->size());
  server.send(200, "image/jpeg");
  WiFiClient client = server.client();
  frame->writeTo(client);
}

void handleJpgLo()
{
  if (!esp32cam::Camera.changeResolution(loRes)) {
    Serial.println("SET-LO-RES FAIL");
  }
  serveJpg();
}

void handleJpgHi()
{
  if (!esp32cam::Camera.changeResolution(hiRes)) {
    Serial.println("SET-HI-RES FAIL");
  }
  serveJpg();
}

void handleJpg()
{
  server.sendHeader("Location", "/cam-hi.jpg");
  server.send(302, "", "");
}

void handleMjpeg()
{
  if (!esp32cam::Camera.changeResolution(hiRes)) {
    Serial.println("SET-HI-RES FAIL");
  }

  Serial.println("STREAM BEGIN");
  WiFiClient client = server.client();
  auto startTime = millis();
  int res = esp32cam::Camera.streamMjpeg(client);
  if (res <= 0) {
    Serial.printf("STREAM ERROR %d\n", res);
    return;
  }
  auto duration = millis() - startTime;
  Serial.printf("STREAM END %dfrm %0.2ffps\n", res, 1000.0 * res / duration);
}

// Init SD Card
void sd_init()
{
  //The argument ("/sdcard",true) means closing LED light on the board
  if (!SD_MMC.begin("/sdcard",true)) { 
    Serial.println("Card Mount Failed");
    return;
  }
  uint8_t cardType = SD_MMC.cardType();
  if (cardType == CARD_NONE) {
    Serial.println("No SD card attached");
    return;
  }
  Serial.print("SD Card Type: ");
  if (cardType == CARD_MMC) {
    Serial.println("MMC");
  }
  else if (cardType == CARD_SD) {
    Serial.println("SDSC");
  }
  else if (cardType == CARD_SDHC) {
    Serial.println("SDHC");
  }
  else {
    Serial.println("UNKNOWN");
  }

//Get the size of SD card, unit: MB
  uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);   
  Serial.printf("SD 卡容量大小: %lluMB\n", cardSize);
}

void setup()
{
  Serial.begin(115200);
  Serial.println();

  {
    using namespace esp32cam;
    Config cfg;
    cfg.setPins(pins::AiThinker);
    cfg.setResolution(hiRes);
    cfg.setBufferCount(2);
    cfg.setJpeg(80);

    bool ok = Camera.begin(cfg);
    Serial.println(ok ? "CAMERA OK" : "CAMERA FAIL");
  }
  sd_init();
  delay(5000);
  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }

  Serial.print("http://");
  Serial.println(WiFi.localIP());
  Serial.println("  /cam.bmp");
  Serial.println("  /cam-lo.jpg");
  Serial.println("  /cam-hi.jpg");
  Serial.println("  /cam.mjpeg");

  server.on("/cam.bmp", handleBmp);
  server.on("/cam-lo.jpg", handleJpgLo);
  server.on("/cam-hi.jpg", handleJpgHi);
  server.on("/cam.jpg", handleJpg);
  server.on("/cam.mjpeg", handleMjpeg);

  server.begin();
}

void loop()
{
  server.handleClient();
  camera_fb_t * fb = esp_camera_fb_get();
  sprintf(path,"/%d.jpg",order);
  if (fb == NULL)
  {
    Serial.println( "Get picture failed");
  }
  else
  {
    fs::FS &fs = SD_MMC;
    Serial.printf("Writing file: %s\n", path);
    File file = fs.open(path, FILE_WRITE);
    if (!file)
    {
      Serial.println("Failed to Create a File!");
    }
    else
    {
      file.write(fb->buf , fb->len);
    }
    esp_camera_fb_return(fb);
    order += 1;
  }
}

最终跑出来的效果如图1.7、1.8所示:

1.Python读取URL获取视频流
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第7张图片

图1.7 Python小程序读取视频流

图中“图像显示”的程序是通过Python编写,这一部分涉及PyQt5的使用,将在1.2节详细介绍。

2.保存拍摄的照片到TF卡
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第8张图片

图1.8 保存到TF卡中的照片

注意: ESP32-CAM的TF卡槽最多支持4G容量的TF卡,超过此容量的TF卡均不能被成功识别。

此部分参考资料如下:

1.GitHub - yoursunny/esp32cam: OV2640 camera on ESP32-CAM, Arduino library / https://github.com/yoursunny/esp32cam

获取视频流的ESP32代码包

2.【ESP32-CAM】使用opencv获取ESP32-CAM视频流(一)_ShemuelHe的博客-CSDN博客_esp32移植opencv / https://blog.csdn.net/ShemuelHe/article/details/121365730?utm_source=app&app_version=5.0.1&code=app_1562916241&uLinkId=usr1mkqgl919blen

WiFi获取视频流,python openCV实现视频流获取

3.(ESP32学习16)ESP32_CAM获取图片并且保存文件名为当前时间_bird1999625的博客-CSDN博客 / https://blog.csdn.net/ailta/article/details/106866261

CAM摄像头拍摄图像并保存至TF卡

1.2 Python PyQt 5编写图像显示的.exe程序

在1.1.4节中介绍了电脑端读取URL图像,获取视频流的效果。该节中的参考链接2中提供了OpenCV的方式来读取视频流。而由于我们需要将程序移植到不同的电脑上使用,因此需要将Python脚本打包成.exe执行文件。这一节将介绍如何使用Python读取URL图像并将整个程序打包成.exe可执行文件,使其在没有安装Python开发环境的电脑上也能运行。

1.2.1 安装Python

此部分内容在网上有很多详细的资料,此处不再赘述,可参考本节末尾的参考资料当中的第一个链接,相当详细,将每一步都列举了出来,按操作即可成功安装。

1.2.2 安装PyQt5包

1.打开PyCharm,新建工程和py脚本,然后按照如下路径依次点击:“文件” → \rightarrow “设置” → \rightarrow “项目” → \rightarrow “Python解释器”,界面如图1.9所示:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第9张图片

图1.9 Python解释器界面

2.点击红圈当中的“+”按钮,进入下面的界面:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第10张图片

图1.10 Python安装库的界面

3.在左上角画红色线的搜索栏中输入“PyQt5”;然后选中右下角橙色线处“指定版本”,选择最新的版本;最后点击左下角红圈圈住的“安装软件包”,等待安装这个包即可。

4.同样,在此工程中,需要安装“PyQt5-tools”包。操作方法同上。

5.安装好了之后,找到这两个包的安装位置。笔者的位置如下:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第11张图片

图1.11 PyQt5及tools包的安装位置

这两个包安装在路径“\UITest\venv\Lib\site-packages”中。

1.2.3 添加外部工具

1.按如下路径依次点击:“文件” → \rightarrow “设置” → \rightarrow “项目” → \rightarrow “工具” → \rightarrow “外部工具” ,进入如下界面:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第12张图片

图1.12 外部工具界面

2.点击上图中左上角的“+”,弹出如下窗口:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第13张图片

图1.13 外部工具添加与配置界面

3.在“名称”栏中,输入外部工具的名字,在这里我们将其命名为“QtDesigner”;在“程序栏”中,输入“designer.exe”的路径;在“工作目录”栏中,输入“$FileDir$”。其中,“designer.exe”的路径如下:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第14张图片

图1.16 designer.exe的路径

输入好后,界面如下:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第15张图片

图1.17 配置好后编辑工具的界面

4.同样,我们需要添加外部工具“pyuic5.exe”程序。该程序将QtDesigner中设计好的UI界面转化成Python脚本,供我们编程开发使用。在“外部工具”中,再点一次“+”,将该工具添加进来:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第16张图片

图1.18 添加pyuic5.exe

我们将该工具命名为PyUIC,“程序”一栏添加pyuic5.exe文件的路径;“实参”一栏添加如下信息:$FileName$ -o $FileNameWithoutExtension$.py;“工作目录”一栏添加:$ProjectFileDir$。然后点击“确定”。这样,我们就添加好了我们所需要的外部工具。

1.2.4 QtDesigner的使用

在1.2.3节中,我们下载好了开发.exe文件所需要的软件包,并添加好了外部工具。至此,准备工作已经全部完成,我们可以开始使用QtDesigner来开发我们的软件了。

1.打开Qt:在PyCharm顶端的菜单栏中,按照如下顺序点击:“工具” → \rightarrow “External tools” → \rightarrow “QtDesigner”:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第17张图片

图1.19 打开QtDesigner

打开后,界面如图1.20所示:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第18张图片

图1.20 QtDesigner的界面

左侧为常用的一些控件树;中间的部分为设计工具提供给我们的一些模板;右侧为控件树和当前选中的控件的一些属性。在这里,我们选择“Main Window”,点击“Create”,界面如图1.21所示:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第19张图片

图1.21 选择Main Window后设计工具的界面

这个时候,我们就可以添加各种控件并给它们配置属性,以达到我们的目的。

2.关于如何布局,读者可参考白月黑羽的教程,相当详细Python Qt 图形界面编程 - PySide2 PyQt5 PyQt PySide_哔哩哔哩_bilibili / https://www.bilibili.com/video/BV1cJ411R7bP?spm_id_from=333.999.0.0,此文档中不再赘述。读者需要尤其注意Layout的使用。本项目中需要使用PyQt5开发的程序较为简单,就是读取URL获取视频流,因此,笔者所设计的UI布局如图1.22所示:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第20张图片

图1.22 笔者所设计的UI界面

在这个界面中,我使用了一个Label控件用来显示图像,三个按钮控件来触发事件,一个文本框用来输入URL地址。控件添加完成后,使用Layout进行布局。

1.2.5 美化控件(QSS)

在QtDesigner中,我们可以通过QSS的方式来美化控件。此处我以按钮控件为例,简要介绍QSS的使用。

1.选中按钮控件,在属性栏中,找到“Qwidget” → \rightarrow “Stylesheet”,点击“StyleSheet”右侧的三点按钮:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第21张图片

图1.23 进入QSS的路径

弹出界面如图1.24所示:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第22张图片

图1.24 QSS代码编写窗口

2.在这个编辑框里,我们可以输入如下格式的代码:

QPushButton {// 按钮一般属性
    background-color: white ;
    font-size:16px;
    color:black;
    border-radius: 15px;//圆角半径
    font-family:微软雅黑;//字体
    background:rgb(255, 255, 255);//背景颜色
    border:2px solid black;//边框宽度
}
QPushButton:hover{ //鼠标悬浮在按钮上时按钮的属性
    background:rgb(237, 108, 0, 150);//鼠标悬浮时背景颜色为rgb(237,108,0,150)
}
QPushButton:pressed{ //鼠标按下时按钮的属性
    background:white;//按下鼠标时背景颜色为白色
}

3.然后点击“OK”即可。这个时候,控件的外观就会按照我们代码所设定的样子显示出来。

1.2.6 将UI转化成.py文件

设计好UI界面后,我们就需要将UI文件转化成.py文件,在PyCharm编辑器中编写程序了。

1.在左侧的文件预览器中,找到我们的.ui文件,右键单击,然后左键依次点击:“External tools” → \rightarrow “PyUIC”:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第23张图片

图1.25 PyUIC的开启路径

2.点击后,会生成一个和.ui文件同名的.py文件。控制台和文件树如下所示:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第24张图片

图1.26 转化得到的.py文件的控制台和文件树

3.双击打开VideoShow.py文件,这时候编辑器就会显示我们所创建的UI对应的.py文件代码了。

1.2.7 运行出ui

当我们有了.ui文件对应的.py文件后,我们就需要将这个ui使用代码运行起来,最终实现我们想要的功能。在这一节,我将以项目中的图像显示为例,简要介绍如何将我们创建的ui在PyCharm中运行出来。

1.在.ui文件和刚才生成的.py文件同一个文件夹下新建一个.py文件,在这里,我将其命名为“Test.py”。

2.添加如下代码:

import sys
import requests
import VideoShow
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtGui import QImage, QPixmap, QIcon

class MainWindows(QMainWindow, VideoShow.Ui_ShowVideo):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setupUi(self)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = MainWindows()
    # 设置软件窗口的名字
    main.setWindowTitle('Submarine 图像显示') 
    # 设置软件的图标。注意,"icon_16.png"文件需要和工程在同一文件夹下。
    main.setWindowIcon(QIcon("icon_16.png"))
    # 显示UI
    main.show()
    sys.exit(app.exec_())
    

3.运行上述代码,我们就可以看到我们刚才设计的UI界面了:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第25张图片

图1.27 运行代码得到的程序UI界面
1.2.8 信号和槽函数

在1.2.7节,我们成功地将我们的UI界面运行了起来。但是,这个时候这个界面是没有什么用的——我们还没有给相关控件添加功能代码,使它们发挥各自的作用。在这里,我们就需要给控件编写信号和槽函数的相关代码了。关于信号和槽的基本概念,在1.2.4节的链接中也有较为详细的介绍。简单来说,当我们点击按钮时,发送一个信号,这个信号被连接到一个槽函数当中,该函数就会执行相应的功能代码。在这个工程中,我们需要给三个按钮编写槽函数,并且开启一个定时器,每隔一段时间读取一次URL。在这一节中,我将简单介绍如何编写功能代码。

1.控件命名

在QtDesigner中的控件树中,我们可以给我们的控件命名:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第26张图片

图1.28 命名控件

例如此处,我将按钮控件分别命名为“CloseAppButton”、“CloseVideoButton”、“OpenVideoButton”。在PyUIC生成的代码中,我们如果想要调用这几个控件,就需要调用这些变量名。

2.槽函数的编写

首先,我们编写一下“打开视频”这个按钮的槽函数。代码如下:

def onOpenVideoButtonClicked(self):
    self.timer.start(20)  # 设置计时间隔并启动,间隔20ms
    self.VideoShowLabel.setScaledContents(True)
    

在这个函数中,我们将定时器启动,并设置读取时间间隔为20ms;设置显示图像的Label控件为自适应图片大小。那么,这个槽函数名就叫做“onOpenVideoButtonClicked”,“打开视频”按钮被按下后,程序就会执行该函数当中的代码。

3.将信号连接到槽函数上

编写好了槽函数后,我们需要将信号连接到槽函数上。比如,我们需要将“打开视频”按钮被点击的信号连接到刚才我们编写的槽函数上,以使槽函数执行功能。连接信号的代码如下:

self.OpenVideoButton.clicked.connect(self.onOpenVideoButtonClicked)

这样,当我们运行UI后,点击这些控件,程序就会执行相应的功能了。

4.整个App的代码如下:

import sys
import requests
import VideoShow
from PyQt5.QtGui import QImage, QPixmap, QIcon
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import QTimer

class MainWindows(QMainWindow, VideoShow.Ui_ShowVideo):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setupUi(self)
        # 初始化一个定时器
        self.timer = QTimer(self)
        # 计时结束调用operate()方法
        self.timer.timeout.connect(self.operate)

        # 将开启信号与槽函数关联
        self.OpenVideoButton.clicked.connect(self.onOpenVideoButtonClicked)
        self.CloseVideoButton.clicked.connect(self.onCloseVideoButtonClicked)
        self.CloseAppButton.clicked.connect(self.onCloseAppButtonClicked)

    def onOpenVideoButtonClicked(self):
        self.timer.start(20)  # 设置计时间隔并启动,间隔20ms
        self.VideoShowLabel.setScaledContents(True)

    def onCloseVideoButtonClicked(self):
        self.timer.stop()  # 关闭定时器

    def onCloseAppButtonClicked(self):
        self.timer.stop()  # 关闭定时器
        self.close() # 关闭应用程序

        # 定时器的处理函数
    def operate(self):
        url = self.URLEdit.text() # 获取URL
        # print(url)
        res = requests.get(url)
        img = QImage.fromData(res.content)
        self.VideoShowLabel.setPixmap(QPixmap.fromImage(img))
        self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = MainWindows()
    main.setWindowTitle('Submarine 图像显示')
    main.setWindowIcon(QIcon("icon_16.png"))
    main.show()
    sys.exit(app.exec_())

这样,我们就编写好了一个简单的UI界面。

1.2.9 封装打包

1.和1.2.2节安装PyQt5包一样,我们需要将“pyinstaller”这个包下载下来:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第27张图片

图1.29 下载安装pyinstaller包

点击“安装软件包”,等待这个包安装好即可。

2.在控制台下端找到“终端”并点击,出现如下界面:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第28张图片

图1.30 进入PyCharm的终端界面

3.然后在上面的窗口中输入如下指令:

pyinstaller -F -w -i icon.ico Test.py  

其中,“-F”表示打包后只生成一个.exe文件(也可以理解为覆盖掉之前产生的同名.exe文件);“-w”表示不使用控制台;“-i”表示改变生成的.exe文件的图标,后面要跟上图标文件的文件名和格式。一般这里支持.ico格式,读者可以在PhotoShop中制作好自己的图标并将其放在工程文件夹下。最后,添加上我们要打包的.py文件名。

常用选项及说明如下:

  • **-F:**打包后只生成单个exe格式文件;
  • **-D:**默认选项,创建一个目录,包含exe文件以及大量依赖文件;
  • **-c:**默认选项,使用控制台(就是类似cmd的黑框);
  • **-w:**不使用控制台;
  • **-p:**添加搜索路径,让其找到对应的库;
  • **-i:**改变生成程序的icon图标。

4.按下回车,等待打包完毕:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第29张图片

图1.31 打包完成时终端的显示内容

5.打开我们的工程文件夹下的“dist”文件夹,如下图所示:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第30张图片

图1.32 .exe文件的路径

可以看到我们刚才生成的.exe文件了。将其复制到我们存放.ico文件的文件夹中(否则图标将不会显示)并双击打开,程序就可以正常运行了:
Arduino开发ESP32-CAM模块 & 使用Python-PyQt5编写图传.exe独立程序_第31张图片

图1.33 双击.exe文件运行出的程序UI界面

至此,我们就制作完成了图像传输的简单.exe程序。该程序可以在没有安装python环境的计算机中运行。

此部分参考资料如下:

1.PyCharm2021安装教程_学习H的博客-CSDN博客_pycharm2021安装教程

2. 将python程序打包成exe_蹦跶的小羊羔的博客-CSDN博客_python打包成exe

3.Python Qt 图形界面编程 - PySide2 PyQt5 PyQt PySide_哔哩哔哩_bilibili

4.Python Qt 简介 | 白月黑羽 (byhy.net)

你可能感兴趣的:(python,qt,ui,小程序,单片机)