Tkinter 程序在 IDE (Visual Studio) 中运行良好,但是当使用 pyinstaller 编译为 .exe 时,线程的工作方式与在 IDE 中不同
Posted
技术标签:
【中文标题】Tkinter 程序在 IDE (Visual Studio) 中运行良好,但是当使用 pyinstaller 编译为 .exe 时,线程的工作方式与在 IDE 中不同【英文标题】:Tkinter program works fine in IDE (Visual Studio) but when using pyinstaller to compile to .exe threading does not work same like it does in IDE 【发布时间】:2021-10-08 23:14:09 【问题描述】:当我在我的 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 scoreself.imgScore"
【问题讨论】:
代码太多了。请尝试将其减少到minimal reproducible example。 仅仅说我的线程组件不像在IDE中那样工作是不够的。请说明有什么不同、不满意或出乎意料的地方。 @BryanOakley 我将代码简化为重要的部分,我保留在 VideoCapture 类中只是为了上下文,并且只包含了线程启动的方法。感谢您的反馈 @BoarGules 感谢您的回复。我添加了更多解释。总而言之,当我对图像进行推断时,就会出现线程问题。 tkinter 上显示的实时提要与 GUI 本身一起冻结,直到图像有结果。 【参考方案1】:所以我找到了解决这个问题的方法,不确定它是否正确,但它可以工作。因此,由于某种原因,当我使用 pyinstaller 创建 .exe 并且有一个控制台窗口时,我遇到了问题,但是当我在使用 pyinstaller 创建无控制台时使用标志 --noconsole 时,问题就消失了,我对图像的推断起作用了在它自己的线程中,就像在我的 IDE 中一样。不知道为什么会这样,但我猜它有效。
【讨论】:
以上是关于Tkinter 程序在 IDE (Visual Studio) 中运行良好,但是当使用 pyinstaller 编译为 .exe 时,线程的工作方式与在 IDE 中不同的主要内容,如果未能解决你的问题,请参考以下文章
用Tkinter打造自己的Python IDE开发工具利用HP_tka模块设计自己的中文代码编辑器
用Tkinter打造自己的Python IDE开发工具利用HP_tka模块设计自己的中文代码编辑器
用Tkinter打造自己的Python IDE开发工具利用HP_tka模块设计自己的中文代码编辑器
我的程序可以在 Visual Studio 中运行,但不能在其他 IDE 示例 AWS Cloud 9 中运行