Skip to main content

License: MIT PyPI Python Tests

pythontk

Composable Python primitives for files, strings, iteration, math, geometry, images, video, audio, and networking — the DCC-agnostic foundation of a game-art tooling ecosystem.

Pure Python: no Qt, no DCC imports, two hard dependencies (numpy, Pillow). Everything heavier — FFmpeg, OpenCV, rembg, PyMeshLab — is optional and feature-gated, so the core runs identically in mayapy, Blender's Python, a CI runner, or a bare venv.

Why

pythontk is the bottom of the chain pythontk → uitk → mayatk / blendertk → tentacle: everything above imports it; it imports nothing above it. The environment-independent 80% of every tool lives at this layer — in effect, the ecosystem's standard library.

Two rules shape it:

  • Placed by data type, not domain. Sharpest-frame extraction lives in vid_utils, perceptual-hash curation in img_utils — not in a "photogrammetry" package — so each primitive stays independently reusable. Domain pipelines (PBR conversion, photogrammetry ingest, timeline audio events) are compositions of these, assembled downstream.
  • Shared code moves down. When two downstream tools need the same helper, it moves here and becomes the single source of truth — Maya and Blender panels share one calculator engine, one material-report formatter, one point-clustering routine, instead of drifting copies.

Install

pip install pythontk

Optional dependencies, each gating a specific feature (guarded by is_available() checks — nothing else breaks without them):

  • FFmpeg (on PATH) — audio conversion / compositing, video compression
  • OpenCV — video frame extraction, image curation, exposure equalization, a few ImgUtils ops
  • rembg — background mask generation (MaskGenerator)
  • PyMeshLab — mesh repair (MeshCleaner)

Packages

Everything is exposed at the package root via the lazy-loading resolver — bare or class-qualified:

import pythontk as ptk

ptk.filter_list(...)                # bare form — wildcard-exposed
ptk.ImgUtils.pack_channels(...)     # class-qualified — explicit, collision-proof
Package What it covers
audio_utils FFmpeg-backed conversion, composite WAV building, waveform envelopes
core_utils The infrastructure layer: mixins (LoggingMixin, HelpMixin, SingletonMixin), listify, package bootstrap (module_resolver), hot-reload (ModuleReloader), app orchestration (AppLauncher, HandoffBridge), task pipeline (TaskFactory), process output streaming (OutputStream/ProcessReader/LogTailer), config/template stores (PresetStore, TemplateSet, SchemaSpec, UserConfig), QC gates, ExecutionMonitor, hierarchy diffing, color primitives (Color / ColorPair / Palette) — plus the domain engines in core_utils/engines/: shots (timeline model/planner/manifest), instancing (AssemblySorter), textures (MapFactory, MapCompositor, map registry/optimizer)
file_utils Filtered directory traversal, atomic writes, JSON helpers, cloud-placeholder detection, mesh format conversion, embedded metadata
geo_utils Pure geometry — Polyline (order/resample/smooth/simplify), PointCloud (PCA, clustering), RailSurface (line-pair framing)
img_utils Pillow-backed image ops, channel packing, exposure equalization, image curation, mask generation
iter_utils Flatten, dedupe, wildcard filtering of lists/dicts, integer-sequence collapse
math_utils Vectors, clustering, remap/lerp/clamp, easing curves (ProgressionCurves), band-limited noise, safe expression evaluation
net_utils SSH client, generic JSON-RPC client + DCC plugin installer, credentials, port/RDP helpers, live WebXR preview server
str_utils Sanitizing, batch rename, affix handling, FuzzyMatcher, hotkey-token parsing
vid_utils Frame rate probing, compression, sharpest-frame extraction

Full public surface (every class, method, signature — auto-generated): API_REGISTRY.md; compact index: API_INDEX.md.


Tour

A curated subset — one example per idea, not per function.

LoggingMixin

Structured logging for any class — custom levels, spam prevention, file tee, ring-buffer dump:

import pythontk as ptk

class MyProcessor(ptk.LoggingMixin):
    def process(self):
        self.logger.info("Starting process")
        self.logger.success("Task completed")      # custom level
        self.logger.error_once("Connection failed") # logs once per 5 min, not per retry
        self.logger.log_box("Summary", ["Files: 10", "Errors: 0"])

MyProcessor.logger.setLevel("DEBUG")
MyProcessor.set_log_file("process.log")             # continuous tee
MyProcessor.enable_log_buffer(2000)                 # O(1) ring buffer, dump on demand

@listify

Make any function accept a single item or a list, with optional multi-threading:

@ptk.CoreUtils.listify(threading=True)
def process_texture(filepath):
    return expensive_operation(filepath)

process_texture("texture.png")                  # single result
process_texture(["a.png", "b.png", "c.png"])    # list, parallelized

One filtering language

The same include/exclude wildcard language runs through the whole library — lists, dicts, directory traversal, image sets:

ptk.filter_list(
    ["mesh_main", "mesh_backup", "mesh_LOD0", "cube_old"],
    inc=["mesh_*", "cube_*"],
    exc=["*_backup", "*_old"],
)
# ['mesh_main', 'mesh_LOD0']

files = ptk.get_dir_contents(
    "/path/to/project",
    content="filepath",              # file | filename | filepath | dir | dirpath
    recursive=True,
    inc_files=["*.py", "*.pyw"],
    exc_files=["*test*", "*_backup*"],
    exc_dirs=["__pycache__", ".git", "venv"],
)

Texture maps — pack, convert, identify

# Pack grayscale maps into RGBA channels for game engines
ptk.ImgUtils.pack_channels(
    channel_files={"R": "ao.png", "G": "roughness.png", "B": "metallic.png"},
    output_path="packed_ORM.png",
)

# Spec/Gloss → Metal/Rough PBR conversion
base_color, metallic, roughness = ptk.MapFactory.convert_spec_gloss_to_pbr(
    specular_map="specular.png", glossiness_map="gloss.png", diffuse_map="diffuse.png",
)

# Bump/height → normal map
ptk.MapFactory.convert_bump_to_normal("height.png", output_format="opengl", intensity=1.5)

# Identify map types from filenames (100+ naming conventions)
ptk.MapFactory.resolve_map_type("character_Normal_DirectX.png")  # "Normal_DirectX"
ptk.MapFactory.resolve_map_type("material_BC.tga")               # "Base_Color"

The Qt panels that drive these engines interactively — Map Converter, Map Packer, Map Compositor — ship in the extapps repo; pythontk itself stays UI-agnostic.

Capture ingest — sharpest-frame extraction & image curation

Video-to-photogrammetry primitives. Fixed-step frame extraction wastes frames when the camera is still and starves overlap when it moves — sharpest-of-window picks the best frame from every part of the timeline instead. Then perceptual-hash curation collapses near-duplicates:

frames = ptk.FrameExtractor().extract_frames_sharpest(
    "capture.mp4", "frames/", window_sec=1.0,   # sharpest frame per second of footage
)

curated_dirs = ptk.ImageCurator().curate(
    ["frames/"], "curated/",
    hash_threshold=5,                 # Hamming distance on dHash — near-dupes cluster
    sharpness_floor_percentile=10,    # drop the blurriest tenth of the survivors
)

ExposureEqualizer (cross-set exposure / white-balance matching) and MaskGenerator (rembg-backed background masks) round out the ingest cluster.

Batch rename & fuzzy matching

ptk.find_str_and_format(["mesh_old", "cube_old"], to="*_new", fltr="*_old")
# ['mesh_new', 'cube_new']

matches, matched_missing, matched_extra = ptk.FuzzyMatcher.find_trailing_digit_matches(
    missing_paths=["group1|mesh_01", "group1|mesh_02"],
    extra_paths=["group1|mesh_03", "group1|mesh_05"],
)

Geometry & math

from pythontk import Polyline, ProgressionCurves

ordered = Polyline.order_points(scattered_points, closed_path=True)
smoothed = Polyline.smooth(ordered, window_size=3)

factor = ProgressionCurves.ease_in_out(0.5)      # also: bounce, elastic, weighted, ...
ptk.remap(50, old_range=(0, 100), new_range=(0, 1))   # 0.5

ptk.collapse_integer_sequence([1, 2, 3, 5, 7, 8, 9, 15])   # "1-3, 5, 7-9, 15"

Long-running task escape hatch

@ptk.ExecutionMonitor.execution_monitor(threshold=30, message="Processing")
def batch_process():
    ...  # shows an abort dialog if it runs past 30s

Plugin discovery (AST-based — never executes plugin code)

plugins = ptk.get_classes_from_path(
    "plugins/", returned_type=["classobj", "filepath"], inc=["*Plugin"], exc=["*Base"],
)

Infrastructure the ecosystem is built on

Beyond the data-type utilities, core_utils supplies the machinery the layers above are built on:

  • bootstrap_package (module_resolver) — the lazy-loading package root. Every ecosystem package (uitk, mayatk, blendertk, …) exposes its public surface through it.
  • PresetStore / TemplateSet / SchemaSpec / UserConfig — Qt-free named-preset and schema-validated-template stores with built-in + user tiers; uitk's PresetManager is a GUI over them.
  • AppLauncher / AppInstaller / HandoffBridge — find, launch, and hand work to external applications; the base of the ecosystem's Maya/Blender/Marmoset/Substance bridges and of mayatk's MayaConnection. HandoffBridge owns one invariant flow — resolve → preflight → produce → deliver → ingest — with the delivery step a per-mode Deliverer strategy, so three hand-off shapes come off one export pipeline: send() (SEND_TO, detached launch), save_as() (SAVE_AS, blocking run that keeps a native file of the target's format), and round_trip() (ROUND_TRIP, blocking run whose artifact is an intermediate _ingest folds back onto the host's own objects, so what the caller gets is changed host state rather than a file — the target either edits the payload in place, as RizomUV does, or writes a new artifact, as mayatk's Blender lightmap bake does). The mode strings are declared once, in core_utils/script_template.py; every bridge imports them, because they are an on-disk contract (each template's BRIDGE_MODES tuple) and a local copy is a second dialect of a file format.
  • RpcClient + RpcPlugin — both ends of the plugin-hosted JSON-RPC protocol, shipped together so the wire format cannot drift. net_utils.rpc.plugin_core is the server that runs inside Toolbag / Painter; it is standard-library only so an installed plugin payload can carry a verbatim copy where pythontk is not importable (staged by m3trik/scripts/sync_rpc_core.py).
  • QcLog / QcGate — structured run logs and threshold-based acceptance gates for batch pipelines.
  • HierarchyPath / HierarchyIndexer / HierarchyMatching / HierarchyAnalyzer / HierarchyDiff — delimited-path hierarchy toolkit: HierarchyPath is the single home for path-string primitives (namespace cleaning, split/join, leaf/parent/tail); indexing and exact / tail-path / fuzzy matching build on it; the analyzer detects moved items (deterministic best-pair assignment), and HierarchyDiff.from_differences turns analyzer records into a JSON-serializable diff.
  • HelpMixin.help(), .source(), .signature() introspection on any class that mixes it in. Reachable from a shell (or an agent) without a REPL snippet via python -m pythontk <dotted.path> [member] [--json|--source|--where|--signature|--brief]; it reads the live object, so it answers what the static API_REGISTRY.md cannot.

Guides

  • Live WebXR preview — the shared DCC → glTF → headset pipeline (PreviewServer / PreviewDeliverer / PreviewBridge + MeshConvert + the bundled three.js viewer): how a baked lightmap is carried through a format that has no lightmap slot, what the scene sidecar repairs and how to read it back out of the deliverable, and the measured size/memory budget.

Links

License

MIT — see LICENSE.

Release files for pythontk 0.9.14

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for pythontk 0.9.14
File Interpreter ABI Platform
pythontk-0.9.14-py3-none-any.whl Python 3 none any Details

Release files / pythontk-0.9.14-py3-none-any.whl

Download URL pythontk-0.9.14-py3-none-any.whl
Size 740.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c9e1db0e26be9529a67da0641b3c92bb72581892959931bda52de518fa799cb1
BLAKE2b-256 checksum
How to use checksums
48874d4eb820f72d632903a3a83a9a1260681a3be6fa2460bc743cbd604eb36d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Release history Release notifications | RSS feed

0.11.2

1 release file

0.11.1

1 release file

0.11.0

1 release file

0.10.1

1 release file

0.10.0

1 release file

0.9.40

1 release file

0.9.39

1 release file

0.9.38

1 release file

0.9.37

1 release file

0.9.36

1 release file

0.9.35

1 release file

0.9.34

1 release file

0.9.33

1 release file

0.9.32

1 release file

0.9.31

1 release file

0.9.30

1 release file

0.9.28

1 release file

0.9.26

1 release file

0.9.24

1 release file

0.9.22

1 release file

0.9.20

1 release file

0.9.18

1 release file

0.9.16

1 release file

This release

0.9.14 This release

1 release file

0.9.12

1 release file

0.9.10

1 release file

0.9.8

1 release file

0.9.7

1 release file

0.9.5

1 release file

0.9.3

1 release file

0.9.1

1 release file

0.8.99

1 release file

0.8.98

1 release file

0.8.96

1 release file

0.8.94

1 release file

0.8.92

1 release file

0.8.90

1 release file

0.8.88

1 release file

0.8.86

1 release file

0.8.84

1 release file

0.8.82

1 release file

0.8.80

1 release file

0.8.79

1 release file

0.8.77

1 release file

0.8.76

1 release file

0.8.74

1 release file

0.8.72

1 release file

0.8.70

1 release file

0.8.68

1 release file

0.8.66

1 release file

0.8.65

1 release file

0.8.63

1 release file

0.8.61

1 release file

0.8.59

1 release file

0.8.58

1 release file

0.8.56

1 release file

0.8.54

1 release file

0.8.51

1 release file

0.8.49

1 release file

0.8.48

1 release file

0.8.46

1 release file

0.8.45

1 release file

0.8.43

1 release file

0.8.42

1 release file

0.8.41

1 release file

0.8.40

1 release file

0.8.39

1 release file

0.8.38

1 release file

0.8.37

1 release file

0.8.34

1 release file

0.8.32

1 release file

0.8.30

1 release file

0.8.28

1 release file

0.8.26

1 release file

0.8.24

1 release file

0.8.23

1 release file

0.8.21

1 release file

0.8.19

1 release file

0.8.17

1 release file

0.8.15

1 release file

0.8.13

1 release file

0.8.11

1 release file

0.8.10

1 release file

0.8.9

1 release file

0.8.7

1 release file

0.8.6

1 release file

0.8.4

1 release file

0.8.3

1 release file

0.8.1

1 release file

0.8.0

1 release file

0.7.99

1 release file

0.7.97

1 release file

0.7.96

1 release file

0.7.95

1 release file

0.7.94

1 release file

0.7.93

1 release file

0.7.92

1 release file

0.7.90

1 release file

0.7.89

1 release file

0.7.88

1 release file

0.7.86

1 release file

0.7.85

1 release file

0.7.84

1 release file

0.7.83

1 release file

0.7.81

1 release file

0.7.80

1 release file

0.7.78

1 release file

0.7.77

1 release file

0.7.75

1 release file

0.7.74

1 release file

0.7.72

1 release file

0.7.71

1 release file

0.7.70

1 release file

0.7.69

1 release file

0.7.68

1 release file

0.7.67

1 release file

0.7.66

1 release file

0.7.65

1 release file

0.7.64

1 release file

0.7.63

1 release file

0.7.62

1 release file

0.7.61

1 release file

0.7.60

1 release file

0.7.59

1 release file

0.7.58

1 release file

0.7.57

1 release file

0.7.56

1 release file

0.7.55

1 release file

0.7.54

1 release file

0.7.53

1 release file

0.7.52

1 release file

0.7.51

1 release file

0.7.50

1 release file

0.7.49

1 release file

0.7.48

1 release file

0.7.47

1 release file

0.7.46

1 release file

0.7.45

1 release file

0.7.44

1 release file

0.7.43

1 release file

0.7.42

1 release file

0.7.41

1 release file

0.7.40

1 release file

0.7.39

1 release file

0.7.38

1 release file

0.7.37

1 release file

0.7.36

1 release file

0.7.35

1 release file

0.7.34

2 release files

0.7.33

2 release files

0.7.32

2 release files

0.7.31

2 release files

0.7.28

2 release files

0.7.27

2 release files

0.7.26

2 release files

0.7.25

2 release files

0.7.24

2 release files

0.7.22

2 release files

0.7.19

2 release files

0.7.18

2 release files

0.7.17

2 release files

0.7.15

2 release files

0.7.14

2 release files

0.7.13

2 release files

0.7.12

2 release files

0.7.11

2 release files

0.7.10

2 release files

0.7.9

2 release files

0.7.8

2 release files

0.7.7

2 release files

0.7.6

2 release files

0.7.5

2 release files

0.7.4

2 release files

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.9

2 release files

0.6.8

2 release files

0.6.7

2 release files

0.6.6

2 release files

0.6.4

2 release files

0.6.2

2 release files

0.6.0

2 release files

0.5.9

2 release files

0.5.8

2 release files

0.5.7

2 release files

0.5.6

2 release files

0.5.5

2 release files

0.5.4

2 release files

0.5.3

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page