kopia lustrzana https://github.com/corrscope/corrscope
Add high-level code organization docs (#297)
rodzic
2a6aafb02f
commit
6b953572ce
|
@ -1,3 +1,8 @@
|
|||
"""
|
||||
The most important class in this module is `DumpableAttrs`.
|
||||
See its docstring for details.
|
||||
"""
|
||||
|
||||
import pickle
|
||||
import warnings
|
||||
from enum import Enum
|
||||
|
@ -113,8 +118,73 @@ def copy_config(obj: T) -> T:
|
|||
|
||||
class DumpableAttrs:
|
||||
""" Marks class as attrs, and enables YAML dumping (excludes default fields).
|
||||
- Subclass parameter `always_dump` contains
|
||||
whitespace-separated list of fields to always dump.
|
||||
|
||||
It is subclassed for
|
||||
(statically-typed key-value objects which will be dumped/loaded as YAML files).
|
||||
|
||||
## Subclassing `DumpableAttrs` and converting to YAML
|
||||
|
||||
- This class works like `dataclasses` or `attrs.dataclass` decorators,
|
||||
and wraps the latter.
|
||||
- Subclass `DumpableAttrs`, then add class-level type annotations.
|
||||
These annotations are converted into `__init__(...)` constructor parameters.
|
||||
|
||||
```py
|
||||
class Config(DumpableAttrs):
|
||||
path: str
|
||||
priority: int = 0
|
||||
```
|
||||
|
||||
Unlike many other libraries and usual JSON,
|
||||
the YAML string representation of a `DumpableAttrs` object
|
||||
encodes what Python type the object is.
|
||||
For example, Config("foo.wav", 1) is dumped as:
|
||||
|
||||
```yaml
|
||||
!Config
|
||||
path: foo.wav # Required
|
||||
priority: 1 # If not present, defaults to 0
|
||||
# See DumpableAttrs docstring for details.
|
||||
```
|
||||
|
||||
## Polymorphism
|
||||
|
||||
The YAML file determines what type is loaded,
|
||||
so the config type can be used to pick a object type at runtime.
|
||||
|
||||
- For example, putting `!CorrelationTriggerConfig` in YAML
|
||||
loads a `CorrelationTriggerConfig` config object,
|
||||
which tells corrscope to create a `CorrelationTrigger` algorithm object.
|
||||
- Putting `!NullTriggerConfig` in YAML
|
||||
instead loads a `NullTriggerConfig` config object,
|
||||
which tells corrscope to create a `NullTrigger` algorithm object.
|
||||
(This is only used for unit tests, and is incompatible with GUI.)
|
||||
|
||||
## `KeywordAttrs` are similar, with 2 differences:
|
||||
|
||||
- Subclasses can have non-default arguments after default arguments.
|
||||
- Its constructor can only be called with keyword (a=1, b=2) arguments,
|
||||
not positional (1, 2).
|
||||
|
||||
## Optional Parameters
|
||||
|
||||
class Config(DumpableAttrs, always_dump="", exclude=""): ...
|
||||
- `always_dump` contains whitespace-separated list of fields to always dump
|
||||
(if equal to default).
|
||||
- If always_dump="*", `exclude` contains whitespace-separated list of fields
|
||||
to not dump (if equal to default).
|
||||
|
||||
## Loading from YAML: Alias and Ignored
|
||||
|
||||
YAML loading uses __getstate__ and __setstate__.
|
||||
|
||||
If `old = Alias("new")`,
|
||||
then loading a YAML file `old: value` initializes `new = value`.
|
||||
|
||||
If `old = Ignored`,
|
||||
then loading a YAML file `old: value` silently discards `value`.
|
||||
Ignored is unused in my code,
|
||||
since __setstate__ automatically discards unrecognized fields (with a warning).
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
@ -1,3 +1,47 @@
|
|||
# Module/Class Structure and Organization
|
||||
|
||||
When corrscope is launched, it first executes `__main__.py` and `cli.py`.
|
||||
|
||||
## cli.py
|
||||
|
||||
If -w is present, it writes a .yaml file. If -p are present, it runs `corrscope.CorrScope` directly. If neither is present, it imports and runs the `gui` subdirectory.
|
||||
|
||||
## CorrScope, Config
|
||||
|
||||
`corrscope.py` defines classes `CorrScope`, `Config` (and `Arguments`).
|
||||
|
||||
- `CorrScope` is the main loop of the program, and only communicates with the GUI through `Arguments`. `CorrScope` requires a `Config` and `Arguments`.
|
||||
- `Arguments` is constructed by the GUI and used to update rendering progress dialogs, etc.
|
||||
- `Config` is a dataclass (see `config.py`) which can be edited through the GUI, or loaded/saved to a YAML file.
|
||||
- When `cli.py` creates new configs, `default_config()` is used as a template to supply values. When loading existing YAML files, only dataclass default values are used.
|
||||
|
||||
-----
|
||||
|
||||
`Config` holds `channels: List[ChannelConfig]`, which store all per-channel settings.
|
||||
|
||||
`CorrScope` turns `channels` into a list of `Channel` objects. Each channel uses its own `ChannelConfig` parameters to create:
|
||||
|
||||
- self.trigger_wave: Wave
|
||||
- self.render_wave: Wave
|
||||
- self.trigger: MainTrigger
|
||||
|
||||
-----
|
||||
|
||||
Each frame:
|
||||
|
||||
`CorrScope` reads data from Wave objects, asks MainTrigger
|
||||
|
||||
Each channel has its own `trigger_wave`, `render_wave`, and `trigger` instance. For each channel, `trigger_wave` is used by `trigger` to find a "trigger time" near the frame's center time.
|
||||
|
||||
The `Renderer` and `Output` instances are shared among all channels.
|
||||
|
||||
- `Renderer` turns a list of N-channel "wave file portions" (from `render_wave`) into a RGB video frame.
|
||||
- `Output` sends a RGB video frame to ffmpeg, ffplay, or (in the future) an interactive GUI player window.
|
||||
|
||||
## config.py
|
||||
|
||||
See docstring at top of file.
|
||||
|
||||
# Design Notes (cross-cutting concerns)
|
||||
|
||||
## Renderer to Output
|
||||
|
|
Ładowanie…
Reference in New Issue