Clipboard smetiště

Tento projekt pracuje s obsahem systémové schránky systému Windows (clipboard).
Jakýkoli text, obrázek nebo soubor, který je zkopírován, je okamžitě vložen do jednoho omezeného prostoru.
Obsah není upravován ani tříděn. Jednotlivé prvky se náhodně umisťují do formátu a vzájemně se překrývají.
Plátno se postupně zaplňuje bez možnosti návratu nebo editace.

Každý z obrázků níže je výstup z jednoho dne a zachycuje průběh mé práce u počítače.

25. 3. 2026
26. 3. 2026
27. 3. 2026
30. 3. 2026
31. 3. 2026
1. 4. 2026
2. 4. 2026
7. 4. 2026
8. 4. 2026
9. 4. 2026
10. 4. 2026
13. 4. 2026
14. 4. 2026
15. 4. 2026
16. 4. 2026
17. 4. 2026

Zdrojový kód
Zdrojový Python kód pro tento projekt je dostupný zde:

import os
import random
import textwrap
from datetime import datetime
import tkinter as tk

import pyperclip
from PIL import Image, ImageTk, ImageGrab, ImageDraw, ImageFont

# =========================
# Nastavení
# =========================
CANVAS_SIZE = 700
BG_COLOR = "white"
POLL_MS = 500
MAX_IMAGE_SIZE = 320
TEXT_WRAP_CHARS = 38
TEXT_FONT = ("Arial", 14)
TEXT_COLOR = "black"
FILE_COLOR = "#1f4e79"

# =========================
# Tkinter okno
# =========================
root = tk.Tk()
root.title("Clipboard Canvas")
root.geometry(f"{CANVAS_SIZE}x{CANVAS_SIZE}")

canvas = tk.Canvas(root, width=CANVAS_SIZE, height=CANVAS_SIZE, bg=BG_COLOR, highlightthickness=0)
canvas.pack(fill="both", expand=True)

# =========================
# PIL "skutečné" plátno pro export do PNG
# =========================
board = Image.new("RGB", (CANVAS_SIZE, CANVAS_SIZE), "white")
board_draw = ImageDraw.Draw(board)

try:
    pil_font = ImageFont.truetype("arial.ttf", 20)
except Exception:
    pil_font = ImageFont.load_default()

# Tohle je důležité: držíme reference na VŠECHNY Tk obrázky,
# jinak Tkinter staré obrázky "uklidí" a zmizí.
tk_images = []

last_text = None
last_image_hash = None
last_file_list = None

# =========================
# Pomocné funkce
# =========================
def get_random_position(item_w, item_h):
    """Vrátí náhodnou pozici tak, aby se objekt vešel celý do plátna."""
    item_w = min(item_w, CANVAS_SIZE)
    item_h = min(item_h, CANVAS_SIZE)

    max_x = max(0, CANVAS_SIZE - item_w)
    max_y = max(0, CANVAS_SIZE - item_h)

    x = random.randint(0, max_x)
    y = random.randint(0, max_y)
    return x, y

def wrap_text_for_display(text, width=TEXT_WRAP_CHARS):
    lines = []
    for paragraph in text.splitlines() or [""]:
        wrapped = textwrap.wrap(paragraph, width=width) or [""]
        lines.extend(wrapped)
    return "\n".join(lines)

def measure_text_block(text):
    """Hrubý odhad velikosti textového bloku pro umístění."""
    lines = text.split("\n")
    max_len = max((len(line) for line in lines), default=1)
    w = min(CANVAS_SIZE, 12 + max_len * 9)
    h = 12 + len(lines) * 24
    return w, h

def draw_text_on_canvas_and_board(text, color=TEXT_COLOR, prefix=None):
    display_text = f"{prefix}{text}" if prefix else text
    wrapped = wrap_text_for_display(display_text)
    w, h = measure_text_block(wrapped)
    x, y = get_random_position(w, h)

    # Tkinter canvas
    canvas.create_text(
        x, y,
        anchor="nw",
        text=wrapped,
        fill=color,
        font=TEXT_FONT,
        width=w
    )

    # PIL board
    board_draw.multiline_text((x, y), wrapped, fill=color, font=pil_font, spacing=6)

def draw_image_on_canvas_and_board(img):
    # zmenšíme kopii, originál neni potřeba
    img = img.convert("RGB")
    img.thumbnail((MAX_IMAGE_SIZE, MAX_IMAGE_SIZE))

    w, h = img.size
    x, y = get_random_position(w, h)

    # Tkinter canvas
    tk_img = ImageTk.PhotoImage(img)
    tk_images.append(tk_img)
    canvas.create_image(x, y, anchor="nw", image=tk_img)

    # PIL board
    board.paste(img, (x, y))

def hash_pil_image(img):
    # Rychlejší než hash(img.tobytes()) na obrovských datech:
    tiny = img.copy()
    tiny.thumbnail((64, 64))
    return hash(tiny.tobytes())

def save_canvas_png(event=None):
    filename = f"clipboard_canvas_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
    board.save(filename, "PNG")
    print(f"Uloženo: {os.path.abspath(filename)}")
    
# =========================
# Clipboard monitoring
# =========================
def check_clipboard():
    global last_text, last_image_hash, last_file_list

    # 1) Zkusit obrázek / seznam souborů
    try:
        clip = ImageGrab.grabclipboard()

        # Obrázek
        if isinstance(clip, Image.Image):
            img_hash = hash_pil_image(clip)
            if img_hash != last_image_hash:
                last_image_hash = img_hash
                draw_image_on_canvas_and_board(clip)

        # Soubory
        elif isinstance(clip, list):
            normalized = tuple(map(str, clip))
            if normalized != last_file_list:
                last_file_list = normalized
                for path in normalized:
                    draw_text_on_canvas_and_board(path, color=FILE_COLOR, prefix="[FILE] ")
    except Exception:
        pass

    # 2) Zkusit text
    try:
        text = pyperclip.paste()
        if isinstance(text, str):
            text = text.strip()
            if text and text != last_text:
                last_text = text
                draw_text_on_canvas_and_board(text)
    except Exception:
        pass

    root.after(POLL_MS, check_clipboard)

# =========================
# Klávesy
# =========================
root.bind("<KeyPress-s>", save_canvas_png)
root.bind("<KeyPress-S>", save_canvas_png)
root.focus_force()

# start
check_clipboard()
root.mainloop()