Annexe : Application - Caméra

Rôle et utilité

apps/camera/app.py est une application utilitaire permettant de visualiser le flux vidéo d'une webcam branchée à l'ordinateur, et d'enregistrer des photos. C'est une démonstration brillante de la capacité de GrimOS à s'interfacer avec du matériel externe.

Implémentation technique

Pistes de modification

Code Source

import tkinter as tk
from tkinter import messagebox
from PIL import Image, ImageTk
import os
import datetime

def start(window, app_manager=None, **kwargs):
    # Essayons d'importer cv2. S'il n'est pas là, on affiche un message d'erreur clair.
    try:
        import cv2
    except ImportError:
        tk.Label(window, text="La librairie OpenCV n'est pas installée.\n\nVeuillez exécuter :\nsudo apt-get install python3-opencv\n\npuis relancez l'application.", fg="red", font=("Arial", 12)).pack(expand=True)
        return

    top_frame = tk.Frame(window, bg="lightgray", height=40)
    top_frame.pack(side="top", fill="x")

    icon_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "icons")
    window.icons = getattr(window, "icons", {})
    if 'btn_camera' not in window.icons:
        ipath = os.path.join(icon_dir, "btn_camera.png")
        if os.path.exists(ipath):
            window.icons['btn_camera'] = tk.PhotoImage(file=ipath)

    video_container = tk.Frame(window, bg="black")
    video_container.pack(fill="both", expand=True)

    video_lbl = tk.Label(video_container, bg="black")
    video_lbl.pack(expand=True)

    try:
        cap = cv2.VideoCapture(0)
    except Exception as e:
        messagebox.showerror("Erreur", f"Impossible d'accéder à la webcam : {e}")
        return

    if not cap.isOpened():
        messagebox.showerror("Erreur", "Aucune webcam détectée sur /dev/video0.")
        return

    def update_frame():
        if not window.winfo_exists():
            cap.release()
            return

        ret, frame = cap.read()
        if ret:
            # OpenCV utilise BGR, ImageTk nécessite RGB
            frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = Image.fromarray(frame_rgb)
            imgtk = ImageTk.PhotoImage(image=img)

            video_lbl.imgtk = imgtk # garder la référence
            video_lbl.configure(image=imgtk)

        window.after(30, update_frame)

    def take_photo():
        ret, frame = cap.read()
        if ret:
            img_dir = os.path.expanduser("~/Images")
            os.makedirs(img_dir, exist_ok=True)
            timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"photo_{timestamp}.jpg"
            filepath = os.path.join(img_dir, filename)

            cv2.imwrite(filepath, frame)

            if app_manager and hasattr(app_manager, 'desktop'):
                app_manager.desktop.show_toast(f"Photo sauvegardée :\n{filepath}")

    btn_photo = tk.Button(top_frame, text=" Prendre une photo", image=window.icons.get('btn_camera'), compound="left", font=("Arial", 11, "bold"), bg="#4CAF50", fg="white", relief="flat", command=take_photo)
    btn_photo.pack(side="left", padx=10, pady=5)

    def on_destroy(event):
        if str(event.widget) == str(window):
            if cap.isOpened():
                cap.release()

    window.bind("<Destroy>", on_destroy)
    update_frame()