[HOW-TO]
Is there a way to display the preview window with Tkinter? In PiCamera I was able to place a preview window independently of TKinter. I don't want to use QT in my project.
I would say try with a short code example and if it's solid enough you can share it in examples folder via pull request. I'm working too on an option to take pictures by pressing a key and stopping the script with a key (without OpenCV).
Here is the process "camera" from my project showing a preview window and capturing. I would like to create the same with Picamera2, but without CV2 and QT, only with Tkinter.
#importing required modules import logging import logging.handlers import multiprocessing import picamera from pathlib import Path import sys
#importing required custom modules from Model.constants import *
#camera child process (overriding "def run" method) def camera(control, control_queues):
#logger
queue_handler = logging.handlers.QueueHandler(control_queues[LOGGING])
#root logger for this child process
root_camera_logger = logging.getLogger()
root_camera_logger.setLevel(logging.DEBUG)
root_camera_logger.addHandler(queue_handler)
#logger for this child process
camera_logger = logging.getLogger("camera logger")
camera_logger.log(logging.INFO, "process started: %s" % multiprocessing.current_process().name)
#multiprocessing logger
camera_mp_logger = multiprocessing.get_logger()
camera_mp_logger.setLevel(logging.DEBUG)
camera_mp_logger.addHandler(queue_handler)
try:
#init picamera
cam = picamera.PiCamera()
#place the preview window independent of the tkinter GUI
cam.preview_fullscreen = False
cam.preview_window = (3, 17, 295, 295)
cam.resolution = (295, 295)
#start endless loop for the camera process
while not control[QUIT_PROCESS]:
if control[CAMERA]:
if control[TOGGLE_CAMERA]:
cam.start_preview()
control[TOGGLE_CAMERA] = False
if control[CAPTURE]:
try:
cam.capture(str(Path(sys.path[0] + "/Media/Datamatrix/TEST.PNG"))) #DTMX.PNG
control[CAPTURE] = False
except IOError as err:
camera_logger.log(logging.DEBUG, "cannot write captured datamatrix code! {0}".format(err))
control[CAPTURE] = False
else:
cam.stop_preview()
camera_logger.log(logging.INFO, "camera process closed")
except Exception as e:
template = "An exception of type {0} in process 'camera' occurred. Arguments:\n{1!r}"
message = template.format(type(e).__name__, e.args)
print(message)
@bnd762 I would try to replace everything where you used PyQt and/or OpenCV and find the equivalent Tkinter functions.
In legacy OS images it was possible to render camera images to the screen directly using the HDMI hardware, but the desktop "knew" nothing about the images that were simply written on top of it. In more recent versions of the OS you need to go through some existing Linux display framework and the old mechanism is no longer supported.
I'm afraid I don't know anything about TkInter, though I expect it must be possible to render camera images that you capture with it (GPU acceleration may be trickier). Sorry not to be more help.
I could pick up the individual images via Mapped Array and display them in Tkinter to simulate a preview. But that's slow
In the Qt world you can use software rather than hardware-assisted rendering, and on a Pi 4, so long as you feed it an appropriate image format, and the resolutions aren't too big, it's not terrible. You could try it and see - presumably that's the performance level you could achieve without delving into OpenGL. The camera images are in uncached memory currently, so the best policy is often just to take an immediate copy and work with that.
There must be a way to do this in Python/Tkinter without QT and CV2. It can't be that it doesn't work.
Hi, is there anything else to investigate here? Unfortunately we don't have any plans to implement a Tkinter preview at this time.
I have now solved the problem with pygame and it works wonderfully. I use multiprocessing. Pygame runs in a separate process and is only used for the camera. The GUI (tkinter) runs in the main process. In the code example I have solved the problem that the Pygame window is in the foreground on the Raspberry X11 and under Windows.
#importing required modules import logging import logging.handlers import multiprocessing from picamera2 import Picamera2 from pathlib import Path import sys import os
#hide support prompt message os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide" os.environ['LIBCAMERA_LOG_LEVEL'] = "3"
import pygame
#windows only if sys.platform.startswith("win"):
from ctypes import windll
#importing required custom modules from Model.constants import *
#camera child process (overriding "def run" method) def camera(control, events, control_queues):
#helpers
#logger
queue_handler = logging.handlers.QueueHandler(control_queues[LOGGING])
#root logger for this child process
root_camera_logger = logging.getLogger()
root_camera_logger.setLevel(logging.DEBUG)
root_camera_logger.addHandler(queue_handler)
#logger for this child process
camera2_logger = logging.getLogger("picamera2")
camera2_logger.setLevel(logging.INFO)
camera_logger = logging.getLogger("camera logger")
camera_logger.log(logging.INFO, "process started: %s" % multiprocessing.current_process().name)
#multiprocessing logger
camera_mp_logger = multiprocessing.get_logger()
camera_mp_logger.setLevel(logging.DEBUG)
camera_mp_logger.addHandler(queue_handler)
try:
#init picamera##https://github.com/raspberrypi/picamera2/issues/527
cam = Picamera2()
#place the preview window independent of the tkinter GUI
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (30,17)
#X11 raspberry only -> set window always on top -> no function!?
os.environ['SDL_WINDOW_ALWAYS_ON_TOP'] = 'SDL_TRUE'
#windows only
if sys.platform.startswith("win"):
SetWindowPos = windll.user32.SetWindowPos
res = (240, 320) #295, 295
cam.preview_configuration.main.size = res
cam.preview_configuration.main.format = 'BGR888'
cam.configure("preview")
#configure still capture
capture_config = cam.create_still_configuration()
cam.still_configuration.main.size = (2500,2500)
#start endless loop for the camera process
while not events[QUIT_PROCESS].is_set():
if control[CAMERA]:
if control[TOGGLE_CAMERA]:
pygame.init()
cam.start()
#borderless window
screen = pygame.display.set_mode(res, pygame.NOFRAME)
#windows only -> set window always on top
if sys.platform.startswith("win"):
SetWindowPos(pygame.display.get_wm_info()['window'], -1, 3, 17, 0, 0, 0x0001)
#raspberry X11 only -> set window always on top
#sudo apt-get install wmctrl
else:
os.system("wmctrl -r 'pygame window' -b add,above")
control[TOGGLE_CAMERA] = False
if control[CAPTURE]:######control_events[CAPTURE].wait()
try:
#cam.capture(str(Path(sys.path[0] + "/Media/Datamatrix/TEST.PNG"))) #DTMX.PNG
cam.switch_mode_and_capture_file(capture_config, str(Path(sys.path[0] + "/Media/Datamatrix/TEST.PNG"))) #DTMX.PNG
control[CAPTURE] = False######control_events[CAPTURE].clear()
except IOError as err:
camera_logger.log(logging.DEBUG, "cannot write captured datamatrix code! {0}".format(err))
control[CAPTURE] = False######control_events[CAPTURE].clear()
array = cam.capture_array()
img = pygame.image.frombuffer(array.data, res, 'RGB')
screen.blit(img, (0, 0))
pygame.display.update()
else:
#to prevent error check if cam is running
if cam.started:
cam.stop()
pygame.quit()
camera_logger.log(logging.INFO, "camera process closed")
except Exception as e:
template = "An exception of type {0} in process 'camera' occurred. Arguments:\n{1!r}"
message = template.format(type(e).__name__, e.args)
print(message)
Cool, glad it's working. It might be nice just to put a display-a-camera-image-using-pygame example into the examples folder too!