Home > Software design >  Python interpreter locked/freezing while trying to run pygetwindow as a thread
Python interpreter locked/freezing while trying to run pygetwindow as a thread

Time:02-23

I'm trying to learn how to use threading and specifically concurrent.futures.ThreadPoolExecutor this is because I need to return a numpy.array from a function I want to run concurrently.

The end goal is to have one process running a video loop of an application, while another process does object detection and GUI interactions. The result() keyword from the concurrent.futures library allows me to do this.

The issue is my code runs once, and then seems to lock up. I'm actually unsure what happens as when I step through it in the debugger it runs once, then the debugger goes blank and I literally cannot step through and no error is thrown.

The code appears to lock up on the line: notepadWindow = pygetwindow.getWindowsWithTitle('Notepad')[0]

I get exactly one loop, the print statement prints once the loop restarts and then it halts at pygetwindow

I don't know much about the GIL but I have tried using the max_workers=1 argument on ThreadPoolExecutor() which doesn't make a difference either way and I was under the impression concurrent.futures allows me to bypass the lock.

How do I run videoLoop as a single thread making sure to return DetectionWindow every iteration?

import cv2 as cv
import numpy as np
import concurrent.futures
from PIL import ImageGrab
import pygetwindow

def videoLoop():
    notepadWindow = pygetwindow.getWindowsWithTitle('Notepad')[0]
    x1 = notepadWindow.left
    y1 = notepadWindow.top
    height = notepadWindow.height
    width = notepadWindow.width
    x2 = x1   width
    y2 = y1   height
    haystack_img = ImageGrab.grab(bbox=(x1, y1, x2, y2))
    haystack_img_np = np.array(haystack_img)
    DetectionWindow= cv.cvtColor(haystack_img_np, cv.COLOR_BGR2GRAY)
    return DetectionWindow

def f1():
    with concurrent.futures.ThreadPoolExecutor() as executor:
        f1 = executor.submit(videoLoop)
        notepadWindow = f1.result()
        cv.imshow("Video Loop", notepadWindow)
        cv.waitKey(1)
        print(f1.result())

while True:
    f1()

CodePudding user response:

A ThreadPoolExecutor won't help you an awful lot here, if you want a continuous stream of frames.

Here's a reworking of your code that uses a regular old threading.Thread and puts frames (and their capture timestamps, since this is asynchronous) in a queue.Queue you can then read in another (or the main) thread.

The thread has an otherwise infinite loop that can be stopped by setting the thread's exit_signal.

(I didn't test this, since I'm presently on a Mac, so there may be typos or other problems.)

import queue
import time

import cv2 as cv
import numpy as np
import threading
from PIL import ImageGrab
import pygetwindow


def do_capture():
    notepadWindow = pygetwindow.getWindowsWithTitle("Notepad")[0]
    x1 = notepadWindow.left
    y1 = notepadWindow.top
    height = notepadWindow.height
    width = notepadWindow.width
    x2 = x1   width
    y2 = y1   height
    haystack_img = ImageGrab.grab(bbox=(x1, y1, x2, y2))
    return cv.cvtColor(np.array(haystack_img), cv.COLOR_BGR2GRAY)


class VideoCaptureThread(threading.Thread):
    def __init__(self, result_queue: queue.Queue) -> None:
        super().__init__()
        self.exit_signal = threading.Event()
        self.result_queue = result_queue

    def run(self) -> None:
        while not self.exit_signal.wait(0.05):
            try:
                result = do_capture()
                self.result_queue.put((time.time(), result))
            except Exception as exc:
                print(f"Failed capture: {exc}")


def main():
    result_queue = queue.Queue()
    thread = VideoCaptureThread(result_queue=result_queue)
    thread.start()
    start_time = time.time()
    while time.time() - start_time < 5:  # Run for five seconds
        frame = result_queue.get()
        print(frame)
    thread.exit_signal.set()
    thread.join()


if __name__ == "__main__":
    main()
  • Related