Files
photobox/main.py
T
2026-04-23 19:01:01 +02:00

282 lines
6.8 KiB
Python

import sys
import cv2
import json
import time
import os
from ftplib import FTP
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QVBoxLayout
from PyQt6.QtGui import QImage, QPixmap, QPainter, QFont, QColor
from PyQt6.QtCore import QTimer, Qt
import qrcode
CONFIG_PATH = "config.json"
def load_config():
with open(CONFIG_PATH) as f:
return json.load(f)
def save_config(cfg):
with open(CONFIG_PATH, "w") as f:
json.dump(cfg, f, indent=2)
CONFIG = load_config()
def create_capture(index):
cap = cv2.VideoCapture(index, cv2.CAP_MSMF)
if not cap.isOpened():
return None
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
for _ in range(20):
ret, frame = cap.read()
if ret and frame is not None:
return cap
cap.release()
return None
def find_cameras():
working = []
for i in range(10):
cap = create_capture(i)
if cap is not None:
working.append(i)
cap.release()
return working
class CameraSelect(QWidget):
def __init__(self, cameras):
super().__init__()
self.setWindowTitle("Kamera auswählen")
self.setGeometry(200, 200, 400, 300)
self.selected = None
layout = QVBoxLayout()
for cam in cameras:
btn = QPushButton(f"Kamera {cam}")
btn.clicked.connect(lambda _, c=cam: self.select(c))
layout.addWidget(btn)
self.setLayout(layout)
def select(self, cam):
self.selected = cam
self.close()
LIVE, COUNTDOWN, PHOTO, QR = range(4)
class PhotoApp(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Photo Booth")
self.showFullScreen()
self.label = QLabel(self)
self.label.setGeometry(0, 0, 1920, 1080)
self.cap = None
self.init_camera()
self.state = LIVE
self.current_frame = None
self.photo = None
self.qr_img = None
self.countdown = 0
self.timer = QTimer()
self.timer.timeout.connect(self.update_frame)
self.timer.start(30)
self.countdown_timer = QTimer()
self.countdown_timer.timeout.connect(self.update_countdown)
def init_camera(self):
cams = find_cameras()
if not cams:
sys.exit(1)
last = CONFIG.get("camera", {}).get("last_used")
if last in cams:
cam_index = last
else:
selector = CameraSelect(cams)
selector.show()
while selector.isVisible():
QApplication.processEvents()
cam_index = selector.selected
if cam_index is None:
sys.exit(1)
CONFIG["camera"]["last_used"] = cam_index
save_config(CONFIG)
self.cap = create_capture(cam_index)
if self.cap is None:
sys.exit(1)
def update_frame(self):
if not self.cap:
return
ret, frame = self.cap.read()
if not ret or frame is None:
return
self.current_frame = frame
if self.state == LIVE:
self.draw(frame, "Drücke 1 für Foto")
elif self.state == COUNTDOWN:
self.draw(frame, str(self.countdown), big=True)
elif self.state == PHOTO:
self.draw(self.photo, "2=Speichern | 3=Drucken")
elif self.state == QR:
self.draw(self.qr_img, "QR Code scannen")
def draw(self, frame, text="", big=False):
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb.shape
img = QImage(rgb.data, w, h, ch * w, QImage.Format.Format_RGB888).copy()
pix = QPixmap.fromImage(img)
painter = QPainter(pix)
painter.fillRect(0, 900, 1920, 180, QColor(0, 0, 0, 120))
painter.setPen(Qt.GlobalColor.white)
if big:
painter.setFont(QFont("Arial", 200))
painter.drawText(pix.rect(), Qt.AlignmentFlag.AlignCenter, text)
else:
painter.setFont(QFont("Arial", 40))
painter.drawText(50, 1000, text)
if self.countdown > 0 and not big:
painter.drawText(1700, 1000, str(self.countdown))
painter.end()
self.label.setPixmap(pix)
def keyPressEvent(self, event):
key = event.text()
if key == CONFIG["buttons"]["capture"]:
self.start_countdown()
elif key == CONFIG["buttons"]["save"]:
self.save_photo()
elif key == CONFIG["buttons"]["print"]:
self.print_photo()
def start_countdown(self):
if self.state != LIVE:
return
self.state = COUNTDOWN
self.countdown = 3
self.countdown_timer.start(1000)
def take_photo(self):
self.photo = self.current_frame.copy()
self.state = PHOTO
self.countdown = 60
def save_photo(self):
if self.state != PHOTO:
return
url = self.upload(self.photo)
self.make_qr(url)
self.state = QR
self.start_qr_timer()
def print_photo(self):
if self.state != PHOTO:
return
url = self.upload(self.photo)
self.make_qr(url)
self.print_image()
self.state = QR
self.start_qr_timer()
def update_countdown(self):
self.countdown -= 1
if self.state == COUNTDOWN and self.countdown == 0:
self.take_photo()
elif self.countdown <= 0:
self.reset()
def start_qr_timer(self):
self.countdown = 30
self.countdown_timer.start(1000)
def reset(self):
self.state = LIVE
self.countdown = 0
self.photo = None
self.qr_img = None
self.countdown_timer.stop()
def upload(self, img):
filename = f"{int(time.time())}.jpg"
path = os.path.join(CONFIG["photo_path"], filename)
os.makedirs(CONFIG["photo_path"], exist_ok=True)
cv2.imwrite(path, img)
ftp = FTP(CONFIG["ftp"]["host"])
ftp.login(CONFIG["ftp"]["user"], CONFIG["ftp"]["password"])
with open(path, "rb") as f:
ftp.storbinary(f"STOR {filename}", f)
ftp.quit()
return CONFIG["ftp"]["base_url"] + filename
def make_qr(self, url):
img = qrcode.make(url)
img.save("qr.png")
self.qr_img = cv2.imread("qr.png")
def print_image(self):
temp = "print.jpg"
cv2.imwrite(temp, self.photo)
if os.name == "nt":
os.startfile(temp, "print")
else:
os.system(f"lp {temp}")
def closeEvent(self, event):
if self.cap:
self.cap.release()
cv2.destroyAllWindows()
event.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
win = PhotoApp()
win.show()
sys.exit(app.exec())