phosphor 0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,66 @@
1
+ name: Documentation
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ branches:
9
+ - main
10
+ - dev
11
+ release:
12
+ types: [published]
13
+ workflow_dispatch:
14
+
15
+ permissions:
16
+ contents: read
17
+ pages: write
18
+ id-token: write
19
+
20
+ # Allow only one concurrent deployment
21
+ concurrency:
22
+ group: "pages"
23
+ cancel-in-progress: false
24
+
25
+ jobs:
26
+ build:
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ with:
31
+ fetch-depth: 0 # Needed for hatch-vcs to determine version
32
+
33
+ - name: Install uv
34
+ uses: astral-sh/setup-uv@v6
35
+ with:
36
+ enable-cache: true
37
+ python-version: "3.12"
38
+
39
+ - name: Install the project
40
+ run: uv sync --only-group docs
41
+
42
+ - name: Build documentation
43
+ run: |
44
+ cd docs
45
+ uv run make html
46
+
47
+ - name: Add .nojekyll file
48
+ run: touch docs/build/html/.nojekyll
49
+
50
+ - name: Upload artifact
51
+ uses: actions/upload-pages-artifact@v3
52
+ with:
53
+ path: 'docs/build/html'
54
+
55
+ deploy:
56
+ # Only deploy when a release is published
57
+ if: github.event_name == 'release'
58
+ environment:
59
+ name: github-pages
60
+ url: ${{ steps.deployment.outputs.page_url }}
61
+ runs-on: ubuntu-latest
62
+ needs: build
63
+ steps:
64
+ - name: Deploy to GitHub Pages
65
+ id: deployment
66
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,28 @@
1
+ # This workflow will upload a Python Package using Twine when a release is created
2
+
3
+ name: Upload Python Package
4
+
5
+ on:
6
+ release:
7
+ types: [published]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ build:
12
+ name: build and upload release to PyPI
13
+ runs-on: ubuntu-latest
14
+ environment: "release"
15
+ permissions:
16
+ id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v6
23
+
24
+ - name: Build Package
25
+ run: uv build
26
+
27
+ - name: Publish package distributions to PyPI
28
+ run: uv publish
@@ -0,0 +1,36 @@
1
+ name: Test package
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches:
8
+ - main
9
+ - dev
10
+ workflow_dispatch:
11
+
12
+ jobs:
13
+ build:
14
+ strategy:
15
+ matrix:
16
+ python-version: ["3.11", "3.12", "3.13"]
17
+ os:
18
+ - "ubuntu-latest"
19
+ - "windows-latest"
20
+ - "macos-latest"
21
+ runs-on: ${{matrix.os}}
22
+
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+
26
+ - name: Install the latest version of uv
27
+ uses: astral-sh/setup-uv@v6
28
+
29
+ - name: Install the project
30
+ run: uv sync --python ${{ matrix.python-version }}
31
+
32
+ - name: Lint
33
+ run: uv tool run ruff check --output-format=github src
34
+
35
+ # - name: Run tests
36
+ # run: uv run pytest tests
@@ -0,0 +1,16 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ uv.lock
8
+ *.local.json
9
+ src/phosphor/__version__.py
10
+
11
+ # Sphinx documentation
12
+ docs/_build/
13
+ docs/build/
14
+ docs/source/_build
15
+ docs/source/generated
16
+ docs/source/api/generated
@@ -0,0 +1,9 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.8.3
4
+ hooks:
5
+ # Run the linter.
6
+ - id: ruff
7
+ args: [ --fix ]
8
+ # Run the formatter.
9
+ - id: ruff-format
phosphor-0.1/PKG-INFO ADDED
@@ -0,0 +1,107 @@
1
+ Metadata-Version: 2.4
2
+ Name: phosphor
3
+ Version: 0.1
4
+ Summary: WebGPU accelerated visualization of unbounded neural data streams in Python
5
+ Author-email: Chadwick Boulay <chadwick.boulay@gmail.com>
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: numpy>=2.4.2
8
+ Requires-Dist: pyside6>=6.10.2
9
+ Requires-Dist: rendercanvas>=2.6.1
10
+ Requires-Dist: wgpu>=0.29.0
11
+ Description-Content-Type: text/markdown
12
+
13
+ # phosphor
14
+
15
+ GPU-accelerated real-time sweep renderer for multichannel timeseries data. Built on [WebGPU](https://github.com/pygfx/wgpu-py) and Qt, phosphor renders thousands of channels at high sample rates with minimal CPU overhead.
16
+
17
+ Designed for neuroscience and real-time signal monitoring -- push `(n_samples, n_channels)` numpy arrays and phosphor handles downsampling, autoscaling, and rendering.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ pip install phosphor
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```python
28
+ import numpy as np
29
+ from PySide6.QtWidgets import QApplication
30
+ from phosphor import SweepConfig, SweepWidget
31
+
32
+ app = QApplication([])
33
+
34
+ widget = SweepWidget(SweepConfig(
35
+ n_channels=128,
36
+ srate=30000.0,
37
+ display_dur=2.0,
38
+ n_visible=64,
39
+ ))
40
+ widget.show()
41
+
42
+ # Push data from any source -- shape: (n_samples, n_channels), float32
43
+ widget.push_data(np.random.randn(500, 128).astype(np.float32))
44
+
45
+ app.exec()
46
+ ```
47
+
48
+ ### Embedding in an Existing Qt Application
49
+
50
+ `SweepWidget` is a standard `QWidget` that can be added to any layout:
51
+
52
+ ```python
53
+ from PySide6.QtWidgets import QMainWindow, QVBoxLayout, QWidget
54
+ from phosphor import SweepConfig, SweepWidget
55
+
56
+ class MyWindow(QMainWindow):
57
+ def __init__(self):
58
+ super().__init__()
59
+ self.sweep = SweepWidget(SweepConfig(n_channels=64, srate=1000.0))
60
+ self.setCentralWidget(self.sweep)
61
+
62
+ def on_new_data(self, data):
63
+ self.sweep.push_data(data)
64
+ ```
65
+
66
+ ### Runtime Configuration
67
+
68
+ Update parameters without recreating the widget:
69
+
70
+ ```python
71
+ from phosphor import SweepConfig
72
+
73
+ widget.update_config(SweepConfig(
74
+ n_channels=256,
75
+ srate=30000.0,
76
+ display_dur=4.0,
77
+ n_visible=128,
78
+ ))
79
+ ```
80
+
81
+ ## Built-in Demo
82
+
83
+ ```bash
84
+ python -m phosphor
85
+ python -m phosphor --channels 256 --srate 30000 --visible 64 --dur 2.0
86
+ ```
87
+
88
+ ## Keyboard Controls
89
+
90
+ | Key | Action |
91
+ |-------------------------|-------------------------------------------|
92
+ | `Up` / `Down` | Scroll channels by 1 |
93
+ | `Page Up` / `Page Down` | Scroll channels by one page |
94
+ | `[` / `]` | Halve / double visible channel count |
95
+ | `-` / `=` | Y-axis zoom out / in (disables autoscale) |
96
+ | `A` | Toggle autoscale |
97
+ | `,` / `.` | Halve / double display duration |
98
+
99
+ ## Development
100
+
101
+ We use [`uv`](https://docs.astral.sh/uv/) for development.
102
+
103
+ 1. Fork and clone the repository
104
+ 2. `uv sync` to create a virtual environment and install dependencies
105
+ 3. `uv run pre-commit install` to set up linting and formatting hooks
106
+ 4. `uv run pytest tests` to run the test suite
107
+ 5. Submit a PR against the `dev` branch
phosphor-0.1/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # phosphor
2
+
3
+ GPU-accelerated real-time sweep renderer for multichannel timeseries data. Built on [WebGPU](https://github.com/pygfx/wgpu-py) and Qt, phosphor renders thousands of channels at high sample rates with minimal CPU overhead.
4
+
5
+ Designed for neuroscience and real-time signal monitoring -- push `(n_samples, n_channels)` numpy arrays and phosphor handles downsampling, autoscaling, and rendering.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install phosphor
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```python
16
+ import numpy as np
17
+ from PySide6.QtWidgets import QApplication
18
+ from phosphor import SweepConfig, SweepWidget
19
+
20
+ app = QApplication([])
21
+
22
+ widget = SweepWidget(SweepConfig(
23
+ n_channels=128,
24
+ srate=30000.0,
25
+ display_dur=2.0,
26
+ n_visible=64,
27
+ ))
28
+ widget.show()
29
+
30
+ # Push data from any source -- shape: (n_samples, n_channels), float32
31
+ widget.push_data(np.random.randn(500, 128).astype(np.float32))
32
+
33
+ app.exec()
34
+ ```
35
+
36
+ ### Embedding in an Existing Qt Application
37
+
38
+ `SweepWidget` is a standard `QWidget` that can be added to any layout:
39
+
40
+ ```python
41
+ from PySide6.QtWidgets import QMainWindow, QVBoxLayout, QWidget
42
+ from phosphor import SweepConfig, SweepWidget
43
+
44
+ class MyWindow(QMainWindow):
45
+ def __init__(self):
46
+ super().__init__()
47
+ self.sweep = SweepWidget(SweepConfig(n_channels=64, srate=1000.0))
48
+ self.setCentralWidget(self.sweep)
49
+
50
+ def on_new_data(self, data):
51
+ self.sweep.push_data(data)
52
+ ```
53
+
54
+ ### Runtime Configuration
55
+
56
+ Update parameters without recreating the widget:
57
+
58
+ ```python
59
+ from phosphor import SweepConfig
60
+
61
+ widget.update_config(SweepConfig(
62
+ n_channels=256,
63
+ srate=30000.0,
64
+ display_dur=4.0,
65
+ n_visible=128,
66
+ ))
67
+ ```
68
+
69
+ ## Built-in Demo
70
+
71
+ ```bash
72
+ python -m phosphor
73
+ python -m phosphor --channels 256 --srate 30000 --visible 64 --dur 2.0
74
+ ```
75
+
76
+ ## Keyboard Controls
77
+
78
+ | Key | Action |
79
+ |-------------------------|-------------------------------------------|
80
+ | `Up` / `Down` | Scroll channels by 1 |
81
+ | `Page Up` / `Page Down` | Scroll channels by one page |
82
+ | `[` / `]` | Halve / double visible channel count |
83
+ | `-` / `=` | Y-axis zoom out / in (disables autoscale) |
84
+ | `A` | Toggle autoscale |
85
+ | `,` / `.` | Halve / double display duration |
86
+
87
+ ## Development
88
+
89
+ We use [`uv`](https://docs.astral.sh/uv/) for development.
90
+
91
+ 1. Fork and clone the repository
92
+ 2. `uv sync` to create a virtual environment and install dependencies
93
+ 3. `uv run pre-commit install` to set up linting and formatting hooks
94
+ 4. `uv run pytest tests` to run the test suite
95
+ 5. Submit a PR against the `dev` branch
@@ -0,0 +1,20 @@
1
+ # Minimal makefile for Sphinx documentation
2
+ #
3
+
4
+ # You can set these variables from the command line, and also
5
+ # from the environment for the first two.
6
+ SPHINXOPTS ?=
7
+ SPHINXBUILD ?= sphinx-build
8
+ SOURCEDIR = source
9
+ BUILDDIR = build
10
+
11
+ # Put it first so that "make" without argument is like "make help".
12
+ help:
13
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14
+
15
+ .PHONY: help Makefile
16
+
17
+ # Catch-all target: route all unknown targets to Sphinx using the new
18
+ # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19
+ %: Makefile
20
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@@ -0,0 +1,35 @@
1
+ @ECHO OFF
2
+
3
+ pushd %~dp0
4
+
5
+ REM Command file for Sphinx documentation
6
+
7
+ if "%SPHINXBUILD%" == "" (
8
+ set SPHINXBUILD=sphinx-build
9
+ )
10
+ set SOURCEDIR=source
11
+ set BUILDDIR=build
12
+
13
+ if "%1" == "" goto help
14
+
15
+ %SPHINXBUILD% >NUL 2>NUL
16
+ if errorlevel 9009 (
17
+ echo.
18
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19
+ echo.installed, then set the SPHINXBUILD environment variable to point
20
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
21
+ echo.may add the Sphinx directory to PATH.
22
+ echo.
23
+ echo.If you don't have Sphinx installed, grab it from
24
+ echo.http://sphinx-doc.org/
25
+ exit /b 1
26
+ )
27
+
28
+ %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29
+ goto end
30
+
31
+ :help
32
+ %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33
+
34
+ :end
35
+ popd
@@ -0,0 +1,65 @@
1
+ {{ fullname | escape | underline}}
2
+
3
+ .. automodule:: {{ fullname }}
4
+ :no-members:
5
+
6
+ {% block attributes %}
7
+ {% if attributes %}
8
+ .. rubric:: Module Attributes
9
+
10
+ .. autosummary::
11
+ :toctree:
12
+ {% for item in attributes %}
13
+ {{ item }}
14
+ {%- endfor %}
15
+ {% endif %}
16
+ {% endblock %}
17
+
18
+ {% block functions %}
19
+ {% if functions %}
20
+ .. rubric:: Functions
21
+
22
+ {% for item in functions %}
23
+ .. autofunction:: {{ item }}
24
+ {%- endfor %}
25
+ {% endif %}
26
+ {% endblock %}
27
+
28
+ {% block classes %}
29
+ {% if classes %}
30
+ .. rubric:: Classes
31
+
32
+ {% for item in classes %}
33
+ .. autoclass:: {{ item }}
34
+ :members:
35
+ :undoc-members:
36
+ :show-inheritance:
37
+ :special-members: __init__
38
+ {%- endfor %}
39
+ {% endif %}
40
+ {% endblock %}
41
+
42
+ {% block exceptions %}
43
+ {% if exceptions %}
44
+ .. rubric:: Exceptions
45
+
46
+ {% for item in exceptions %}
47
+ .. autoexception:: {{ item }}
48
+ :members:
49
+ :show-inheritance:
50
+ {%- endfor %}
51
+ {% endif %}
52
+ {% endblock %}
53
+
54
+ {% block modules %}
55
+ {% if modules %}
56
+ .. rubric:: Modules
57
+
58
+ .. autosummary::
59
+ :toctree:
60
+ :recursive:
61
+ {% for item in modules %}
62
+ {{ item }}
63
+ {%- endfor %}
64
+ {% endif %}
65
+ {% endblock %}
@@ -0,0 +1,11 @@
1
+ API Reference
2
+ =============
3
+
4
+ This page contains auto-generated API reference documentation.
5
+
6
+ .. autosummary::
7
+ :toctree: generated
8
+ :recursive:
9
+ :template: autosummary/module.rst
10
+
11
+ phosphor
@@ -0,0 +1,129 @@
1
+ # Configuration file for the Sphinx documentation builder.
2
+
3
+ import os
4
+ import sys
5
+
6
+ # Add the source directory to the path
7
+ sys.path.insert(0, os.path.abspath("../../src"))
8
+
9
+ # -- Project information --------------------------
10
+
11
+ project = "ezmsg.sigproc"
12
+ copyright = "2024, ezmsg Contributors"
13
+ author = "ezmsg Contributors"
14
+
15
+ # The version is managed by hatch-vcs and stored in __version__.py
16
+ try:
17
+ from ezmsg.sigproc.__version__ import version as release
18
+ except ImportError:
19
+ release = "unknown"
20
+
21
+ # For display purposes, extract the base version without git commit info
22
+ version = release.split("+")[0] if release != "unknown" else release
23
+
24
+ # -- General configuration --------------------------
25
+
26
+ extensions = [
27
+ "sphinx.ext.autodoc",
28
+ "sphinx.ext.autosummary",
29
+ "sphinx.ext.napoleon",
30
+ "sphinx.ext.intersphinx",
31
+ "sphinx.ext.viewcode",
32
+ "sphinx.ext.duration",
33
+ "sphinx.ext.graphviz",
34
+ # "sphinx_autodoc_typehints", # Disabled due to compatibility issue
35
+ "sphinx_copybutton",
36
+ "myst_parser", # For markdown files
37
+ ]
38
+
39
+ templates_path = ["_templates"]
40
+ source_suffix = {
41
+ ".rst": "restructuredtext",
42
+ ".md": "markdown",
43
+ }
44
+ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
45
+
46
+ # The toctree master document
47
+ master_doc = "index"
48
+
49
+ # -- Autodoc configuration ------------------------------
50
+
51
+ # Auto-generate API docs
52
+ autosummary_generate = True
53
+ autosummary_imported_members = False
54
+ autodoc_typehints = "description"
55
+ autodoc_member_order = "bysource"
56
+ autodoc_typehints_format = "short"
57
+ python_use_unqualified_type_names = True
58
+ autodoc_default_options = {
59
+ "members": True,
60
+ "member-order": "bysource",
61
+ "special-members": "__init__",
62
+ "undoc-members": True,
63
+ "show-inheritance": True,
64
+ }
65
+
66
+ # Don't show the full module path in the docs
67
+ add_module_names = False
68
+
69
+ # -- Intersphinx configuration --------------------------
70
+
71
+ intersphinx_mapping = {
72
+ "python": ("https://docs.python.org/3/", None),
73
+ "numpy": ("https://numpy.org/doc/stable/", None),
74
+ "scipy": ("https://docs.scipy.org/doc/scipy/", None),
75
+ "ezmsg": ("https://www.ezmsg.org/ezmsg/", None),
76
+ "ezmsg.baseproc": ("https://www.ezmsg.org/ezmsg-baseproc/", None),
77
+ "ezmsg.learn": ("https://www.ezmsg.org/ezmsg-learn/", None),
78
+ "ezmsg.event": ("https://www.ezmsg.org/ezmsg-event/", None),
79
+ "ezmsg.lsl": ("https://www.ezmsg.org/ezmsg-lsl/", None),
80
+ "ezmsg.blackrock": ("https://www.ezmsg.org/ezmsg-blackrock/", None),
81
+ }
82
+ intersphinx_disabled_domains = ["std"]
83
+
84
+ # -- Options for HTML output -----------------------------
85
+
86
+ html_theme = "pydata_sphinx_theme"
87
+ html_static_path = ["_static"]
88
+
89
+ # Set the base URL for the documentation
90
+ html_baseurl = "https://www.ezmsg.org/ezmsg-sigproc/"
91
+
92
+ html_theme_options = {
93
+ "logo": {
94
+ "text": f"ezmsg.sigproc {version}",
95
+ "link": "https://ezmsg.org", # Link back to main site
96
+ },
97
+ "header_links_before_dropdown": 4,
98
+ "navbar_start": ["navbar-logo"],
99
+ "navbar_end": ["theme-switcher", "navbar-icon-links"],
100
+ "icon_links": [
101
+ {
102
+ "name": "GitHub",
103
+ "url": "https://github.com/ezmsg-org/ezmsg-sigproc",
104
+ "icon": "fa-brands fa-github",
105
+ },
106
+ {
107
+ "name": "ezmsg.org",
108
+ "url": "https://www.ezmsg.org",
109
+ "icon": "fa-solid fa-house",
110
+ },
111
+ ],
112
+ }
113
+
114
+ # Timestamp is inserted at every page bottom in this strftime format.
115
+ html_last_updated_fmt = "%Y-%m-%d"
116
+
117
+ # -- Options for linkcode -----------------------------
118
+
119
+ branch = "main"
120
+ code_url = f"https://github.com/ezmsg-org/ezmsg-sigproc/blob/{branch}/"
121
+
122
+
123
+ def linkcode_resolve(domain, info):
124
+ if domain != "py":
125
+ return None
126
+ if not info["module"]:
127
+ return None
128
+ filename = info["module"].replace(".", "/")
129
+ return f"{code_url}src/{filename}.py"
@@ -0,0 +1,14 @@
1
+ ```{include} ../../README.md
2
+ ```
3
+
4
+ ```{toctree}
5
+ :maxdepth: 2
6
+ :caption: Contents:
7
+
8
+ api/index
9
+ ```
10
+
11
+ ## Indices and tables
12
+
13
+ - {ref}`genindex`
14
+ - {ref}`modindex`