POMOCNIK · Tom 1OBEJMUJE · R.4, 7, 16, 18WYMAGA · Python 3, żadnego DCC
Pomocnik praktyczny / Tom 1

Zbuduj mini-pipeline od zera

Te same tematy co w rozdziałach 4, 7, 16 i 18, ale tym razem piszesz kod: prosty publish script, hook do niego i okienko PySide, które to wszystko spina — krok po kroku.

Zanim zaczniesz

Cały kod poniżej to zwykły Python — nie potrzebujesz Mayi ani żadnego innego DCC, żeby go uruchomić. Zamiast prawdziwej sceny 3D „publikujemy" mały plik JSON, udając plik roboczy artysty — mechanizm jest identyczny, tylko dane są uproszczone do minimum, żebyś mógł to odpalić w dowolnym terminalu i zobaczyć wynik.

Krok 1 · Rozdział 4

Konwencja ścieżek

Zaczynamy od najprostszego elementu: funkcji, która buduje ścieżkę do pliku na podstawie kontekstu, zamiast pozwalać komukolwiek wpisywać ją ręcznie.

import os

def build_path(show, task, filename, kind="work"):
    """kind: 'work' albo 'publish'"""
    return os.path.join(show, task, kind, filename)

print(build_path("DEMO", "lighting", "shot0100.json"))
# DEMO/lighting/work/shot0100.json

To dosłownie cała treść Rozdziału 4 zamknięta w jednej funkcji: nazwa i lokalizacja pliku przestają być czymś, co artysta wpisuje z pamięci, i stają się wynikiem obliczenia z kontekstu.

Krok 2 · Rozdział 7

Publish script

Zanim zobaczysz gotowe rozwiązanie — spróbuj sam. Napisz funkcję next_version(publish_dir), która przeszuka folder publish_dir w poszukiwaniu plików nazwanych *_v001.json, *_v002.json itd., i zwróci numer o jeden większy niż najwyższy znaleziony (albo 1, jeśli folder jest pusty).

Pokaż rozwiązanie
import glob, re

def next_version(publish_dir):
    versions = []
    for f in glob.glob(os.path.join(publish_dir, "*_v*.json")):
        m = re.search(r"_v(\d+)\.json$", f)
        if m:
            versions.append(int(m.group(1)))
    return max(versions, default=0) + 1

Mając build_path() i next_version(), publish script pisze się niemal sam:

import json

def publish(show, task, name, data):
    publish_dir = build_path(show, task, "", kind="publish")
    os.makedirs(publish_dir, exist_ok=True)
    v = next_version(publish_dir)
    out_path = os.path.join(publish_dir, f"{name}_v{v:03d}.json")
    with open(out_path, "w") as f:
        json.dump(data, f, indent=2)
    print(f"Opublikowano: {out_path}")
    return out_path

publish("DEMO", "lighting", "shot0100", {"pass": "beauty"})
# Opublikowano: DEMO/lighting/publish/shot0100_v001.json

Uruchom to dwa razy z rzędu — zobaczysz shot0100_v001.json, potem shot0100_v002.json. To już cały mechanizm z Rozdziału 7, tylko bez kroku zatwierdzenia i bez Perforce w tle.

Krok 3 · Rozdział 18

Hook

Dopiszmy prosty mechanizm hooków — listę funkcji wywoływanych automatycznie po każdej publikacji, dokładnie jak w Rozdziale 18.

hooks = []

def register_hook(fn):
    hooks.append(fn)

def run_hooks(published_path):
    for hook in hooks:
        hook(published_path)

# dopisz na końcu funkcji publish(), przed "return out_path":
#     run_hooks(out_path)

def notify_department(path):
    print(f"[hook] Nowa publikacja gotowa -> {path}")

register_hook(notify_department)

Dodaj wywołanie run_hooks(out_path) na końcu publish(), tuż przed return, i przetestuj ponownie — zobaczysz, że powiadomienie pojawia się samo, bez zmiany ani jednej linijki w miejscu, gdzie wywołujesz publish(). To dokładnie ten mechanizm rozdziela od siebie Rozdział 18: logika publikacji nie musi nic wiedzieć o tym, kto i dlaczego chce zareagować na jej wynik.

Krok 4 · Rozdział 16

Okienko PySide

Ten fragment wymaga już zainstalowanego PySide (pip install PySide6) albo uruchomienia wewnątrz DCC, który je osadza (Rozdział 16) — potraktuj go jako kod do przeczytania i samodzielnego wypróbowania, nie do wklejenia w ten sam plik co poprzednie kroki.

from PySide6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QLabel, QPushButton
)

class PublishWindow(QWidget):
    def __init__(self, show, task, name, data):
        super().__init__()
        self.show_, self.task, self.name, self.data = show, task, name, data
        self.setWindowTitle("Publish")
        layout = QVBoxLayout(self)
        # kontekst pokazany, nie do edycji — zasada z Rozdziału 16
        layout.addWidget(QLabel(f"{show} / {task} / {name}"))
        btn = QPushButton("Publish")
        btn.clicked.connect(self.on_publish)
        layout.addWidget(btn)

    def on_publish(self):
        publish(self.show_, self.task, self.name, self.data)

Zwróć uwagę, że on_publish() woła dokładnie tę samą funkcję publish(), którą napisałeś w Kroku 2 — okno nie duplikuje logiki, tylko daje jej przycisk. Dokładnie tak, jak Rozdział 15 opisał różnicę między skryptem a narzędziem.