TypeError: video_stream() missing 1 required positional argument: 'self'
Написал интерфейс для программы используя для него отдельный класс. Пытался в отдельный фрейм (место для видео на интерфейсе) вывести видео. Нашел код, который выводит видео в Tkinter, но при добавлении его в класс не видел переменной в методе def. Добавил self и выдал ошибку. Вот код, который нашел:
from Tkinter import *
from PIL import ImageTk, Image
import cv2
root = Tk()
# Create a frame
app = Frame(root, bg="white")
app.grid()
# Create a label in the frame
lmain = Label(app)
lmain.grid()
# Capture from camera
cap = cv2.VideoCapture(0)
# function for video streaming
def video_stream():
_, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(1, video_stream)
video_stream()
root.mainloop()
При переводе в класс:
from tkinter import *
from PIL import ImageTk, Image
import cv2
class GUT:
root = Tk()
# Create a frame
app = Frame(root, bg="white")
app.grid()
# Create a label in the frame
lmain = Label(app)
lmain.grid()
# Capture from camera
cap = cv2.VideoCapture("8-bit trip.mp4")
# function for video streaming
def video_stream(self):
_, frame = self.cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
self.lmain.imgtk = imgtk
self.lmain.configure(image=imgtk)
self.lmain.after(1, self.video_stream)
video_stream()
root.mainloop()
(self добавил т.к. не видел переменные) Как исправить ошибку или есть альтернативный вариант для привязки видео к отдельному фрейму в интерфейсе.
Ответы (1 шт):
- Сначала нужно ознакомиться с теорией - как должен выглядеть класс в Python, и как он работает, попробовать написать несколько классов без tkinter.
- После этого нужно подумать - нужен ли вам класс вообще? Часто это совершенно излишне. Если у вас на весь код одна функция, и вы зачем-то этот код хотите обернуть в класс, то вы что-то делаете не так.
- Дополнительно ко второму пункту: не нужно бездумно весь код собирать в один класс, иначе у вас получится классический "божественный объект". Это не ООП, это карго культ ООП. Не нужно думать, что просто добавление в ваш код классов сделает ваш код лучше.
- В целом, ООП - это один из инструментов структурирования и уменьшения сложности кода. Если вы просто обернете весь код в один класс - это никак не уменьшит сложность кода (скорее увеличит).
К прочтению: Перестаньте писать классы (Джэк Дидрих)
Если уж сильно хочется обернуть код в класс, то код верхнего уровня (код вне функций, кроме импортов) нужно перенести в метод __init__ класса, тогда этот код будет выполняться при создании объекта. Поля, которые должны быть доступны из методов класса нужно записывать через self. Методы также должны вызываться от self (см. вызов метода video_stream в примере ниже). Само по себе такое оборачивание бессмысленно, см. объяснение в начале.
Грубый пример такого "оборачивания":
class GUT:
def __init__(self):
root = Tk()
# Create a frame
app = Frame(root, bg="white")
app.grid()
# Create a label in the frame
self.lmain = Label(app)
self.lmain.grid()
# Capture from camera
self.cap = cv2.VideoCapture("8-bit trip.mp4")
self.video_stream()
root.mainloop()
# function for video streaming
def video_stream(self):
_, frame = self.cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
self.lmain.imgtk = imgtk
self.lmain.configure(image=imgtk)
self.lmain.after(1, self.video_stream)
if __name__ == '__main__':
GUT()
Для tkinter есть более-менее общепринятый подход - для окна или фрейма (если фрейм работает более-менее независимо других частей окна) делать свой класс, при этом класс наследуется от класса окна (Tk или Toplevel) или фрейма.
Вариант с наследованием от Tk:
class GUT(Tk):
def __init__(self):
super().__init__()
# Create a frame
app = Frame(self, bg="white")
app.grid()
# Create a label in the frame
self.lmain = Label(app)
self.lmain.grid()
# Capture from camera
self.cap = cv2.VideoCapture("8-bit trip.mp4")
self.video_stream()
# function for video streaming
def video_stream(self):
_, frame = self.cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
self.lmain.imgtk = imgtk
self.lmain.configure(image=imgtk)
self.lmain.after(1, self.video_stream)
if __name__ == '__main__':
GUT().mainloop()
Вариант с наследованием от Frame:
class GUT(Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Create a label in the frame
self.lmain = Label(self)
self.lmain.grid()
# Capture from camera
self.cap = cv2.VideoCapture("8-bit trip.mp4")
self.video_stream()
# function for video streaming
def video_stream(self):
_, frame = self.cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
self.lmain.imgtk = imgtk
self.lmain.configure(image=imgtk)
self.lmain.after(1, self.video_stream)
if __name__ == '__main__':
root = Tk()
app = GUT(root, bg="white")
app.grid()
root.mainloop()
Также можно пробовать реализовать нечто вроде MVC - тогда у вас отдельный класс (view) должен отвечать только за создание графического интерфейса (части графического интерфейса) и взаимодействие с пользователем, отдельный (model) - за хранение состояния приложения или части приложения, отдельный (controller) - за выполнение какой-то полезной работы, например того же получения данных с камеры. Это имеет смысл делать для сложных многооконных/многофреймовых приложений. Для простого однооконного приложения такое разделение скорее всего будет излишне, и возможно приведет к усложнению кода.