python - Tkinter 程序在 IDE (Visual Studio) 中运行良好,但是当使用 pyinstaller 编译为 .exe 时,线程的工作方式与在 IDE 中不同
问题描述
当我在我的 IDE 中运行我的 python 项目时,GUI 和一切都是响应式的并且运行良好。但是当我作为 .exe 运行时,我的线程组件不像在 IDE 中那样工作。该程序的目标是通过 RTSP 获取实时提要并使用 opencv 显示图像。这是在它自己的线程中完成的。
import time
import threading
import cv2
import PIL.Image
"""TODO: add docstring"""
class VideoCapture:
def __init__(self, xmlDict=None, width=None, height=None, fps=None):
"""TODO: add docstring"""
self.xmlDict = xmlDict
self.width = width
self.height = height
self.fps = int(self.xmlDict['FPS'])
self.running = False
# Open the video source
self.vid = cv2.VideoCapture(self.xmlDict['IpAddress'])
if not self.vid.isOpened():
raise ValueError("[MyVideoCapture] Unable to open video source", xmlDict['IpAddress'])
# Get video source width and height
if not self.width:
self.width = int(self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)) # convert float to int
if not self.height:
self.height = int(self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) # convert float to int
if not self.fps:
self.fps = int(self.vid.get(cv2.CAP_PROP_FPS)) # convert float to int
# default value at start
self.ret = False
self.frame = None
self.convert_color = cv2.COLOR_BGR2RGB
#self.convert_color = cv2.COLOR_BGR2GRAY
self.convert_pillow = True
# start thread
self.running = True
self.thread = threading.Thread(target=self.process)
self.thread.start()
def process(self):
"""TODO: add docstring"""
while self.running:
ret, frame = self.vid.read()
if ret:
# process image
frame = cv2.resize(frame, (self.width, self.height))
# it has to record before converting colors
if self.convert_pillow:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = PIL.Image.fromarray(frame)
else:
print('[MyVideoCapture] stream end:', self.video_source)
# TODO: reopen stream
self.running = False
if self.recording:
self.stop_recording()
break
# assign new frame
self.ret = ret
self.frame = frame
# sleep for next frame
#if self.fps != "FULL":
# time.sleep(1/int(self.fps))
我有一个名为 start 的按钮设置,它每 2 秒推断一次图像并打印出标签和置信度。当我在 .exe 中执行此操作时,实时提要和 GUI 在进行推理时会冻结,但是当我在 IDE 中使用程序时,它不会冻结。这是执行此操作的代码。
#Button to start inference
self.btn_snapshot = tk.Button(self.btnFrame,width = 10,height = 2, text="Start", command=lambda:threading.Thread(target = self.snapshot).start())
self.btn_snapshot.grid(row = 1,column = 0)
#snapshot function
def snapshot(self):
self.recording = True
while self.recording:
filename = self.vid.snapshot()
result = self.predictImage(filename)
output = self.calculatePassFail(result)
if self.manager:
self.manager.onClick(output)
else:
print('something')
time.sleep(2)
快照函数调用的另外两个方法是 predictImage 和 calculatePassFail。
def predictImage(self,imageName):
onnxModel = ImageModel.load(self.xmlDict['ModelPath'])
result = onnxModel.predict_from_file(imageName)
return result
def calculatePassFail(self,result):
calcResult = result.labels[0]
self.labelName = calcResult[0]
self.imgScore = calcResult[1]*100
return f"{self.labelName} with score{self.imgScore}"
解决方案
所以我找到了一个解决这个问题的方法,不确定它是否是一个正确的修复,但它可以工作。因此,由于某种原因,当我使用 pyinstaller 创建 .exe 并且有一个控制台窗口时,我遇到了问题,但是当我在使用 pyinstaller 创建 w/out 控制台时使用标志 --noconsole 时,问题就消失了,我对图像的推断起作用了在它自己的线程中,就像在我的 IDE 中一样。不知道为什么会这样,但我猜它有效。
推荐阅读
- android - 我的网址出现错误意外响应代码 404,而凌空发布请求我的代码是
- c++ - 从 regex_search 抛出的 boost regex error_stack 异常
- reverse-engineering - 查找使用 IDA PRO 打开的文件
- php - 未定义的属性:MongoDB\Driver\Manager::$db
- android - Using multiple orderby Firebase Android
- python - 我尝试为 Python 3.7 下载 PyCrypto 但失败了
- php - 使用 Guzzle 6 使用 REST API
- c++ - 内循环的有效并行化
- javascript - Summernote 将光标指向插入的 HTML 末尾
- asp.net-web-api - 在 mvc 控制器中从 web api 调用 PUT 方法