Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 13 additions & 14 deletions docs/source/runtime/cli-and-execution.rst
Original file line number Diff line number Diff line change
Expand Up @@ -266,26 +266,25 @@ ClearEx now separates movie generation into two explicit operations:

Runtime storage:

- ``render_movie`` latest metadata:
- ``clearex/results/render_movie/latest``
- ``compile_movie`` latest metadata:
- ``clearex/results/compile_movie/latest``
- Default in-store artifacts:
- ``<analysis_store>/clearex/results/visualization/latest/keyframes.json``
- ``<analysis_store>/clearex/results/render_movie/latest/render_manifest.json``
- ``<analysis_store>/clearex/results/render_movie/latest/level_<nn>_frames/frame_000000.png``
- ``<analysis_store>/clearex/results/compile_movie/latest/*.mp4``
- ``<analysis_store>/clearex/results/compile_movie/latest/*.mov``
- Override note:
- ``output_directory`` can still redirect ``render_movie`` or
``compile_movie`` to an external export tree when desired.
- ``render_movie`` latest metadata lives under
``clearex/results/render_movie/latest``.
- ``compile_movie`` latest metadata lives under
``clearex/results/compile_movie/latest``.
- Default in-store artifacts include
``<analysis_store>/clearex/results/visualization/latest/keyframes.json``,
``<analysis_store>/clearex/results/render_movie/latest/render_manifest.json``,
``<analysis_store>/clearex/results/render_movie/latest/level_<nn>_frames/frame_000000.png``,
``<analysis_store>/clearex/results/compile_movie/latest/*.mp4``, and
``<analysis_store>/clearex/results/compile_movie/latest/*.mov``.
- ``output_directory`` can still redirect ``render_movie`` or
``compile_movie`` to an external export tree when desired.

Practical guidance:

- Use coarse levels such as ``[1]`` or ``[2]`` plus moderate frame sizes for
preview renders.
- Use level ``0`` and the final frame size for publication renders.
- `default_transition_frames` around ``48`` is a good default for smooth
- ``default_transition_frames`` around ``48`` is a good default for smooth
motion.
- ``mp4_crf`` in the ``16`` to ``24`` range is a reasonable review/final
quality band, with lower values trading size for quality.
Expand Down
32 changes: 32 additions & 0 deletions src/clearex/gui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,38 @@ def _resolve_gui_asset_path(filename: str) -> Path:
return (_GUI_ASSET_DIRECTORY / filename).resolve()


def _apply_application_icon(app: Any) -> None:
"""Fallback no-op when PyQt6 is unavailable at import time.

Parameters
----------
app : Any
Placeholder application object.

Returns
-------
None
No-op fallback used until the PyQt implementation overrides it.
"""
Comment on lines +250 to +261

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring says the fallback is used "until the PyQt implementation overrides it", but when HAS_PYQT6 is False the override never happens in the same interpreter session. Consider rephrasing to clarify that these are permanent no-ops in non-PyQt environments, and are only shadowed by the PyQt definitions when PyQt6 is importable during module import.

Copilot uses AI. Check for mistakes.
del app


def _show_startup_splash(app: Any) -> None:
"""Fallback no-op when PyQt6 is unavailable at import time.

Parameters
----------
app : Any
Placeholder application object.

Returns
-------
None
No-op fallback used until the PyQt implementation overrides it.
"""
del app


def _primary_screen_available_size() -> Optional[tuple[int, int]]:
"""Return the available size of the primary screen for the active app.

Expand Down
48 changes: 48 additions & 0 deletions tests/gui/test_gui_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
from __future__ import annotations

import inspect
import json
import math
import subprocess
import sys
import threading
from pathlib import Path
from types import SimpleNamespace
Expand Down Expand Up @@ -259,6 +262,51 @@ def test_resolve_initial_dialog_dimensions_keeps_larger_default_size() -> None:
assert startup_size == app_module._SETUP_DIALOG_PREFERRED_SIZE


def test_gui_module_exports_fallback_helpers_when_pyqt6_is_unavailable() -> None:
repo_root = Path(__file__).resolve().parents[2]
script = f"""
import builtins
import importlib
import json
import sys

sys.path.insert(0, {str(repo_root / "src")!r})
original_import = builtins.__import__


def _blocked_import(name, globals=None, locals=None, fromlist=(), level=0):
if name == "PyQt6" or name.startswith("PyQt6."):
raise ImportError("blocked for test")
return original_import(name, globals, locals, fromlist, level)


builtins.__import__ = _blocked_import
module = importlib.import_module("clearex.gui.app")
print(
json.dumps(
{{
"HAS_PYQT6": module.HAS_PYQT6,
"has_apply_application_icon": hasattr(module, "_apply_application_icon"),
"has_show_startup_splash": hasattr(module, "_show_startup_splash"),
}}
)
)
"""
result = subprocess.run(
[sys.executable, "-c", script],

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inline script passed to python -c is indented inside the triple-quoted string (leading spaces before import ...). That will raise IndentationError: unexpected indent in the subprocess. Consider left-aligning the script content or wrapping the string with textwrap.dedent()/inspect.cleandoc() before passing it to -c so the first statement starts at column 0.

Suggested change
[sys.executable, "-c", script],
[sys.executable, "-c", inspect.cleandoc(script)],

Copilot uses AI. Check for mistakes.
check=True,
capture_output=True,
text=True,
cwd=repo_root,
)

assert json.loads(result.stdout) == {
"HAS_PYQT6": False,
"has_apply_application_icon": True,
"has_show_startup_splash": True,
}


def test_resolve_initial_dialog_dimensions_clamps_to_available_screen() -> None:
available_size = (1366, 900)
minimum_size, startup_size = app_module._resolve_initial_dialog_dimensions(
Expand Down
5 changes: 4 additions & 1 deletion tests/io/test_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,10 @@ def _fake_spec_cluster(*, scheduler, workers, asynchronous):
"_detect_cuda_library_paths",
lambda: ["/cuda/runtime/lib", "/cuda/cudnn/lib"],
)
path_env_var = experiment_module._library_path_env_vars_for_platform()[0]
path_env_vars = experiment_module._library_path_env_vars_for_platform()
for env_var in path_env_vars:
monkeypatch.delenv(env_var, raising=False)
path_env_var = path_env_vars[0]
monkeypatch.setenv(path_env_var, "/cluster/custom/lib")

_ = experiment_module.create_dask_client(
Expand Down
Loading