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 中运行

[IDE]在Visual Studtio 2012使用Visual SourceSafe(VSS)进行版本控管

Visual Studio Code 一款好用的IDE