0

A few years back I hacked together a tk script to listen for events from frigate and show the camera feed for a few seconds when someone was at the door. It worked without much effort until I updated to F43 (python 3.14), now I'm stuck looking out the window like one of those people that goes outside!

I suspect the issue lies somewhere between requests and Threading but I'm not sure how to further troubleshoot that. FWIW I am not trying to to thread the request (no aiohttp or asyncio here) just a thread for tk and a thread for mqtt (whose callback is where the request is happening).

This suspicion is fueled by being able to call stream() from main() and it works as expected, but calling anywhere from either of mqtt's callbacks throws this

 File "/opt/cameras/./cams.py", line 33, in on_connect 
 stream("garage", 10) 
 ~~~~~~^^^^^^^^ 
 File "/opt/cameras/./cams.py", line 64, in stream 
 render = ImageTk.PhotoImage(load) 
 File "/home/htpc/.local/lib/python3.14/site-packages/PIL/ImageTk.py", line 129, in __init__ 
 self.__photo = tkinter.PhotoImage(**kw) 
 ~~~~~~~~~~~~~~~~~~^^^^^^ 
 File "/usr/lib64/python3.14/tkinter/__init__.py", line 4301, in __init__ 
 Image.__init__(self, 'photo', name, cnf, master, **kw) 
 ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
 File "/usr/lib64/python3.14/tkinter/__init__.py", line 4248, in __init__ 
 self.tk.call(('image', 'create', imgtype, name,) + options) 
 ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
_tkinter.TclError: image type "photo" does not exist 

cams.py

#!/usr/bin/env python3
"""
Listen for mqtt events from frigate and show them on the htpc
"""
import sys
import json
from threading import Thread
from io import BytesIO
from tkinter import Tk, Label
#from tkinter import *
from PIL import Image, ImageTk
import paho.mqtt.client as mqtt
import requests
FRIGATE_API = ""
MQTT_SERVER = ""
MQTT_USERNAME = ""
MQTT_PASSWORD = ""
size = 540,540
WIN = Tk()
WIN.overrideredirect(True)
LABEL = Label(WIN)
LABEL.place(x=0, y=0)
LABEL.pack()
def on_connect(client, userdata, flags, rc, properties):
 """
 connect callback
 """
 stream("", 10) #just here for testing
 del userdata, flags, properties
 print(f"MQTT connected {rc}")
 client.subscribe("frigate/events")
def on_message(client, userdata, msg):
 """
 message callback
 """
 del client, userdata
 event = msg.payload
 event = json.loads(event.decode())
 if event['before']['stationary'] is False:
 if 'front' in event['before']['current_zones']:
 stream(event['before']['camera'], 10)
def stream(camera, i):
 """
 do the actual drawing
 """
 url = f"{FRIGATE_API}/{camera}/latest.jpg"
 if i == -1:
 WIN.withdraw()
 else:
 WIN.deiconify()
 frame = requests.get(url, timeout=1)
 load = Image.open(BytesIO(frame.content))
 load.thumbnail(size, Image.Resampling.LANCZOS)
 render = ImageTk.PhotoImage(load)
 LABEL.configure(image=render)
 LABEL.image = render
 WIN.after(1000, stream, camera, i-1)
def main():
 """
 main
 """
 client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
 client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
 client.on_connect = on_connect
 client.on_message = on_message
 client.connect(MQTT_SERVER, 1883, 60)
 print("dispatch client thread")
 client_loop = Thread(target=client.loop_forever, daemon=True)
 client_loop.start()
 print("start tk main loop")
 WIN.mainloop()
if __name__ == '__main__':
 sys.exit(main())
asked Dec 23, 2025 at 1:35
4
  • What was the old python version the one where your script worked? Also try adding load.save("debug.png") and see if you can open the image using an external image viewer. Commented Dec 23, 2025 at 12:01
  • F42 was on Python 3.13. I didn't see anything that looked related in the breaking changes in the 3.14 release notes. Writing the frame to disk works. Gwenview opens it without complaining. Commented Dec 23, 2025 at 15:06
  • Maybe first use print() (and print(type(...)), print(len(...)), etc.) to see which part of code is executed and what you really have in variables. It is called "print debugging" and it helps to see what code is really doing. Commented Dec 24, 2025 at 16:31
  • it seems you run some Tkinter code in thread - and maybe this is problem. As I know Tkinter is not thread-safe and it can make problem when you want to change something in widgets in another thread. It may need to use queue to send it to main thread and WIN may need to use after to run code which checks if there is new information in queue and update it in widget. Commented Dec 24, 2025 at 16:37

1 Answer 1

0

As @furas mentioned in the comments, tk is not thread-safe and queue should be used to communicate.

Updated snippet where on_message publishes and tk polls for new events.

def on_message(client, userdata, msg):
 """
 message callback
 """
 del client, userdata
 try:
 event = json.loads(msg.payload.decode())
 if event['before']['stationary'] is False:
 if 'front' in event['before']['current_zones']:
 msg_queue.put({
 "camera": event['before']['camera'],
 "count": 10
 })
 except json.JSONDecodeError as e:
 print(f"Error parsing decoding event: {e}")
def check_queue():
 """
 check for events from mqtt callback
 """
 try:
 task = msg_queue.get_nowait()
 stream(task['camera'], task['count'])
 except queue.Empty:
 pass
 WIN.after(100, check_queue)
def stream(camera, i):
 """
 do the actual drawing
 """
 if i <= -1:
 WIN.withdraw()
 return
 try:
 WIN.deiconify()
 url = f"{FRIGATE_API}/{camera}/latest.jpg"
 frame = requests.get(url, timeout=1)
 load = Image.open(BytesIO(frame.content))
 load.thumbnail(size, Image.Resampling.LANCZOS)
 render = ImageTk.PhotoImage(load, master=WIN)
 LABEL.configure(image=render)
 LABEL.image = render
 WIN.after(1000, stream, camera, i-1)
 except Exception as e: # pylint: disable=broad-exception-caught
 print(f"Stream error: {e}")
 WIN.after(1000, stream, camera, i-1
answered Dec 27, 2025 at 17:16
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.