Python3如何在类中启动第二个线程和连续接收结果



大家好,

我一直在思考这个问题,感觉像是永恒的,但无法解决这个问题。因此,我想我还不如问问比我更有经验的开发人员。我的情况是:

我有一个python脚本,它可以通过视频流连续识别人们的面部,并创建一个dict对象,该对象的名称和识别时间被称为identifier_class.py。这个脚本有一个类,该类有一个检索已识别人员的方法,称为returnIdentifiedFaces((

我有第二个python(称为tkapp.py(脚本,它由一个主脚本和两个类组成。第一个创建了一个屏幕,上面有一些信息,主要是天气、时钟以及个性化的新闻和日历事件。第二个名为identifierAsync((。现在,最后两个项目,新闻和日历事件,应该根据用户的名称进行个性化设置;用户";当然,应该通过调用返回IdentifiedFaces((方法的第一个脚本来识别我试图通过在tkapp.py中创建第二个类identizerAsync((来实现这一点,以实例化identizer_class类并将其作为线程运行。

我可以单独启动这两个脚本,它们都能工作-显然tkapp没有个性化:-(

下面是脚本tkapp中的代码,用于实例化识别器类并将识别过程作为线程启动

class recognizerAsync(Thread):
def __init__(self):
super().__init__()
print("starting up recogizer from callRecognizerThread")

if (use_user == True):
#myRecognizer = recognizer(consoleLog, debugMsg, run_from_flask)
self.myRecognizer = the_recognizer.recognizer(True, True, True)
def run(self):
print("starting up recogizer from callRecognizerThread")

if (use_user == True):
#myRecognizer = recognizer(consoleLog, debugMsg, run_from_flask)
self.myRecognizer.faceRecognizer()
def getUser(self):
self.myRecognizer.printRecognizedFaces()
return(self.myRecognizer.returnRecognizedFaces())

如前所述,此代码位于tkapp.py脚本中。

我把它改为使用队列,所以现在代码看起来像这样,但它仍然不能正确执行。从未执行外部识别器类的returnRecognizedFaces方法。这是新代码:

class recognizerAsync(Thread):
def __init__(self):
super(recognizerAsync,self).__init__()
print("starting up recogizer from callRecognizerThread")

if (use_user == True):
#myRecognizer = recognizer(consoleLog, debugMsg, run_from_flask)
self.myRecognizer = the_recognizer.recognizer(True, True, True)


def run(self):
print("starting up recogizer from callRecognizerThread")


if (use_user == True):
self.myRecognizer.faceRecognizer()
while (True):
if ( not q.full() ):
fd = self.myRecognizer.returnRecognizedFaces()
print(f"PRODUCER: putting into queue {fd}")
q.put(fd)
else: 
print("ERROR :: Queue q is full")

tkapp.py启动后,它实例化自己的GUI类,然后用以下代码片段启动identifier_class.py线程:

# start the gui
window = Tk()
window.title("Smart Mirror")
window.geometry('1024x768')
window.configure(background=gui_back_color)
window.bind("<Escape>", close_escape)
mirror = GUI(window)
mirror.setupGUI()
# update the news calling returnRecognizedFaces()
window.after(1000, mirror.updateNews)
face_thread = recognizerAsync()
face_thread.start()
window.mainloop()

这一切都有效。因此,创建了GUI并开始识别人员(我认为这是一个线程(

我遇到的问题是,在执行该操作的那一刻,我可以在日志和控制台中看到,identifier_class.py确实在识别人员,但我不知道如何查询identifier_class的结果。

这是tkapp.py中我认为应该完成的代码:

def updateNews(self):
# cola the method to query the recognised person dictionary from recognizer_class.py
self.user = recognizerAsync.getUser()
print(f"got user {self.user} from recognizer")

news_data = self.myNews.returnNews(self.user)
if (len(news_data) > 0):
GUI.news_today.configure(text=(f"Nachrichten für {self.user}"))
GUI.news_number1.insert(INSERT, (f"{news_data[0]['title']}"))
else:
GUI.news_title.configure(text="Keine Nachrichten verfügbar")

window.after(50000000, mirror.updateNews)

如前所述,这不起作用,我真的不知道如何做对。这是我从这个代码中收到的错误:

File "./tkapp.py", line 271, in updateNews
self.user = recognizerAsync.getUser()
TypeError: getUser() missing 1 required positional argument: 'self'
starting face detection - press Ctrl C to stop

我当然知道,identifierAsync类是从主脚本实例化的,因此在tkapp.py脚本的GUI类中不可用,但我该如何解决这个问题

,我希望有人能帮我

基督教

请在下面找到tkapp.py 的代码

tkapp.py:

#!/usr/bin/env python3

# needed to source in the configuration from file config.py
from pathlib import Path
# time formatting for clock
import locale
locale.setlocale(locale.LC_TIME, 'de_DE.UTF-8')
import time
from time import strftime
from datetime import date, timedelta

# the TK Gui
from tkinter import *
import tkinter.font
import pandas as pd
# needed to run the recognizer in background
from threading import Thread
# need to show images from the web
from PIL import Image, ImageTk
from urllib.request import urlopen
from io import BytesIO
import Weather.weather_class as the_weather
import News.news_class as the_news
import Recognizer.recognizer_class as the_recognizer
# EVERY RUNTIME BEHAVIOUR IS SET IN THE CONFIG FILE config.py
config_file = 'config.py'
#
##  THE GUI CLASS SETS UP THE LABLES AND INSTANTIATES THE NEWS AND WEATHER CLASS
##  IT IS ALSO RESPONSIBLE FOR UPDATING THE SCREEN CONTENT
#
class GUI(Frame):
largeFont = ''
mediumFont = ''
normalplusFont = ''
normalFont = ''
lightFont = ''
identified_person = 'nil'   # the dict we should get back from recognizer
user = 'nil'                # a simple name string we pass to other functions

# module pathes etc.
weather_icons_path = ''
def __init__(self, master):
Frame.__init__(self, master)

self.largeFont = tkinter.font.Font(family=gui_font_large[0], size=gui_font_large[1])
self.mediumFont = tkinter.font.Font(family=gui_font_medium[0], size=gui_font_medium[1])
self.normalplusFont = tkinter.font.Font(family=gui_font_normalplus[0], size=gui_font_normalplus[1])
self.normalFont = tkinter.font.Font(family=gui_font_normal[0], size=gui_font_normal[1])
self.lightFont = tkinter.font.Font(family=gui_font_light[0], size=gui_font_light[1])
# instantiate news and weather class
if (show_weather == True):
self.myWeather = the_weather.weather()
if (show_news) == True:
self.myNews = the_news.news()
#if (use_user == True):
#myRecognizer = recognizer(consoleLog, debugMsg, run_from_flask)
#self.myRecognizer = the_recognizer.recognizer(True, True, True)
def setupGUI(self):
self.grid(row=0, column=0, padx=20, pady=20)

if (show_weather == True):
# Weather & news frame to contain weather/news info
today_weather_frame = Frame(self, width=400, height=500, bg=gui_back_color)
today_weather_frame.grid(row=0, column=0, sticky=W)
GUI.weather_headline = Label(today_weather_frame, text="Wetter...", fg=gui_fore_color, bg=gui_back_color,
font=self.mediumFont, justify=LEFT)
GUI.weather_headline.grid(row=0, column=0, sticky=NW)

GUI.weather_report = Label(today_weather_frame, text="Wetterbericht...", fg=gui_fore_color, bg=gui_back_color,
font=self.normalplusFont, justify=LEFT)
GUI.weather_report.grid(row=1, column=0, sticky=W)

today_weather_frame_details = Frame(self, width=400, height=500, bg=gui_back_color)
today_weather_frame_details.grid(row=1, column=0, sticky=W)
GUI.weather_temp = Label(today_weather_frame_details, text="Temperatur:", fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont, justify=LEFT)
GUI.weather_temp.grid(row=2, column=0, sticky=W)
GUI.weather_temp_el = Label(today_weather_frame_details, text="...", fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont, justify=LEFT)
GUI.weather_temp_el.grid(row=2, column=1, sticky=W)

GUI.weather_humidity = Label(today_weather_frame_details, text="Luftfeuchtigkeit:", fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont, justify=LEFT)
GUI.weather_humidity.grid(row=3, column=0, sticky=W)
GUI.weather_humidity_el = Label(today_weather_frame_details, text="...", fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont, justify=LEFT)
GUI.weather_humidity_el.grid(row=3, column=1, sticky=W)

GUI.weather_sunrise = Label(today_weather_frame_details, text="Sonnenaufgang:", fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont, justify=LEFT)
GUI.weather_sunrise.grid(row=4, column=0, sticky=W)
GUI.weather_sunrise_el = Label(today_weather_frame_details, text="...", fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont, justify=LEFT)
GUI.weather_sunrise_el.grid(row=4, column=1, sticky=W)

GUI.weather_sunset = Label(today_weather_frame_details, text="Sonnenuntergang:", fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont, justify=LEFT)
GUI.weather_sunset.grid(row=5, column=0, sticky=W)
GUI.weather_sunset_el = Label(today_weather_frame_details, text="...", fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont, justify=LEFT)
GUI.weather_sunset_el.grid(row=5, column=1, sticky=W)

# the initial weather icon
icon = PhotoImage(file=weather_icons_path+"partly-cloudy-day.gif")
icon = icon.subsample(10)
# Set up labels to hold weather icons
GUI.icon_label = Label(today_weather_frame, borderwidth=0, image=icon)
GUI.icon_label.photo = icon
GUI.icon_label.grid(row=0, column=1, sticky=W)

empty_frame = Frame(self, width=200, height=500, bg=gui_back_color)
empty_frame.grid(row=2, column=0, sticky=W)

if (show_news == True):
# Labels to hold news info
news_frame = Frame(self, width=400, height=500, bg=gui_back_color)
news_frame.grid(row=3, column=0, sticky=W)
GUI.news_today = Label(news_frame, text="nNachrichten:", fg=gui_fore_color, bg=gui_back_color,
font=self.normalplusFont, justify=LEFT)
GUI.news_today.grid(row=0, column=0, sticky=W)
GUI.news_number1 = Text(news_frame, wrap=WORD, height=1, width=80, 
fg=gui_fore_color, bg=gui_back_color, bd=0,
font=self.lightFont, highlightthickness=0)
GUI.news_number1.grid(row=1, column=0, sticky=W)
GUI.news_number2 = Text(news_frame, wrap=WORD, height=1, width=80, 
fg=gui_fore_color, bg=gui_back_color, bd=0,
font=self.lightFont, highlightthickness=0)
GUI.news_number2.grid(row=2, column=0, sticky=W)

GUI.news_number3 = Text(news_frame, wrap=WORD, height=1, width=80, 
fg=gui_fore_color, bg=gui_back_color, bd=0,
font=self.lightFont, highlightthickness=0)
GUI.news_number3.grid(row=3, column=0, sticky=W)

GUI.news_number4 = Text(news_frame, wrap=WORD, height=1, width=80, 
fg=gui_fore_color, bg=gui_back_color, bd=0,
font=self.lightFont, highlightthickness=0)
GUI.news_number4.grid(row=4, column=0, sticky=W)

GUI.news_number5 = Text(news_frame, wrap=WORD, height=1, width=80, 
fg=gui_fore_color, bg=gui_back_color, bd=0,
font=self.lightFont, highlightthickness=0)
GUI.news_number5.grid(row=5, column=0, sticky=W)

# Adjust this width for spacing
frame_placeholder = Frame(self, width=gui_width/4, height=10, bg=gui_back_color)
frame_placeholder.grid(row=0, column=1)
# Time frame to hold time & date in grid
if (show_clock == True):
time_frame = Frame(self, width=400, height=500, bg=gui_back_color)
time_frame.grid(row=0, column=2, sticky=NE)
GUI.time_label = Label(time_frame, text=strftime("%H:%M", time.localtime()), fg=gui_fore_color, bg=gui_back_color,
font=self.largeFont)
GUI.time_label.grid(row=0, column=0, sticky=NE)
GUI.date_label = Label(time_frame, text=strftime("%A, %d. %B", time.localtime()), fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont)
GUI.date_label.grid(row=1, column=0, sticky=NE)
# Frame for calendar info
if (show_calendar == True):
calendar_frame = Frame(self, width=400, height=500, bg=gui_back_color)
calendar_frame.grid(row=1, column=2, sticky=NE)
GUI.calendar_header = Label(calendar_frame, text='nUpcoming events:', fg=gui_fore_color, bg=gui_back_color,
font=self.mediumFont)
GUI.calendar_header.grid(row=0, column=0, sticky=NE)

GUI.calendar_event1 = Label(calendar_frame, text='Loading calendar events...', fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont)
GUI.calendar_event1.grid(row=1, column=0, sticky=NE)

GUI.calendar_event2 = Label(calendar_frame, text='Loading calendar events...', fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont)
GUI.calendar_event2.grid(row=2, column=0, sticky=NE)

GUI.calendar_event3 = Label(calendar_frame, text='Loading calendar events...', fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont)
GUI.calendar_event3.grid(row=3, column=0, sticky=NE)

GUI.calendar_event4 = Label(calendar_frame, text='Loading calendar events...', fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont)
GUI.calendar_event4.grid(row=4, column=0, sticky=NE)

GUI.calendar_event5 = Label(calendar_frame, text='Loading calendar events...', fg=gui_fore_color, bg=gui_back_color,
font=self.normalFont)
GUI.calendar_event5.grid(row=5, column=0, sticky=NE)
self.configure(background=gui_back_color)
def updateClock(self):
GUI.time_label.configure(text=strftime("%H:%M", time.localtime()))
GUI.date_label.configure(text=strftime("%A, %d. %B", time.localtime()))
window.after(1000, mirror.updateClock)
def updateWeather(self):
# Updates the weather information
weather_data = self.myWeather.returnWeather(self.user)
GUI.weather_headline.configure(text=(f"Das {weather_data['city']} Wetter"))
GUI.weather_report.configure(text=(f"{weather_data['weather_desc']}"))
GUI.weather_temp_el.configure(text=(f"{weather_data['temp']}°C (Gefühlt: {weather_data['feels_like']}°C)"))
GUI.weather_humidity_el.configure(text=(f"{weather_data['humidity']}%"))
GUI.weather_sunrise_el.configure(text=(f"{weather_data['sunrise']}"))
GUI.weather_sunset_el.configure(text=(f"{weather_data['sunset']}"))

window.after(50000000, mirror.updateWeather)
def updateNews(self):
if (use_user == True):
self.user = recognizerAsync.getUser()
print(f"got user {self.user} from recognizer")
news_data = self.myNews.returnNews(self.user)
#print("size: " + str(len(news_data)) + "n / result: " + str(news_data))
if (len(news_data) > 0):
if self.user != 'nil' and self.user != "":
GUI.news_today.configure(text=(f"Nachrichten für {self.user}"))
else:
GUI.news_today.configure(text=(f"Aktuelle Nachrichten"))
GUI.news_number1.insert(INSERT, (f"{news_data[0]['title']}"))
GUI.news_number2.insert(INSERT, (f"{news_data[1]['title']}"))
GUI.news_number3.insert(INSERT, (f"{news_data[2]['title']}"))
GUI.news_number4.insert(INSERT, (f"{news_data[3]['title']}"))
GUI.news_number5.insert(INSERT, (f"{news_data[4]['title']}"))
else:
GUI.news_title.configure(text="Keine Nachrichten verfügbar")

window.after(50000000, mirror.updateNews)
def updateCalendar(self):
event_list = []
if not events:
print('No upcoming events found.')
for event in events:
event_str = ''
start = event['start'].get('dateTime', event['start'].get('date'))
start = start[0:10] # Remove unnecessary characters at end of string
year = start.find('-')
start_day = datetime.datetime.strptime(start, '%Y-%m-%d').strftime('%a %b %d')
event_date = start[year + 1:year + 6]
summary = event['summary'].encode('ascii', 'ignore').decode('ascii') # Remove emojis
event_str += summary + ' | ' + start_day
event_list.append(event_str)

window.after(500000000, mirror.updateCalendar)
def recognizer(self):
self.myRecognizer.faceRecognizer()


class recognizerAsync(Thread):
def __init__(self):
super().__init__()
print("starting up recogizer from callRecognizerThread")

if (use_user == True):
#myRecognizer = recognizer(consoleLog, debugMsg, run_from_flask)
self.myRecognizer = the_recognizer.recognizer(True, True, True)

def run(self):
print("starting up recogizer from callRecognizerThread")

if (use_user == True):
self.myRecognizer.faceRecognizer()

def getUser(self):
self.myRecognizer.printRecognizedFaces()
return(self.myRecognizer.returnRecognizedFaces())
#
## MAIN SCRIPT RUN FROM HERE
#
app_config={}
print("setting config file to " + str(config_file))
exec(Path(config_file).read_text(encoding="utf8"), {}, app_config)
use_user = (app_config["use_user"])
show_news = (app_config["show_news"])
show_weather = (app_config["show_weather"])
show_calendar = (app_config["show_calendar"])
show_clock = (app_config["show_clock"])
analog_clock = (app_config["analog_clock"])
# GUI controls
gui_width = (app_config["gui_width"])
gui_height = (app_config["gui_height"])
gui_fullscreen = (app_config["gui_fullscreen"])
gui_back_color = (app_config["gui_back_color"])
gui_fore_color = (app_config["gui_fore_color"])
gui_font_large = (app_config["gui_font_large"])
gui_font_medium = (app_config["gui_font_medium"])
gui_font_normalplus = (app_config["gui_font_normalplus"])
gui_font_normal = (app_config["gui_font_normal"])
gui_font_light = (app_config["gui_font_light"])
# module pathes etc.
weather_icons_path = (app_config["weather_icons_path"])
def close_escape(event=None):
print('Smart mirror closed')
window.destroy()
print(f"starting GUI with {gui_width}x{gui_height}")
# start the gui
window = Tk()
window.title("Smart Mirror")
window.geometry((F'{gui_width}x{gui_height}'))
window.configure(background=gui_back_color)
#Removes borders from GUI and implements quit via esc
window.overrideredirect(1)
window.overrideredirect(0)
window.attributes("-fullscreen", gui_fullscreen)
window.wm_attributes("-topmost", 1)
window.focus_set()
window.bind("<Escape>", close_escape)
mirror = GUI(window)
mirror.setupGUI()

if (show_clock == True):
window.after(1000, mirror.updateClock)
if (show_weather == True):
window.after(1000, mirror.updateWeather)
if (show_news == True):
window.after(1000, mirror.updateNews)
if (show_calendar == True):
window.after(1000, mirror.updateCalendar)

if (use_user == True):
face_thread = recognizerAsync()
face_thread.start()
window.mainloop()

使用Queue,它是为线程间通信而设计的。在分割线程之前初始化一个队列对象,它将在两个线程中都可用。当识别器线程找到用户时,将其放入队列。在您的更新功能中,如果队列不是空的,则获取数据并显示它。

编辑:这里是一个最小的例子:

from tkinter import Tk, Label
from threading import Thread
from queue import Queue
from time import sleep
queue = Queue()
class CountLabel(Label):
def __init__(self, master):
super().__init__(text="Waiting")
self.pack()
def update(self):
while not queue.empty():
count = queue.get()
self.configure(text=f"Count: {count}")
self.master.after(1000, self.update)
def produce(queue):
count = 0
while (True):
count += 1
queue.put(count)
sleep(1)
window = Tk()
label = CountLabel(window)
window.after(1000, label.update)
thread = Thread(target=produce, args=[queue]).start()
window.mainloop()

最新更新