использовать многопоточность в tkinter - PullRequest
0 голосов
/ 03 января 2019

У меня есть два класса и я использую демоверсию tkinter.

class App:
    def __init__(self, window, window_title, video_source=0):
        self.window = window
        self.window.title(window_title)
        self.video_source = video_source

        # open video source (by default this will try to open the computer webcam)
        self.vid = MyVideoCapture(self.video_source)

        # Create a canvas that can fit the above video source size
        self.canvas = tkinter.Canvas(window).place(x=50, y=0)

        # set plot parameter
        self.fig = Figure(figsize=(7, 4), dpi=100)
        self.fresh = FigureCanvasTkAgg(self.fig, master=self.window)
        self.ax1 = self.fig.add_subplot(211)
        self.ax2 = self.fig.add_subplot(212)

        self.fresh.get_tk_widget().place(x=700, y=0)
        self.window.geometry('1500x550')

        # Camera thread
        self.photo = None
        self.delay = 15
        self.t = threading.Thread(target=self.update, args=())
        self.t.setDaemon(True)
        self.t.start()

    def refresh(self, data):
        sample_track = pd.read_csv('/home/share/sample_track.csv')
        x = [i for i in range(len(sample_track))]
        y = sample_track['0']
        xdata = [i for i in range(len(data))]
        ydata = data
        self.ax1.plot(x, y, 'bo--')
        self.ax2.plot(xdata, ydata, 'ro--')
        self.fresh.draw()

    def update(self):
        # Get a frame from the video source
        ret, frame = self.vid.get_frame()
        if ret:
            self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame))
            self.canvas.create_image(0, 0, image=self.photo, anchor=tkinter.NW)
        self.window.after(self.delay, self.update)

class MyVideoCapture:
    def __init__(self, video_source=0):
        # Open the video source
        self.vid = cv2.VideoCapture(video_source)
        if not self.vid.isOpened():
            raise ValueError("Unable to open video source", video_source)   
        self.ret = None

    def get_frame(self):
        if self.vid.isOpened():
            self.ret, frame = self.vid.read()
            if self.ret:
                # Return a boolean success flag and the current frame converted to BGR
                return self.ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            else:
                return self.ret, None

if __name__ == '__main__':
    win = tkinter.Tk()
    panel = App(win, "Dance")
    value = []
    for i in range(15):
        value.append(np.random.randint(0, 800))
        panel.refresh(data=value)
        time.sleep(0.1)
    win.mainloop()

Я хочу запустить веб-камеру и одновременно обновить фигуру. Я пытался использовать поток, но все равно не удалось. Произошла следующая ошибка:

  • RuntimeError: основной поток не находится в основном цикле
  • AttributeError: у объекта «PhotoImage» нет атрибута «_PhotoImage__photo»

Как я могу решить это?

1 Ответ

0 голосов
/ 03 января 2019

Вот рабочая tkinter камера, взятая прямо из здесь (найдена путем быстрого поиска 'камеры tkinter'):

import tkinter
import cv2
import PIL.Image, PIL.ImageTk
import time

class App:
    def __init__(self, window, window_title, video_source=0):
        self.window = window
        self.window.title(window_title)
        self.video_source = video_source

        # open video source (by default this will try to open the computer webcam)
        self.vid = MyVideoCapture(self.video_source)

        # Create a canvas that can fit the above video source size
        self.canvas = tkinter.Canvas(window, width = self.vid.width, height = self.vid.height)
        self.canvas.pack()

        # Button that lets the user take a snapshot
        self.btn_snapshot=tkinter.Button(window, text="Snapshot", width=50, command=self.snapshot)
        self.btn_snapshot.pack(anchor=tkinter.CENTER, expand=True)

        # After it is called once, the update method will be automatically called every delay milliseconds
        self.delay = 15
        self.update()

        self.window.mainloop()

    def snapshot(self):
        # Get a frame from the video source
        ret, frame = self.vid.get_frame()

        if ret:
            cv2.imwrite("frame-" + time.strftime("%d-%m-%Y-%H-%M-%S") + ".jpg", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))

    def update(self):
        # Get a frame from the video source
        ret, frame = self.vid.get_frame()

        if ret:
            self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame))
            self.canvas.create_image(0, 0, image = self.photo, anchor = tkinter.NW)

        self.window.after(self.delay, self.update)


class MyVideoCapture:
    def __init__(self, video_source=0):
        # Open the video source
        self.vid = cv2.VideoCapture(video_source)
        if not self.vid.isOpened():
            raise ValueError("Unable to open video source", video_source)

        # Get video source width and height
        self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)

    def get_frame(self):
        if self.vid.isOpened():
            ret, frame = self.vid.read()
            if ret:
                # Return a boolean success flag and the current frame converted to BGR
                return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
            else:
                return (ret, None)
        else:
            return (ret, None)

    # Release the video source when the object is destroyed
    def __del__(self):
        if self.vid.isOpened():
            self.vid.release()

# Create a window and pass it to the Application object
App(tkinter.Tk(), "Tkinter and OpenCV")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...