Clipboard Dump

This project works with the contents of the Windows system clipboard .
Any text, image, or file that is copied is immediately pasted into a single limited space.
The content is neither edited nor sorted. Individual elements are randomly placed within the format and overlap one another.
The canvas gradually fills up with no possibility of going back or editing.

Each of the images below is the output from a single day and captures the course of my work at the computer.

March 25, 2026
March 26, 2026
March 27, 2026
March 30, 2026
March 31, 2026
April 1, 2026
April 2, 2026
April 7, 2026
April 8, 2026
April 9, 2026
April 10, 2026
April 13, 2026
April 14, 2026
April 15, 2026
April 16, 2026
April 17, 2026

Source Code
The Python source code for this project is available here:

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()