Fotobox2/Fotobox.py

274 lines
12 KiB
Python

import os.path
import time
from typing import List
from PIL import Image
from escpos.escpos import Escpos
from ConsolePrinter import ConsolePrinter
from serial import SerialException
from Event import Event, load_event
from Photo import Photo
from config import *
from enum import Enum
from config import read, file_increase, iso, now
from timer import Timer
from libcamera import Transform
class State(Enum):
IDLE = 0
COUNTDOWN = 1
SHOOTING = 2
PREPARE = 3
CHOOSE = 4
QR = 5
res = (1024,768)
class Fotobox:
def __init__(self, camera):
camera.vflip = False
camera.hflip = True
camera.resolution = (2592, 1944)
camera.rotation = 270
camera.preview_configuration.main.size = res
camera.preview_configuration.main.format = 'BGR888'
camera.preview_configuration.transform = Transform(hflip=1, vflip=1)
camera.configure("preview")
#####################
# here configure still capture
self._capture_config = camera.create_still_configuration()
camera.still_configuration.main.size = (2592, 1944)
#####################
camera.start()
self._camera = camera
self._running: bool = True
self._state: State = State.IDLE
self._photos: List[Photo] = []
self._choice: Photo | None = None
self._countdown_timer: Timer = Timer("countdown", 0, 1, COUNTDOWN_COUNT + 1, self._countdown_callback)
self._shooting_timer: Timer = Timer("shooting", 0, SHOOTING_INTERVAL, SHOOTING_COUNT, self._shooting_callback)
self._fotobox_uuid = read("./data/fotobox.uuid")
print("Starting fotobox: F-%s" % self._fotobox_uuid)
self._runtime_datetime = now()
self._runtime_number = file_increase("./data/runtime.number")
print("Starting runtime: R%04d-%s" % (self._runtime_number, iso(self._runtime_datetime)))
self._event: Event | None = load_event()
self._idle_url_without_protocol = "%s/e/%s" % (DOMAIN, self._event.code) if self._event is not None else DOMAIN
self._idle_url_with_protocol = "http%s://%s/" % ('s' if HTTPS else '', self._idle_url_without_protocol)
self._idle_qr = qr_create(self._idle_url_with_protocol)
self._idle_qr_rect = self._idle_qr.get_rect(center=SCREEN_RECT.center)
self._idle_url = FONT_URL.render(self._idle_url_without_protocol, True, (255, 255, 255))
self._idle_url_rect = self._idle_url.get_rect(centerx=SCREEN_RECT.centerx, bottom=HEIGHT - BORDER)
self._shooting_number = 0
self._shooting_datetime = None
self._printer: Escpos | None = None
def _countdown_callback(self):
if self._countdown_timer.complete():
self._set_state(State.SHOOTING)
else:
print("COUNTDOWN: %d" % self._countdown_timer.rest())
def _shooting_callback(self):
print("SHOT: %d/%d" % (self._shooting_timer.current(), SHOOTING_COUNT))
for i in range(24):
array = self._camera.capture_array()
img = pygame.image.frombuffer(array.data, res, 'RGB')
screen.blit(img, (0, 0))
pygame.display.update()
screen.fill((255, 255, 255))
pygame.display.flip()
if not DEBUG:
time.sleep(0.2)
frame = self._camera.switch_mode_and_capture_array(self._capture_config, "main")
image = Image.fromarray(frame)
self._photos.append(Photo(self._shooting_timer.current(), image))
#stream = io.BytesIO()
#self._camera.capture(stream, format='rgb')
#stream.seek(0)
#self._photos.append(Photo(self._shooting_timer.current(), Image.open(stream)))
screen.fill((0, 0, 0))
pygame.display.flip()
if self._shooting_timer.complete():
self._set_state(State.PREPARE)
def run(self):
if DEBUG:
self._set_state(State.SHOOTING)
while self._running:
self._events()
self._timer()
self._draw()
def _events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self._running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if self._state == State.IDLE:
self._set_state(State.COUNTDOWN)
elif self._state == State.CHOOSE:
if CANCEL_RECT.collidepoint(event.pos):
print("CANCEL")
self._set_state(State.IDLE)
elif SAVE_RECT.collidepoint(event.pos) and self._choice is not None:
print("SAVE")
self._set_state(State.QR)
else:
for photo in self._photos:
if photo.thumb_rect.collidepoint(event.pos):
self._choice = photo
print("CHOSE: %d" % self._choice.number)
elif self._state == State.QR:
if DONE_RECT.collidepoint(event.pos):
print("DONE")
self._set_state(State.IDLE)
elif self._printer is not None and not self._printed and PRINT_RECT.collidepoint(event.pos):
print("PRINT")
try:
self._printer.set(align='center', font='b', width=2, height=2)
self._printer.text("Fotobox\n")
self._printer.set(align='center', font='a', width=1, height=1)
if self._event is not None:
self._printer.text(self._event.date.strftime("%d.%m.%Y") + "\n")
self._printer.text(self._event.title + "\n")
self._printer.text(self._event.url_without_protocol + "\n")
self._printer.text("Passwort: " + self._event.password + "\n")
else:
self._printer.text(datetime.now().strftime("%d.%m.%Y") + "\n")
self._printer.text(self._idle_url_without_protocol + "\n")
self._printer.qr(self._choice.urlWithProtocol, center=True)
self._printer.text(self._choice.urlWithoutProtocol)
self._printer.cut()
self._printed = True
except Exception as e:
print(e)
def _timer(self):
dt: float = clock.tick(20) / 1000
self._shooting_timer.tick(dt)
self._countdown_timer.tick(dt)
def _draw(self):
screen.fill((0, 0, 0))
if self._state == State.IDLE:
screen.blit(FOTOBOX_TITLE_SURFACE, FOTOBOX_TITLE_RECT)
screen.blit(self._idle_qr, self._idle_qr_rect)
screen.blit(self._idle_url, self._idle_url_rect)
elif self._state == State.COUNTDOWN:
screen.blit(self._event.frame, self._event.frame_rect)
if self._countdown_timer.rest() == 3:
screen.blit(FOTOBOX_COUNTDOWN3_SURFACE, FOTOBOX_COUNTDOWN3_RECT)
elif self._countdown_timer.rest() == 2:
screen.blit(FOTOBOX_COUNTDOWN2_SURFACE, FOTOBOX_COUNTDOWN2_RECT)
elif self._countdown_timer.rest() == 1:
screen.blit(FOTOBOX_COUNTDOWN1_SURFACE, FOTOBOX_COUNTDOWN1_RECT)
elif self._state == State.SHOOTING:
screen.blit(self._event.frame, self._event.frame_rect)
elif self._state == State.PREPARE:
screen.blit(LOADING_SURFACE, LOADING_RECT)
elif self._state == State.CHOOSE:
screen.blit(CHOOSE_SURFACE, CHOOSE_RECT)
screen.blit(CANCEL_SURFACE, CANCEL_RECT)
if self._choice is not None:
screen.blit(SAVE_SURFACE, SAVE_RECT)
for photo in self._photos:
screen.blit(photo.thumb_surface, photo.thumb_rect)
if self._choice == photo:
rect = pygame.Rect(
photo.thumb_rect.x - CHOICE_BORDER,
photo.thumb_rect.y - CHOICE_BORDER,
photo.thumb_rect.width + 2 * CHOICE_BORDER,
photo.thumb_rect.height + 2 * CHOICE_BORDER
)
pygame.draw.rect(screen, (0, 255, 0), rect, width=CHOICE_BORDER)
elif self._state == State.QR:
screen.blit(self._qr, self._qr_rect)
screen.blit(self._choice.chosen_surface, self._choice.chosen_rect)
if self._printer is not None and not self._printed:
screen.blit(PRINT_SURFACE, PRINT_RECT)
screen.blit(DONE_SURFACE, DONE_RECT)
pygame.display.flip()
def _set_state(self, new_state: State):
print(new_state)
self._state = new_state
if new_state == State.COUNTDOWN:
#config = self._camera.create_preview_configuration(main={"size": (PHOTO_WIDTH, PHOTO_HEIGHT)})
#self._camera.configure(config)
#self._camera.start_preview()
#self._camera.start()
self._countdown_timer.restart()
elif new_state == State.SHOOTING:
self._photos = []
self._shooting_number += 1
self._shooting_datetime = now()
print("Starting shooting: S%04d-%s" % (self._shooting_number, iso(self._shooting_datetime)))
self._shooting_timer.restart()
elif new_state == State.PREPARE:
#self._camera.stop_preview()
for photo in self._photos:
photo.prepare(self._event)
self._set_state(State.CHOOSE)
elif new_state == State.CHOOSE:
self._choice = None
elif new_state == State.QR:
self._printed = False
try:
if self._printer is None or not self._printer.is_online():
# self._printer = Serial(devfile='/dev/ttyUSB0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1.00, dsrdtr=True)
self._printer = ConsolePrinter()
self._printer.profile.media['width']['pixels'] = 512
print("Printer AVAILABLE")
except SerialException:
self._printer = None
print("No printer available")
self._qr = qr_create(self._choice.urlWithProtocol)
self._qr_rect = self._qr.get_rect(left=2 * BORDER, centery=SCREEN_RECT.centery)
self.save(self._choice.photo_image, "")
self.save(self._choice.framed_image, "framed")
def save(self, img: Image, suffix: str):
filename = "F-%s---R%04d-%s---S%04d-%s---P%04d-%s---%s%s.jpg" % (
self._fotobox_uuid,
self._runtime_number,
iso(self._runtime_datetime),
self._shooting_number,
iso(self._shooting_datetime),
self._choice.number,
iso(self._choice.datetime),
self._choice.code,
"---" + suffix if suffix != "" else ""
)
path = Path("./data/photos/original/F-%s/R%04d-%s/%s" % (
self._fotobox_uuid,
self._runtime_number,
iso(self._runtime_datetime),
filename,
))
path.parent.mkdir(parents=True, exist_ok=True)
img.convert('RGB').save(path, format='JPEG', quality=95)
upload = Path("./data/photos/upload/%s" % (filename,))
upload.parent.mkdir(parents=True, exist_ok=True)
os.link(path, upload)
print("Photo saved: %s" % path.absolute())