2
0
forked from awendt/pyrelacs

39 Commits

Author SHA1 Message Date
5a7843e866 [project] make it a package 2024-09-27 17:13:06 +02:00
a569014cba [app] move PyRelacs main window to ui subpackage 2024-09-27 17:12:20 +02:00
7347278c8f [gitignore] ignore nix files 2024-09-27 17:10:55 +02:00
2ede519b95 [app] remove main window size limit 2024-09-27 16:48:49 +02:00
wendtalexander
a16fe0b735 [project] adding dev dependencies 2024-09-27 16:24:05 +02:00
wendtalexander
5c3c2c407a [project] updating project structure 2024-09-27 14:21:13 +02:00
wendtalexander
8ef8ac7506 [app] adding pyqtplot for displaying the beat 2024-09-27 14:20:43 +02:00
wendtalexander
7d3224f351 [app][repros] adding nix file and checks 2024-09-27 14:20:18 +02:00
wendtalexander
a748385335 [app] [calibration] adding writing to nix file 2024-09-27 14:19:46 +02:00
wendtalexander
cd6bc0dc04 [app] starting repos with as a thread 2024-09-27 10:21:05 +02:00
wendtalexander
cb5c08bc94 [doc] renaming spec sheet 2024-09-27 09:30:46 +02:00
45a267c8c7 Merge pull request 'main' (#1) from jgrewe/minipyrelacs:main into main
Reviewed-on: Awendt/pyrelacs#1
2024-09-27 07:29:03 +00:00
291ed8859c Merge branch 'main' of https://whale.am28.uni-tuebingen.de/git/Awendt/pyrelacs 2024-09-27 09:17:34 +02:00
aa792bcb1d [docs] add attenuator specification 2024-09-27 09:14:44 +02:00
6a73f38fba [app] store window settings and restore upon startup 2024-09-27 09:13:29 +02:00
6008cc03d6 [project] add info.py that reads the project toml file to set application information 2024-09-27 09:12:56 +02:00
938d70fbac [project] add some more information 2024-09-27 09:12:15 +02:00
wendtalexander
dd3e0d045d rewriting the project structure 2024-09-27 09:12:11 +02:00
8d616847f5 [setup] add gui script entry point 2024-09-26 16:30:40 +02:00
836d6dc3d9 [app] restructure to have a valid entry point 2024-09-26 16:30:14 +02:00
wendtalexander
43e0d4b75a adding calbi for different db 2024-09-26 14:29:47 +02:00
wendtalexander
66ea22fb4a checking amplitude, checking beat 2024-09-26 11:25:13 +02:00
wendtalexander
d3800ddfa2 handle initial connection, diggital trigger 2024-09-26 11:24:03 +02:00
wendtalexander
1dc72d00bb adding scipy 2024-09-26 11:23:08 +02:00
wendtalexander
26f43151a2 fixing seg fault by returning data_analog_output 2024-09-25 17:05:21 +02:00
wendtalexander
a9be09dc06 checking if amplitude is the same form input to output 2024-09-25 17:04:53 +02:00
wendtalexander
06f5a6ae46 del file 2024-09-25 17:04:29 +02:00
wendtalexander
b912159b76 Merge branch 'main' of https://whale.am28.uni-tuebingen.de/git/Awendt/pyrelacs 2024-09-25 16:08:58 +02:00
wendtalexander
9cd6aadb3b trying to find changes that lead to the segfault 2024-09-25 16:05:50 +02:00
wendtalexander
1a2185d5e4 trying to fix seg fault 2024-09-25 15:31:15 +02:00
wendtalexander
deb60fa84c updating output 2024-09-25 13:37:40 +02:00
wendtalexander
1579c947c9 changing Repos to mccdac 2024-09-25 07:57:32 +02:00
wendtalexander
d6e2f8c5ba changing Repos to MccDac 2024-09-25 07:57:07 +02:00
wendtalexander
8e73b2ae1f adding to mccdac 2024-09-25 07:56:46 +02:00
wendtalexander
9f7d28ccf8 adding assertions, digital trigger 2024-09-24 17:34:34 +02:00
wendtalexander
3865bb8216 adding try execpt 2024-09-24 17:34:17 +02:00
wendtalexander
2317fd73c8 adding attenuator class 2024-09-24 17:33:59 +02:00
wendtalexander
fbb4d3b81d bug cant read digio line with d_in_scan 2024-09-23 17:21:52 +02:00
wendtalexander
7d02cb994f adding unlimited time 2024-09-23 14:32:49 +02:00
21 changed files with 1341 additions and 517 deletions

2
.gitignore vendored
View File

@@ -161,3 +161,5 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/
# ignore created data files
*.nix

BIN
docs/AttCS3310.pdf Normal file

Binary file not shown.

452
poetry.lock generated
View File

@@ -1,4 +1,22 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. # This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
[[package]]
name = "asttokens"
version = "2.4.1"
description = "Annotate AST trees with source code positions"
optional = false
python-versions = "*"
files = [
{file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"},
{file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"},
]
[package.dependencies]
six = ">=1.12.0"
[package.extras]
astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"]
test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"]
[[package]] [[package]]
name = "click" name = "click"
@@ -124,6 +142,31 @@ files = [
docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] docs = ["ipython", "matplotlib", "numpydoc", "sphinx"]
tests = ["pytest", "pytest-cov", "pytest-xdist"] tests = ["pytest", "pytest-cov", "pytest-xdist"]
[[package]]
name = "decorator"
version = "5.1.1"
description = "Decorators for Humans"
optional = false
python-versions = ">=3.5"
files = [
{file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
]
[[package]]
name = "executing"
version = "2.1.0"
description = "Get the currently executing AST node of a frame, and other information"
optional = false
python-versions = ">=3.8"
files = [
{file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"},
{file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"},
]
[package.extras]
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
[[package]] [[package]]
name = "fonttools" name = "fonttools"
version = "4.53.1" version = "4.53.1"
@@ -189,6 +232,99 @@ ufo = ["fs (>=2.2.0,<3)"]
unicode = ["unicodedata2 (>=15.1.0)"] unicode = ["unicodedata2 (>=15.1.0)"]
woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
[[package]]
name = "h5py"
version = "3.12.1"
description = "Read and write HDF5 files from Python"
optional = false
python-versions = ">=3.9"
files = [
{file = "h5py-3.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f0f1a382cbf494679c07b4371f90c70391dedb027d517ac94fa2c05299dacda"},
{file = "h5py-3.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb65f619dfbdd15e662423e8d257780f9a66677eae5b4b3fc9dca70b5fd2d2a3"},
{file = "h5py-3.12.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b15d8dbd912c97541312c0e07438864d27dbca857c5ad634de68110c6beb1c2"},
{file = "h5py-3.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59685fe40d8c1fbbee088c88cd4da415a2f8bee5c270337dc5a1c4aa634e3307"},
{file = "h5py-3.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:577d618d6b6dea3da07d13cc903ef9634cde5596b13e832476dd861aaf651f3e"},
{file = "h5py-3.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ccd9006d92232727d23f784795191bfd02294a4f2ba68708825cb1da39511a93"},
{file = "h5py-3.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ad8a76557880aed5234cfe7279805f4ab5ce16b17954606cca90d578d3e713ef"},
{file = "h5py-3.12.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1473348139b885393125126258ae2d70753ef7e9cec8e7848434f385ae72069e"},
{file = "h5py-3.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:018a4597f35092ae3fb28ee851fdc756d2b88c96336b8480e124ce1ac6fb9166"},
{file = "h5py-3.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:3fdf95092d60e8130ba6ae0ef7a9bd4ade8edbe3569c13ebbaf39baefffc5ba4"},
{file = "h5py-3.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06a903a4e4e9e3ebbc8b548959c3c2552ca2d70dac14fcfa650d9261c66939ed"},
{file = "h5py-3.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7b3b8f3b48717e46c6a790e3128d39c61ab595ae0a7237f06dfad6a3b51d5351"},
{file = "h5py-3.12.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:050a4f2c9126054515169c49cb900949814987f0c7ae74c341b0c9f9b5056834"},
{file = "h5py-3.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c4b41d1019322a5afc5082864dfd6359f8935ecd37c11ac0029be78c5d112c9"},
{file = "h5py-3.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:e4d51919110a030913201422fb07987db4338eba5ec8c5a15d6fab8e03d443fc"},
{file = "h5py-3.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:513171e90ed92236fc2ca363ce7a2fc6f2827375efcbb0cc7fbdd7fe11fecafc"},
{file = "h5py-3.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:59400f88343b79655a242068a9c900001a34b63e3afb040bd7cdf717e440f653"},
{file = "h5py-3.12.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3e465aee0ec353949f0f46bf6c6f9790a2006af896cee7c178a8c3e5090aa32"},
{file = "h5py-3.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba51c0c5e029bb5420a343586ff79d56e7455d496d18a30309616fdbeed1068f"},
{file = "h5py-3.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:52ab036c6c97055b85b2a242cb540ff9590bacfda0c03dd0cf0661b311f522f8"},
{file = "h5py-3.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d2b8dd64f127d8b324f5d2cd1c0fd6f68af69084e9e47d27efeb9e28e685af3e"},
{file = "h5py-3.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4532c7e97fbef3d029735db8b6f5bf01222d9ece41e309b20d63cfaae2fb5c4d"},
{file = "h5py-3.12.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdf6d7936fa824acfa27305fe2d9f39968e539d831c5bae0e0d83ed521ad1ac"},
{file = "h5py-3.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84342bffd1f82d4f036433e7039e241a243531a1d3acd7341b35ae58cdab05bf"},
{file = "h5py-3.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:62be1fc0ef195891949b2c627ec06bc8e837ff62d5b911b6e42e38e0f20a897d"},
{file = "h5py-3.12.1.tar.gz", hash = "sha256:326d70b53d31baa61f00b8aa5f95c2fcb9621a3ee8365d770c551a13dbbcbfdf"},
]
[package.dependencies]
numpy = ">=1.19.3"
[[package]]
name = "ipython"
version = "8.27.0"
description = "IPython: Productive Interactive Computing"
optional = false
python-versions = ">=3.10"
files = [
{file = "ipython-8.27.0-py3-none-any.whl", hash = "sha256:f68b3cb8bde357a5d7adc9598d57e22a45dfbea19eb6b98286fa3b288c9cd55c"},
{file = "ipython-8.27.0.tar.gz", hash = "sha256:0b99a2dc9f15fd68692e898e5568725c6d49c527d36a9fb5960ffbdeaa82ff7e"},
]
[package.dependencies]
colorama = {version = "*", markers = "sys_platform == \"win32\""}
decorator = "*"
jedi = ">=0.16"
matplotlib-inline = "*"
pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
prompt-toolkit = ">=3.0.41,<3.1.0"
pygments = ">=2.4.0"
stack-data = "*"
traitlets = ">=5.13.0"
[package.extras]
all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
black = ["black"]
doc = ["docrepr", "exceptiongroup", "intersphinx-registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing-extensions"]
kernel = ["ipykernel"]
matplotlib = ["matplotlib"]
nbconvert = ["nbconvert"]
nbformat = ["nbformat"]
notebook = ["ipywidgets", "notebook"]
parallel = ["ipyparallel"]
qtconsole = ["qtconsole"]
test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"]
test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
[[package]]
name = "jedi"
version = "0.19.1"
description = "An autocompletion tool for Python that can be used for text editors."
optional = false
python-versions = ">=3.6"
files = [
{file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"},
{file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"},
]
[package.dependencies]
parso = ">=0.8.3,<0.9.0"
[package.extras]
docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
[[package]] [[package]]
name = "kiwisolver" name = "kiwisolver"
version = "1.4.7" version = "1.4.7"
@@ -399,6 +535,20 @@ python-dateutil = ">=2.7"
[package.extras] [package.extras]
dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"]
[[package]]
name = "matplotlib-inline"
version = "0.1.7"
description = "Inline Matplotlib backend for Jupyter"
optional = false
python-versions = ">=3.8"
files = [
{file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
{file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
]
[package.dependencies]
traitlets = "*"
[[package]] [[package]]
name = "mdurl" name = "mdurl"
version = "0.1.2" version = "0.1.2"
@@ -410,66 +560,65 @@ files = [
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
] ]
[[package]]
name = "nixio"
version = "1.5.3"
description = "Python reimplementation of NIXIO (http://g-node.github.io/nix/)"
optional = false
python-versions = ">=3.6"
files = [
{file = "nixio-1.5.3-py3-none-any.whl", hash = "sha256:5bc6258b0911738070f7008237d4cef348a994cce7f557adf624452f0f4287cb"},
{file = "nixio-1.5.3.tar.gz", hash = "sha256:0ba7a65148297bd43a5ddaf143c4faad1c999771aa1d7e690aa0a5e31f368608"},
]
[package.dependencies]
h5py = "*"
numpy = "*"
six = "*"
[[package]] [[package]]
name = "numpy" name = "numpy"
version = "2.1.1" version = "1.26.4"
description = "Fundamental package for array computing in Python" description = "Fundamental package for array computing in Python"
optional = false optional = false
python-versions = ">=3.10" python-versions = ">=3.9"
files = [ files = [
{file = "numpy-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8a0e34993b510fc19b9a2ce7f31cb8e94ecf6e924a40c0c9dd4f62d0aac47d9"}, {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
{file = "numpy-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7dd86dfaf7c900c0bbdcb8b16e2f6ddf1eb1fe39c6c8cca6e94844ed3152a8fd"}, {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"},
{file = "numpy-2.1.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:5889dd24f03ca5a5b1e8a90a33b5a0846d8977565e4ae003a63d22ecddf6782f"}, {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"},
{file = "numpy-2.1.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:59ca673ad11d4b84ceb385290ed0ebe60266e356641428c845b39cd9df6713ab"}, {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"},
{file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ce49a34c44b6de5241f0b38b07e44c1b2dcacd9e36c30f9c2fcb1bb5135db7"}, {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"},
{file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913cc1d311060b1d409e609947fa1b9753701dac96e6581b58afc36b7ee35af6"}, {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"},
{file = "numpy-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:caf5d284ddea7462c32b8d4a6b8af030b6c9fd5332afb70e7414d7fdded4bfd0"}, {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"},
{file = "numpy-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:57eb525e7c2a8fdee02d731f647146ff54ea8c973364f3b850069ffb42799647"}, {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"},
{file = "numpy-2.1.1-cp310-cp310-win32.whl", hash = "sha256:9a8e06c7a980869ea67bbf551283bbed2856915f0a792dc32dd0f9dd2fb56728"}, {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"},
{file = "numpy-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:d10c39947a2d351d6d466b4ae83dad4c37cd6c3cdd6d5d0fa797da56f710a6ae"}, {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"},
{file = "numpy-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550"}, {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"},
{file = "numpy-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f"}, {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"},
{file = "numpy-2.1.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0"}, {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"},
{file = "numpy-2.1.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95"}, {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"},
{file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca"}, {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"},
{file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf"}, {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"},
{file = "numpy-2.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e"}, {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"},
{file = "numpy-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2"}, {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"},
{file = "numpy-2.1.1-cp311-cp311-win32.whl", hash = "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d"}, {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"},
{file = "numpy-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e"}, {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"},
{file = "numpy-2.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e"}, {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"},
{file = "numpy-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe"}, {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"},
{file = "numpy-2.1.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f"}, {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"},
{file = "numpy-2.1.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521"}, {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"},
{file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b"}, {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"},
{file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201"}, {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"},
{file = "numpy-2.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a"}, {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"},
{file = "numpy-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313"}, {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"},
{file = "numpy-2.1.1-cp312-cp312-win32.whl", hash = "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed"}, {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"},
{file = "numpy-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270"}, {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"},
{file = "numpy-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5"}, {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"},
{file = "numpy-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5"}, {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"},
{file = "numpy-2.1.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136"}, {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"},
{file = "numpy-2.1.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0"}, {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"},
{file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb"}, {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"},
{file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df"}, {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"},
{file = "numpy-2.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78"},
{file = "numpy-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556"},
{file = "numpy-2.1.1-cp313-cp313-win32.whl", hash = "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b"},
{file = "numpy-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0"},
{file = "numpy-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553"},
{file = "numpy-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480"},
{file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f"},
{file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468"},
{file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef"},
{file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f"},
{file = "numpy-2.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c"},
{file = "numpy-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec"},
{file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7be6a07520b88214ea85d8ac8b7d6d8a1839b0b5cb87412ac9f49fa934eb15d5"},
{file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:52ac2e48f5ad847cd43c4755520a2317f3380213493b9d8a4c5e37f3b87df504"},
{file = "numpy-2.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a95ca3560a6058d6ea91d4629a83a897ee27c00630aed9d933dff191f170cd"},
{file = "numpy-2.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:99f4a9ee60eed1385a86e82288971a51e71df052ed0b2900ed30bc840c0f2e39"},
{file = "numpy-2.1.1.tar.gz", hash = "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd"},
] ]
[[package]] [[package]]
@@ -483,6 +632,35 @@ files = [
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
] ]
[[package]]
name = "parso"
version = "0.8.4"
description = "A Python Parser"
optional = false
python-versions = ">=3.6"
files = [
{file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"},
{file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"},
]
[package.extras]
qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
testing = ["docopt", "pytest"]
[[package]]
name = "pexpect"
version = "4.9.0"
description = "Pexpect allows easy control of interactive console applications."
optional = false
python-versions = "*"
files = [
{file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
{file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
]
[package.dependencies]
ptyprocess = ">=0.5"
[[package]] [[package]]
name = "pillow" name = "pillow"
version = "10.4.0" version = "10.4.0"
@@ -580,6 +758,45 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
typing = ["typing-extensions"] typing = ["typing-extensions"]
xmp = ["defusedxml"] xmp = ["defusedxml"]
[[package]]
name = "prompt-toolkit"
version = "3.0.48"
description = "Library for building powerful interactive command lines in Python"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"},
{file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"},
]
[package.dependencies]
wcwidth = "*"
[[package]]
name = "ptyprocess"
version = "0.7.0"
description = "Run a subprocess in a pseudo terminal"
optional = false
python-versions = "*"
files = [
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
]
[[package]]
name = "pure-eval"
version = "0.2.3"
description = "Safely evaluate AST nodes without side effects"
optional = false
python-versions = "*"
files = [
{file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
{file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
]
[package.extras]
tests = ["pytest"]
[[package]] [[package]]
name = "pygments" name = "pygments"
version = "2.18.0" version = "2.18.0"
@@ -672,6 +889,20 @@ files = [
{file = "PyQt6_sip-13.8.0.tar.gz", hash = "sha256:2f74cf3d6d9cab5152bd9f49d570b2dfb87553ebb5c4919abfde27f5b9fd69d4"}, {file = "PyQt6_sip-13.8.0.tar.gz", hash = "sha256:2f74cf3d6d9cab5152bd9f49d570b2dfb87553ebb5c4919abfde27f5b9fd69d4"},
] ]
[[package]]
name = "pyqtgraph"
version = "0.13.7"
description = "Scientific Graphics and GUI Library for Python"
optional = false
python-versions = ">=3.9"
files = [
{file = "pyqtgraph-0.13.7-py3-none-any.whl", hash = "sha256:7754edbefb6c367fa0dfb176e2d0610da3ada20aa7a5318516c74af5fb72bf7a"},
{file = "pyqtgraph-0.13.7.tar.gz", hash = "sha256:64f84f1935c6996d0e09b1ee66fe478a7771e3ca6f3aaa05f00f6e068321d9e3"},
]
[package.dependencies]
numpy = ">=1.22.0"
[[package]] [[package]]
name = "python-dateutil" name = "python-dateutil"
version = "2.9.0.post0" version = "2.9.0.post0"
@@ -704,6 +935,56 @@ pygments = ">=2.13.0,<3.0.0"
[package.extras] [package.extras]
jupyter = ["ipywidgets (>=7.5.1,<9)"] jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "scipy"
version = "1.14.1"
description = "Fundamental algorithms for scientific computing in Python"
optional = false
python-versions = ">=3.10"
files = [
{file = "scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389"},
{file = "scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3"},
{file = "scipy-1.14.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0"},
{file = "scipy-1.14.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3"},
{file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d"},
{file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69"},
{file = "scipy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad"},
{file = "scipy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5"},
{file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"},
{file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"},
{file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"},
{file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"},
{file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"},
{file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"},
{file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"},
{file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"},
{file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"},
{file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"},
{file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"},
{file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"},
{file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"},
{file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"},
{file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"},
{file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"},
{file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"},
{file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"},
{file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"},
{file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"},
{file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"},
{file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"},
{file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"},
{file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"},
{file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"},
]
[package.dependencies]
numpy = ">=1.23.5,<2.3"
[package.extras]
dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"]
doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<=7.3.7)", "sphinx-design (>=0.4.0)"]
test = ["Cython", "array-api-strict (>=2.0)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
[[package]] [[package]]
name = "shellingham" name = "shellingham"
version = "1.5.4" version = "1.5.4"
@@ -727,16 +1008,50 @@ files = [
] ]
[[package]] [[package]]
name = "tomli" name = "stack-data"
version = "2.0.1" version = "0.6.3"
description = "A lil' TOML parser" description = "Extract data from python stack frames and tracebacks for informative displays"
optional = false optional = false
python-versions = ">=3.7" python-versions = "*"
files = [ files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
] ]
[package.dependencies]
asttokens = ">=2.1.0"
executing = ">=1.2.0"
pure-eval = "*"
[package.extras]
tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
[[package]]
name = "tomlkit"
version = "0.13.2"
description = "Style preserving TOML library"
optional = false
python-versions = ">=3.8"
files = [
{file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"},
{file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"},
]
[[package]]
name = "traitlets"
version = "5.14.3"
description = "Traitlets Python configuration system"
optional = false
python-versions = ">=3.8"
files = [
{file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
{file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
]
[package.extras]
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
[[package]] [[package]]
name = "typer" name = "typer"
version = "0.12.5" version = "0.12.5"
@@ -776,7 +1091,18 @@ files = [
{file = "uldaq-1.2.3.tar.gz", hash = "sha256:2d405ead334bb4e37c20b72d8ec498f0e372b08f1d3323f622abe0581ac28b98"}, {file = "uldaq-1.2.3.tar.gz", hash = "sha256:2d405ead334bb4e37c20b72d8ec498f0e372b08f1d3323f622abe0581ac28b98"},
] ]
[[package]]
name = "wcwidth"
version = "0.2.13"
description = "Measures the displayed width of unicode strings in a terminal"
optional = false
python-versions = "*"
files = [
{file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.12" python-versions = "^3.12"
content-hash = "6b680c385942c0a2c0eef934f3fb37fdc3d2e1dc058a7f2d891d4f2f0607d9c6" content-hash = "b1076b7f750e8f7e66542918ec746e74544fbfdb376875158f083fb573d18107"

View File

@@ -1,20 +1,42 @@
[project]
organization = "de.uni-tuebingen.neuroetho"
copyright = "(c) 2020, Neuroethology lab, Uni Tuebingen"
[tool.poetry] [tool.poetry]
name = "pyrelacs" name = "pyrelacs"
version = "0.1.0" version = "0.1.0"
description = "Relaxed ELectrophysiology Acquisition, Control, and Stimulation in python" description = "Relaxed ELectrophysiology Acquisition, Control, and Stimulation in python"
authors = ["wendtalexander <wendtalexander@protonmail.com>"] authors = ["wendtalexander <wendtalexander@protonmail.com>"]
repository = "https://whale.am28.uni-tuebingen.de/git/awendt/pyrelacs"
readme = "README.md" readme = "README.md"
license = "MIT"
classifiers = [
"Topic :: Scientific/Engineering",
"Intended Audience :: Science/Research",
"Intended Audience :: End Users/Desktop",
]
include = [
{ path = "pyproject.toml" }
]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.12" python = "^3.12"
uldaq = "^1.2.3" uldaq = "^1.2.3"
typer = "^0.12.5" typer = "^0.12.5"
matplotlib = "^3.9.2" matplotlib = "^3.9.2"
numpy = "^2.1.1" numpy = "^1.9"
pyqt6 = "^6.7.1" pyqt6 = "^6.7.1"
tomli = "^2.0.1" tomlkit = "^0.13.2"
scipy = "^1.14.1"
nixio = "^1.5.3"
pyqtgraph = "^0.13.7"
[tool.poetry.scripts]
pyrelacs = "pyrelacs.app:main"
[tool.poetry.group.dev.dependencies]
ipython = "^8.27.0"
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"

0
pyrelacs/__init__.py Normal file
View File

View File

@@ -1,144 +1,44 @@
from PyQt6.QtGui import QAction
import sys import sys
import pathlib import pathlib
import ctypes
from PyQt6.QtCore import QProcess, QSize, QThreadPool, Qt from PyQt6.QtCore import QSettings
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import QApplication
QApplication,
QGridLayout,
QPushButton,
QToolBar,
QWidget,
QMainWindow,
QPlainTextEdit,
)
import tomli
import uldaq
from IPython import embed
import numpy as np
from pyrelacs.util.logging import config_logging
from . import info
from .ui.mainwindow import PyRelacs
from .util.logging import config_logging
log = config_logging() log = config_logging()
class PyRelacs(QMainWindow): def main():
def __init__(self): app = QApplication(sys.argv)
super().__init__() app.setApplicationName(info.NAME)
self.setWindowTitle("PyRelacs") app.setApplicationVersion(str(info.VERSION))
self.setMinimumSize(1000, 1000) app.setOrganizationDomain(info.ORGANIZATION)
self.threadpool = QThreadPool() # read window settings
# for starting a Qprocess settings = QSettings(info.ORGANIZATION, info.NAME)
self.p = None width = int(settings.value("app/width", 1024))
height = int(settings.value("app/height", 768))
x = int(settings.value("app/pos_x", 100))
y = int(settings.value("app/pos_y", 100))
self.daq_connect_button = QPushButton("Connect Daq") window = PyRelacs()
self.daq_connect_button.setCheckable(True) window.setMinimumWidth(200)
self.daq_connect_button.clicked.connect(self.connect_dac) window.setMinimumHeight(200)
window.resize(width, height)
window.move(x, y)
window.show()
exit_code = app.exec()
self.daq_disconnect_button = QPushButton("Disconnect Daq") # store window position and size
self.daq_disconnect_button.setCheckable(True) pos = window.pos()
self.daq_disconnect_button.clicked.connect(self.disconnect_dac) settings.setValue("app/width", window.width())
settings.setValue("app/height", window.height())
self.text = QPlainTextEdit() settings.setValue("app/pos_x", pos.x())
self.text.setReadOnly(True) settings.setValue("app/pos_y", pos.y())
sys.exit(exit_code)
layout = QGridLayout()
layout.addWidget(self.daq_connect_button, 0, 0)
layout.addWidget(self.daq_disconnect_button, 0, 1)
layout.addWidget(self.text, 2, 0, 1, 2)
self.toolbar = QToolBar("Repros")
self.addToolBar(self.toolbar)
self.repro()
self.setFixedSize(QSize(400, 300))
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
def connect_dac(self):
devices = uldaq.get_daq_device_inventory(uldaq.InterfaceType.USB)
try:
self.daq_device = uldaq.DaqDevice(devices[0])
log.debug(f"Found daq devices {len(devices)}, connecting to the first one")
self.daq_device.connect()
log.debug("Connected")
except IndexError:
log.debug("DAQ is not connected, closing")
QApplication.quit()
self.daq_connect_button.setDisabled(True)
def disconnect_dac(self):
try:
log.debug(f"{self.daq_device}")
self.daq_device.disconnect()
self.daq_device.release()
log.debug(f"{self.daq_device}")
self.daq_disconnect_button.setDisabled(True)
self.daq_connect_button.setEnabled(True)
except AttributeError:
log.debug("DAQ was not connected")
def repro(self):
repos_path = pathlib.Path(__file__).parent / "repros"
repos_names = list(repos_path.glob("*.py"))
# exclude the repos.py file
repos_names = [
f.with_suffix("").name for f in repos_names if not f.name == "repos.py"
]
for rep in repos_names:
individual_repro_button = QAction(rep, self)
individual_repro_button.setStatusTip("Button")
individual_repro_button.triggered.connect(
lambda checked, n=rep: self.run_repro(n)
)
self.toolbar.addAction(individual_repro_button)
def message(self, s):
self.text.appendPlainText(s)
def run_repro(self, name_of_repo):
if self.p is None:
self.message(f"Executing process {name_of_repo}")
self.p = QProcess()
self.p.setWorkingDirectory(str(pathlib.Path(__file__).parent / "repros/"))
# log.debug(pathlib.Path(__file__).parent / "repos")
self.p.readyReadStandardOutput.connect(self.handle_stdout)
self.p.readyReadStandardError.connect(self.handle_stderr)
self.p.stateChanged.connect(self.handle_state)
self.p.finished.connect(self.process_finished)
self.p.start("python3", [f"{name_of_repo}" + ".py"])
def handle_stderr(self):
if self.p is not None:
data = self.p.readAllStandardError()
stderr = bytes(data).decode("utf8")
self.message(stderr)
def handle_stdout(self):
if self.p is not None:
data = self.p.readAllStandardOutput()
stdout = bytes(data).decode("utf8")
self.message(stdout)
def handle_state(self, state):
states = {
QProcess.ProcessState.NotRunning: "Not running",
QProcess.ProcessState.Starting: "Starting",
QProcess.ProcessState.Running: "Running",
}
state_name = states[state]
self.message(f"State changed: {state_name}")
def process_finished(self):
self.text.appendPlainText("Process finished")
self.p = None
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication(sys.argv) main()
window = PyRelacs()
window.show()
app.exec()

BIN
pyrelacs/data Normal file

Binary file not shown.

View File

291
pyrelacs/devices/mccdac.py Normal file
View File

@@ -0,0 +1,291 @@
from ctypes import Array, c_double
import time
from typing import Union
from IPython import embed
import numpy.typing as npt
import uldaq
import numpy as np
from pyrelacs.util.logging import config_logging
log = config_logging()
class MccDac:
def __init__(self) -> None:
devices = uldaq.get_daq_device_inventory(uldaq.InterfaceType.USB)
log.debug(f"Found daq devices {len(devices)}, connecting to the first one")
if len(devices) == 0:
log.error("Did not found daq devices, please connect one")
exit(1)
self.daq_device = uldaq.DaqDevice(devices[0])
try:
self.daq_device.connect()
except uldaq.ul_exception.ULException:
self.disconnect_dac()
self.connect_dac()
self.ai_device = self.daq_device.get_ai_device()
self.ao_device = self.daq_device.get_ao_device()
self.dio_device = self.daq_device.get_dio_device()
log.debug("Connected")
def connect_dac(self):
devices = uldaq.get_daq_device_inventory(uldaq.InterfaceType.USB)
log.debug(f"Found daq devices {len(devices)}, connecting to the first one")
if len(devices) == 0:
log.error("Did not found daq devices, please connect one")
exit(1)
self.daq_device = uldaq.DaqDevice(devices[0])
self.daq_device.connect()
self.ai_device = self.daq_device.get_ai_device()
self.ao_device = self.daq_device.get_ao_device()
self.dio_device = self.daq_device.get_dio_device()
log.debug("Connected")
def read_analog(
self,
channels: list[int],
duration: int,
samplerate: float,
AiInputMode: uldaq.AiInputMode = uldaq.AiInputMode.SINGLE_ENDED,
Range: uldaq.Range = uldaq.Range.BIP10VOLTS,
ScanOption: uldaq.ScanOption = uldaq.ScanOption.DEFAULTIO,
AInScanFlag: uldaq.AInScanFlag = uldaq.AInScanFlag.DEFAULT,
) -> Array[c_double]:
assert len(channels) == 2, log.error("You can only provide two channels [0, 1]")
if channels[0] != channels[1]:
buffer_len_channels = 2
else:
buffer_len_channels = 1
buffer_len = np.shape(np.arange(0, duration, 1 / samplerate))[0]
data_analog_input = uldaq.create_float_buffer(buffer_len_channels, buffer_len)
er = self.ai_device.a_in_scan(
channels[0],
channels[1],
AiInputMode,
Range,
buffer_len,
samplerate,
ScanOption,
AInScanFlag,
data=data_analog_input,
)
return data_analog_input
def write_analog(
self,
data: Union[list, npt.NDArray],
channels: list[int],
samplerate: float,
Range: uldaq.Range = uldaq.Range.BIP10VOLTS,
ScanOption: uldaq.ScanOption = uldaq.ScanOption.DEFAULTIO,
AOutScanFlag: uldaq.AOutScanFlag = uldaq.AOutScanFlag.DEFAULT,
) -> Array[c_double]:
assert len(channels) == 2, log.error("You can only provide two channels [0, 1]")
buffer = c_double * len(data)
data_analog_output = buffer(*data)
log.debug(f"Created C_double data {data_analog_output}")
try:
err = self.ao_device.a_out_scan(
channels[0],
channels[1],
Range,
int(len(data)),
samplerate,
ScanOption,
AOutScanFlag,
data_analog_output,
)
except Exception as e:
print(f"{e}")
self.set_analog_to_zero()
self.disconnect_dac()
return data_analog_output
def set_analog_to_zero(self, channels: list[int] = [0, 1]):
try:
err = self.ao_device.a_out_list(
channels[0],
channels[1],
[
uldaq.Range.BIP10VOLTS,
uldaq.Range.BIP10VOLTS,
],
uldaq.AOutListFlag.DEFAULT,
[0, 0],
)
except Exception as e:
log.error("f{e}")
log.error("disconnection dac")
self.disconnect_dac()
def diggital_trigger(self) -> None:
data = self.read_bit(channel=0)
if data:
self.write_bit(channel=0, bit=0)
time.time_ns()
self.write_bit(channel=0, bit=1)
else:
self.write_bit(channel=0, bit=1)
def write_bit(self, channel: int = 0, bit: int = 1) -> None:
self.dio_device.d_config_bit(
uldaq.DigitalPortType.AUXPORT, channel, uldaq.DigitalDirection.OUTPUT
)
self.dio_device.d_bit_out(
uldaq.DigitalPortType.AUXPORT, bit_number=channel, data=bit
)
def read_bit(self, channel: int = 0):
bit = self.dio_device.d_bit_in(uldaq.DigitalPortType.AUXPORT, channel)
return bit
def read_digitalio(
self,
channels: list[int],
duration,
samplerate,
ScanOptions: uldaq.ScanOption = uldaq.ScanOption.DEFAULTIO,
DInScanFlag: uldaq.DInScanFlag = uldaq.DInScanFlag.DEFAULT,
):
if channels[0] == channels[1]:
channel_len = 1
else:
channel_len = len(channels)
buffer_len = np.shape(np.arange(0, duration, 1 / samplerate))[0]
data_digital_input = uldaq.create_int_buffer(channel_len, buffer_len)
self.dio_device.d_config_port(
uldaq.DigitalPortType.AUXPORT, uldaq.DigitalDirection.INPUT
)
scan_rate = self.dio_device.d_in_scan(
uldaq.DigitalPortType.AUXPORT0,
uldaq.DigitalPortType.AUXPORT0,
len(data_digital_input),
samplerate,
ScanOptions,
DInScanFlag,
data_digital_input,
)
return data_digital_input
def disconnect_dac(self):
self.daq_device.disconnect()
self.daq_device.release()
def check_attenuator(self):
"""
ident : attdev-1
strobepin : 6
datainpin : 5
dataoutpin: -1
cspin : 4
mutepin : 7
zcenpin : -1
"""
SAMPLERATE = 40_000.0
DURATION = 5
AMPLITUDE = 1
SINFREQ = 1
t = np.arange(0, DURATION, 1 / SAMPLERATE)
data = AMPLITUDE * np.sin(2 * np.pi * SINFREQ * t)
# data_channels = np.concatenate((data, data))
db_values = [0, 0, -2, -5, -10, -20, -50]
db_values = [0, -10, -20]
for i, db_value in enumerate(db_values):
log.info(f"Attenuating the Channels, with {db_value}")
if i == 1:
log.info("Muting the Channels")
self.set_attenuation_level(
db_value, db_value, mute_channel1=True, mute_channel2=True
)
else:
self.set_attenuation_level(db_value, db_value)
_ = self.write_analog(
data,
[0, 0],
SAMPLERATE,
ScanOption=uldaq.ScanOption.EXTTRIGGER,
Range=uldaq.Range.BIP10VOLTS,
)
self.diggital_trigger()
try:
self.ao_device.scan_wait(uldaq.WaitType.WAIT_UNTIL_DONE, 15)
self.write_bit(channel=0, bit=0)
self.set_analog_to_zero()
except uldaq.ul_exception.ULException:
log.debug("Operation timed out")
self.write_bit(channel=0, bit=0)
self.disconnect_dac()
self.connect_dac()
self.set_analog_to_zero()
finally:
self.write_bit(channel=0, bit=0)
self.disconnect_dac()
self.connect_dac()
self.set_analog_to_zero()
log.info("Sleeping for 1 second, before next attenuation")
time.sleep(1)
def set_attenuation_level(
self,
db_channel1: float = 5.0,
db_channel2: float = 5.0,
mute_channel1: bool = False,
mute_channel2: bool = False,
):
"""
ident : attdev-1
strobepin : 6
datainpin : 5
dataoutpin: -1
cspin : 4
mutepin : 7
zcenpin : -1
"""
self.activate_attenuator()
hardware_possible_db = np.arange(-95.5, 32.0, 0.5)
byte_number = np.arange(1, 256)
byte_number_db1 = byte_number[hardware_possible_db == db_channel1][0]
binary_db1 = np.binary_repr(byte_number_db1, width=8)
byte_number_db2 = byte_number[hardware_possible_db == db_channel2][0]
binary_db2 = np.binary_repr(byte_number_db2, width=8)
if mute_channel1:
log.info("Muting channel one")
binary_db1 = "00000000"
if mute_channel2:
log.info("Muting channel one")
binary_db2 = "00000000"
channels_db = binary_db2 + binary_db1
self.write_bit(channel=4, bit=0)
for b in channels_db:
self.write_bit(channel=5, bit=int(b))
time.time_ns()
self.write_bit(channel=6, bit=1)
time.time_ns()
self.write_bit(channel=6, bit=0)
time.time_ns()
self.write_bit(channel=4, bit=1)
def activate_attenuator(self):
for ch, b in zip([4, 5, 6, 7], [1, 0, 0, 1]):
self.write_bit(channel=ch, bit=b)
def deactivate_attenuator(self):
# mute should be enabled for starting calibration
self.write_bit(channel=7, bit=0)

38
pyrelacs/info.py Normal file
View File

@@ -0,0 +1,38 @@
import tomlkit
import pathlib
def load_project_settings(project_root):
# Read the pyproject.toml file
with open(pathlib.Path.joinpath(project_root, "pyproject.toml"), "r") as f:
pyproject_content = f.read()
# Parse the toml content
pyproject = tomlkit.parse(pyproject_content)
# Access project settings
return {
"name": pyproject["tool"]["poetry"]["name"],
"version": pyproject["tool"]["poetry"]["version"],
"description": pyproject["tool"]["poetry"]["description"],
"authors": pyproject["tool"]["poetry"]["authors"],
"readme": pyproject["tool"]["poetry"]["authors"],
"licence": pyproject["tool"]["poetry"]["license"],
"organization": pyproject["project"]["organization"],
"classifiers": pyproject["tool"]["poetry"]["classifiers"],
"copyright": pyproject["project"]["copyright"],
"repository": pyproject["tool"]["poetry"]["repository"],
}
_root = pathlib.Path(__file__).parent.parent
_infodict = load_project_settings(_root)
NAME = _infodict["name"]
VERSION = _infodict["version"]
AUTHORS = _infodict["authors"]
COPYRIGHT = _infodict["copyright"]
HOMEPAGE = _infodict["repository"]
CLASSIFIERS = _infodict["classifiers"]
DESCRIPTION = _infodict["description"]
ORGANIZATION = _infodict["organization"]

View File

View File

@@ -1,42 +1,272 @@
import ctypes import signal
import sys
import faulthandler
import time
import nixio as nix
import uldaq import uldaq
from IPython import embed from IPython import embed
from pyrelacs.repros.repos import Repos
from pyrelacs.util.logging import config_logging
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from scipy.signal import welch, csd
from scipy.signal import find_peaks
from pyrelacs.devices.mccdac import MccDac
from pyrelacs.util.logging import config_logging
log = config_logging() log = config_logging()
faulthandler.enable()
class Calibration(Repos): class Calibration(MccDac):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self.SAMPLERATE = 40_000.0
self.DURATION = 5
self.AMPLITUDE = 1
self.SINFREQ = 750
@staticmethod
def run(nix_file: nix.File):
calb = Calibration()
calb.check_beat(nix_file)
def check_amplitude(self):
db_values = [0.0, -5.0, -10.0, -20.0, -50.0]
colors = ["red", "green", "blue", "black", "yellow"]
self.set_attenuation_level(db_channel1=0.0, db_channel2=0.0)
# write to ananlog 1
t = np.arange(0, self.DURATION, 1 / self.SAMPLERATE)
data = self.AMPLITUDE * np.sin(2 * np.pi * self.SINFREQ * t)
fig, ax = plt.subplots()
for i, db_value in enumerate(db_values):
self.set_attenuation_level(db_channel1=db_value, db_channel2=db_value)
log.debug(f"{db_value}")
stim = self.write_analog(
data,
[0, 0],
self.SAMPLERATE,
ScanOption=uldaq.ScanOption.EXTTRIGGER,
)
data_channel_one = self.read_analog(
[0, 0],
self.DURATION,
self.SAMPLERATE,
ScanOption=uldaq.ScanOption.EXTTRIGGER,
)
time.sleep(1)
log.debug("Starting the Scan")
self.diggital_trigger()
try:
self.ao_device.scan_wait(uldaq.WaitType.WAIT_UNTIL_DONE, 15)
log.debug("Scan finished")
self.write_bit(channel=0, bit=0)
time.sleep(1)
self.set_analog_to_zero()
except uldaq.ul_exception.ULException:
log.debug("Operation timed out")
# reset the diggital trigger
self.write_bit(channel=0, bit=0)
time.sleep(1)
self.set_analog_to_zero()
self.disconnect_dac()
if i == 0:
ax.plot(t, stim, label=f"Input_{db_value}", color=colors[i])
ax.plot(t, data_channel_one, label=f"Reaout {db_value}", color=colors[i])
ax.legend()
plt.show()
def run_calibration(self):
# Stimulus
time = np.arange(0, DURATION, 1 / SAMPLERATE)
data = AMPLITUDE * np.sin(2 * np.pi * SINFREQ * time)
# sending stimulus
stim, ao_device = self.send_analog_dac(
data, [0, 0], SAMPLERATE, ScanOption=uldaq.ScanOption.EXTTRIGGER
)
read_data = self.read_analog_daq(
[0, 1], DURATION, SAMPLERATE, ScanOption=uldaq.ScanOption.EXTTRIGGER
)
self.digital_trigger()
ao_device.scan_wait(uldaq.WaitType.WAIT_UNTIL_DONE, 11)
self.digital_trigger(data=0)
self.disconnect_dac() self.disconnect_dac()
embed()
exit() def check_beat(self, nix_file: nix.File):
self.set_attenuation_level(db_channel1=-10.0, db_channel2=0.0)
t = np.arange(0, self.DURATION, 1 / self.SAMPLERATE)
data = self.AMPLITUDE * np.sin(2 * np.pi * self.SINFREQ * t)
# data = np.concatenate((data, data))
db_values = [0.0, -5.0, -8.5, -10.0]
colors = ["red", "blue", "black", "green"]
colors_in = ["lightcoral", "lightblue", "grey", "lightgreen"]
block = nix_file.create_block("Calibration", "data")
# fig, axes = plt.subplots(2, 2, sharex="col")
for i, db_value in enumerate(db_values):
self.set_attenuation_level(db_channel1=db_value)
stim = self.write_analog(
data,
[0, 0],
self.SAMPLERATE,
ScanOption=uldaq.ScanOption.EXTTRIGGER,
)
readout = self.read_analog(
[0, 1],
self.DURATION,
self.SAMPLERATE,
ScanOption=uldaq.ScanOption.EXTTRIGGER,
)
self.diggital_trigger()
log.info(self.ao_device)
ai_status = uldaq.ScanStatus.RUNNING
ao_status = uldaq.ScanStatus.RUNNING
log.debug(
f"Status Analog_output {ao_status}\n, Status Analog_input {ai_status}"
)
while (ai_status != uldaq.ScanStatus.IDLE) and (
ao_status != uldaq.ScanStatus.IDLE
):
# log.debug("Scanning")
time.time_ns()
ai_status = self.ai_device.get_scan_status()[0]
ao_status = self.ao_device.get_scan_status()[0]
self.write_bit(channel=0, bit=0)
log.debug(
f"Status Analog_output {ao_status}\n, Status Analog_input {ai_status}"
)
channel1 = np.array(readout[::2])
channel2 = np.array(readout[1::2])
block.create_data_array(
f"stimulus_{db_value}",
"Array",
shape=data.shape,
data=channel1,
label="Voltage",
unit="V",
)
block.create_data_array(
f"fish_{db_value}",
"Array",
shape=data.shape,
data=channel2,
label="Voltage",
unit="V",
)
beat = channel1 + channel2
beat_square = beat**2
f, powerspec = welch(beat, fs=self.SAMPLERATE)
powerspec = decibel(powerspec)
f_sq, powerspec_sq = welch(beat_square, fs=self.SAMPLERATE)
powerspec_sq = decibel(powerspec_sq)
peaks = find_peaks(powerspec_sq, prominence=20)[0]
f_stim, powerspec_stim = welch(channel1, fs=self.SAMPLERATE)
powerspec_stim = decibel(powerspec_stim)
f_in, powerspec_in = welch(channel2, fs=self.SAMPLERATE)
powerspec_in = decibel(powerspec_in)
# axes[0, 0].plot(
# t,
# channel1,
# label=f"{db_value} Readout Channel0",
# color=colors[i],
# )
# axes[0, 0].plot(
# t,
# channel2,
# label=f"{db_value} Readout Channel1",
# color=colors_in[i],
# )
#
# axes[0, 1].plot(
# f_stim,
# powerspec_stim,
# label=f"{db_value} powerspec Channel0",
# color=colors[i],
# )
# axes[0, 1].plot(
# f_in,
# powerspec_in,
# label=f"{db_value} powerspec Channel2",
# color=colors_in[i],
# )
# axes[0, 1].set_xlabel("Freq [HZ]")
# axes[0, 1].set_ylabel("dB")
#
# axes[1, 0].plot(
# t,
# beat,
# label="Beat",
# color=colors[i],
# )
# axes[1, 0].plot(
# t,
# beat**2,
# label="Beat squared",
# color=colors_in[i],
# )
# axes[1, 0].legend()
#
# axes[1, 1].plot(
# f,
# powerspec,
# color=colors[i],
# )
# axes[1, 1].plot(
# f_sq,
# powerspec_sq,
# color=colors_in[i],
# label=f"dB {db_value}, first peak {np.min(f_sq[peaks])}",
# )
# axes[1, 1].scatter(
# f_sq[peaks],
# powerspec_sq[peaks],
# color="maroon",
# )
# axes[1, 1].set_xlabel("Freq [HZ]")
# axes[1, 1].set_ylabel("dB")
# axes[0, 0].legend()
# axes[1, 1].legend()
# plt.show()
self.set_analog_to_zero()
self.disconnect_dac()
if __name__ == "__main__": def decibel(power, ref_power=1.0, min_power=1e-20):
SAMPLERATE = 40_000.0 """Transform power to decibel relative to ref_power.
DURATION = 5
AMPLITUDE = 3 \\[ decibel = 10 \\cdot \\log_{10}(power/ref\\_power) \\]
SINFREQ = 1 Power values smaller than `min_power` are set to `-np.inf`.
daq_input = Calibration()
daq_input.run_calibration() Parameters
----------
power: float or array
Power values, for example from a power spectrum or spectrogram.
ref_power: float or None or 'peak'
Reference power for computing decibel.
If set to `None` or 'peak', the maximum power is used.
min_power: float
Power values smaller than `min_power` are set to `-np.inf`.
Returns
-------
decibel_psd: array
Power values in decibel relative to `ref_power`.
"""
if np.isscalar(power):
tmp_power = np.array([power])
decibel_psd = np.array([power])
else:
tmp_power = power
decibel_psd = power.copy()
if ref_power is None or ref_power == "peak":
ref_power = np.max(decibel_psd)
decibel_psd[tmp_power <= min_power] = float("-inf")
decibel_psd[tmp_power > min_power] = 10.0 * np.log10(
decibel_psd[tmp_power > min_power] / ref_power
)
if np.isscalar(power):
return decibel_psd[0]
else:
return decibel_psd

View File

@@ -1,86 +0,0 @@
import ctypes
import uldaq
from IPython import embed
import matplotlib.pyplot as plt
import numpy as np
from pyrelacs.util.logging import config_logging
log = config_logging()
class ReadWrite:
def __init__(self) -> None:
devices = uldaq.get_daq_device_inventory(uldaq.InterfaceType.USB)
log.debug(f"Found daq devices {len(devices)}, connecting to the first one")
self.daq_device = uldaq.DaqDevice(devices[0])
self.daq_device.connect()
log.debug("Connected")
# self.daq_device.enable_event(
# uldaq.DaqEventType.ON_DATA_AVAILABLE,
# 1,
# self.read_write,
# (uldaq.DaqEventType.ON_DATA_AVAILABLE, 1, 1),
# )
def read_write(self) -> None:
# event_type = callback_args.event_type
# event_data = callback_args.event_data
# user_data = callback_args.user_data
FS = 30_000.0
DURATION = 10
FREQUENCY = 100
time = np.arange(0, DURATION, 1 / FS)
data = 2 * np.sin(2 * np.pi * FREQUENCY * time)
buffer = ctypes.c_double * len(time)
data_c = buffer(*data)
buf = uldaq.create_float_buffer(1, len(time))
# Get the Ananlog In device and Analog Info
ai_device = self.daq_device.get_ai_device()
ai_info = ai_device.get_info()
# Get the Analog Out device
ao_device = self.daq_device.get_ao_device()
ao_info = ao_device.get_info()
er_ao = ao_device.a_out_scan(
0,
0,
uldaq.Range.BIP10VOLTS,
int(len(data)),
30_000.0,
uldaq.ScanOption.DEFAULTIO,
uldaq.AOutScanFlag.DEFAULT,
data_c,
)
er_ai = ai_device.a_in_scan(
1,
1,
uldaq.AiInputMode.SINGLE_ENDED,
uldaq.Range.BIP10VOLTS,
len(time),
FS,
uldaq.ScanOption.DEFAULTIO,
uldaq.AInScanFlag.DEFAULT,
data=buf,
)
ai_device.scan_wait(uldaq.WaitType.WAIT_UNTIL_DONE, timeout=-1)
log.debug("Scanning")
self.daq_device.disconnect()
self.daq_device.release()
plt.plot(buf)
plt.plot(data_c)
plt.show()
if __name__ == "__main__":
daq_input = ReadWrite()
daq_input.read_write()

View File

@@ -1,28 +0,0 @@
import uldaq
import matplotlib.pyplot as plt
from pyrelacs.util.logging import config_logging
from .repos import Repos
log = config_logging()
class ReadData(Repos):
def __init__(self) -> None:
super().__init__()
def analog_in(self) -> None:
# Get the Ananlog In device and Analog Info
data = self.read_analog_daq(
[0, 0],
10,
3000.0,
)
plt.plot(data)
plt.show()
self.disconnect_dac()
if __name__ == "__main__":
daq_input = ReadData()
daq_input.analog_in()

View File

@@ -1,37 +0,0 @@
import ctypes
import uldaq
from IPython import embed
from pyrelacs.repros.repos import Repos
from pyrelacs.util.logging import config_logging
import numpy as np
import matplotlib.pyplot as plt
log = config_logging()
class Output_daq(Repos):
def __init__(self) -> None:
super().__init__()
# devices = uldaq.get_daq_device_inventory(uldaq.InterfaceType.USB)
# self.daq_device = uldaq.DaqDevice(devices[0])
# self.daq_device.connect()
def write_daq(self):
log.debug("running repro")
time = np.arange(0, 10, 1 / 30_000.0)
data = 2 * np.sin(2 * np.pi * 1 * time)
self.send_analog_dac(
data, [0, 0], 30_000, ScanOption=uldaq.ScanOption.EXTTRIGGER
)
def trigger(self):
self.digital_trigger(1)
self.daq_device.disconnect()
self.daq_device.release()
if __name__ == "__main__":
daq_input = Output_daq()
daq_input.write_daq()
# daq_input.trigger()

View File

@@ -1,142 +0,0 @@
from ctypes import Array, c_double
from typing import Union
from IPython import embed
import numpy.typing as npt
import uldaq
import numpy as np
from pyrelacs.util.logging import config_logging
log = config_logging()
class Repos:
def __init__(self) -> None:
devices = uldaq.get_daq_device_inventory(uldaq.InterfaceType.USB)
log.debug(f"Found daq devices {len(devices)}, connecting to the first one")
if len(devices) == 0:
log.error("Did not found daq devices, please connect one")
exit(1)
self.daq_device = uldaq.DaqDevice(devices[0])
self.daq_device.connect()
log.debug("Connected")
def read_analog_daq(
self,
channels: list[int],
duration: int,
samplerate: float,
AiInputMode: uldaq.AiInputMode = uldaq.AiInputMode.SINGLE_ENDED,
Range: uldaq.Range = uldaq.Range.BIP10VOLTS,
ScanOption: uldaq.ScanOption = uldaq.ScanOption.DEFAULTIO,
AInScanFlag: uldaq.AInScanFlag = uldaq.AInScanFlag.DEFAULT,
) -> Array[c_double]:
if channels[0] == channels[1]:
channel_len = 1
else:
channel_len = len(channels)
assert len(channels) == 2, log.error("Please provide a list with two ints")
ai_device = self.daq_device.get_ai_device()
buffer_len = np.shape(np.arange(0, duration, 1 / samplerate))[0]
data_analog_input = uldaq.create_float_buffer(channel_len, buffer_len)
er = ai_device.a_in_scan(
channels[0],
channels[1],
AiInputMode,
Range,
buffer_len,
samplerate,
ScanOption,
AInScanFlag,
data=data_analog_input,
)
# ai_device.scan_wait(uldaq.WaitType.WAIT_UNTIL_DONE, timeout=-1)
return data_analog_input
def send_analog_dac(
self,
data: Union[list, npt.NDArray],
channels: list[int],
samplerate: float,
Range: uldaq.Range = uldaq.Range.BIP10VOLTS,
ScanOption: uldaq.ScanOption = uldaq.ScanOption.DEFAULTIO,
AOutScanFlag: uldaq.AOutScanFlag = uldaq.AOutScanFlag.DEFAULT,
):
"""
Parameters
----------
data : Union[list, npt.NDArray]
channels : list[int]
duration : int
samplerate : float
AiInputMode : uldaq.AiInputMode
Range : uldaq.Range
ScanOption : uldaq.ScanOption
AInScanFlag : uldaq.AOutScanFlag
Returns
-------
Array[c_double]
ao_device
"""
buffer = c_double * len(data)
data_analog_output = buffer(*data)
log.debug(f"Created C_double data {data_analog_output}")
ao_device = self.daq_device.get_ao_device()
ao_info = ao_device.get_info()
err = ao_device.a_out_scan(
channels[0],
channels[1],
Range,
int(len(data)),
samplerate,
ScanOption,
AOutScanFlag,
data_analog_output,
)
log.info(f"The actual scan rate was {err}")
# ao_device.scan_wait(uldaq.WaitType.WAIT_UNTIL_DONE, 11)
return data_analog_output, ao_device
def digital_trigger(self, portn: int = 0, data: int = 1) -> None:
log.info(f"{self.daq_device}")
dio_device = self.daq_device.get_dio_device()
dio_device.d_config_bit(
uldaq.DigitalPortType.AUXPORT, portn, uldaq.DigitalDirection.OUTPUT
)
dio_device.d_bit_out(uldaq.DigitalPortType.AUXPORT, bit_number=portn, data=data)
def disconnect_dac(self):
self.daq_device.disconnect()
self.daq_device.release()
def clean_up():
pass
def run_repo(self) -> None:
pass
def stop_repo(self) -> None:
pass
def reload_repo(self) -> None:
pass

55
pyrelacs/repros/repros.py Normal file
View File

@@ -0,0 +1,55 @@
import sys
import importlib.util
import ast
import pathlib
from IPython import embed
import nixio as nix
from pyrelacs.util.logging import config_logging
log = config_logging()
class Repro:
def __init__(self) -> None:
pass
def run_repro(
self, nix_file: nix.File, name: str, file: pathlib.Path, *args, **kwargs
) -> None:
spec = importlib.util.spec_from_file_location("rep", file)
if not spec:
log.error("Could not load the file")
else:
module = importlib.util.module_from_spec(spec)
if not module:
log.error("Could not load the module of the repro")
else:
sys.modules[name] = module
spec.loader.exec_module(module)
if hasattr(module, name):
rep_class = getattr(module, name)
rep_class.run(nix_file)
else:
raise AttributeError(f"{file.name} has no {name} class")
def names_of_repros(self):
file_path_cur = pathlib.Path(__file__).parent
python_files = list(file_path_cur.glob("**/*.py"))
exclude_files = ["repros.py", "__init__.py"]
python_files = [f for f in python_files if f.name not in exclude_files]
repro_names = []
file_names = []
for python_file in python_files:
with open(python_file, "r") as file:
file_content = file.read()
tree = ast.parse(file_content)
class_name = [
node.name
for node in ast.walk(tree)
if isinstance(node, ast.ClassDef)
]
repro_names.extend(class_name)
file_names.append(python_file)
file.close()
return repro_names, file_names

0
pyrelacs/ui/__init__.py Normal file
View File

178
pyrelacs/ui/mainwindow.py Normal file
View File

@@ -0,0 +1,178 @@
from PyQt6.QtGui import QAction
from PyQt6.QtCore import QSize, QThreadPool
from PyQt6.QtWidgets import (
QApplication,
QGridLayout,
QPushButton,
QToolBar,
QWidget,
QMainWindow,
QPlainTextEdit,
)
import uldaq
import pathlib
import numpy as np
import nixio as nix
import pyqtgraph as pg
from scipy.signal import welch, find_peaks
from ..worker import Worker
from ..repros.repros import Repro
from ..util.logging import config_logging
log = config_logging()
from IPython import embed
class PyRelacs(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PyRelacs")
self.setMinimumSize(1000, 1000)
self.plot_graph = pg.PlotWidget()
self.threadpool = QThreadPool()
self.repros = Repro()
self.daq_connect_button = QPushButton("Connect Daq")
self.daq_connect_button.setCheckable(True)
self.daq_connect_button.clicked.connect(self.connect_dac)
self.daq_disconnect_button = QPushButton("Disconnect Daq")
self.daq_disconnect_button.setCheckable(True)
self.daq_disconnect_button.clicked.connect(self.disconnect_dac)
self.plot_calibration_button = QPushButton("Plot Calibration")
self.plot_calibration_button.setCheckable(True)
self.plot_calibration_button.clicked.connect(self.plot_calibration)
self.text = QPlainTextEdit()
self.text.setReadOnly(True)
layout = QGridLayout()
layout.addWidget(self.plot_calibration_button, 0, 0)
layout.addWidget(self.daq_disconnect_button, 0, 1)
layout.addWidget(self.text, 3, 0, 1, 2)
layout.addWidget(self.plot_graph, 2, 0, 1, 2)
self.toolbar = QToolBar("Repros")
self.addToolBar(self.toolbar)
self.repros_to_toolbar()
# self.setFixedSize(QSize(400, 300))
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
filename = pathlib.Path.joinpath(pathlib.Path.cwd(), "data.nix")
self.nix_file = nix.File.open(
str(filename), nix.FileMode.Overwrite
)
def plot_calibration(self):
def decibel(power, ref_power=1.0, min_power=1e-20):
"""Transform power to decibel relative to ref_power.
\\[ decibel = 10 \\cdot \\log_{10}(power/ref\\_power) \\]
Power values smaller than `min_power` are set to `-np.inf`.
Parameters
----------
power: float or array
Power values, for example from a power spectrum or spectrogram.
ref_power: float or None or 'peak'
Reference power for computing decibel.
If set to `None` or 'peak', the maximum power is used.
min_power: float
Power values smaller than `min_power` are set to `-np.inf`.
Returns
-------
decibel_psd: array
Power values in decibel relative to `ref_power`.
"""
if np.isscalar(power):
tmp_power = np.array([power])
decibel_psd = np.array([power])
else:
tmp_power = power
decibel_psd = power.copy()
if ref_power is None or ref_power == "peak":
ref_power = np.max(decibel_psd)
decibel_psd[tmp_power <= min_power] = float("-inf")
decibel_psd[tmp_power > min_power] = 10.0 * np.log10(
decibel_psd[tmp_power > min_power] / ref_power
)
if np.isscalar(power):
return decibel_psd[0]
else:
return decibel_psd
block = self.nix_file.blocks[0]
for stim, fish in zip(
list(block.data_arrays)[::2], list(block.data_arrays)[1::2]
):
beat = stim[:] + fish[:]
beat_squared = beat**2
f, powerspec = welch(beat, fs=40_000.0)
powerspec = decibel(powerspec)
f_sq, powerspec_sq = welch(beat_squared, fs=40_000.0)
powerspec_sq = decibel(powerspec_sq)
peaks = find_peaks(powerspec_sq, prominence=20)[0]
pen = pg.mkPen()
self.plot_graph.plot(
np.arange(0, len(beat)) / 40_000.0, beat_squared, pen=pen
)
def connect_dac(self):
devices = uldaq.get_daq_device_inventory(uldaq.InterfaceType.USB)
try:
self.daq_device = uldaq.DaqDevice(devices[0])
log.debug(f"Found daq devices {len(devices)}, connecting to the first one")
self.daq_device.connect()
log.debug("Connected")
except IndexError:
log.debug("DAQ is not connected, closing")
QApplication.quit()
self.daq_connect_button.setDisabled(True)
def disconnect_dac(self):
try:
log.debug(f"{self.daq_device}")
self.daq_device.disconnect()
self.daq_device.release()
log.debug(f"{self.daq_device}")
self.daq_disconnect_button.setDisabled(True)
self.daq_connect_button.setEnabled(True)
except AttributeError:
log.debug("DAQ was not connected")
def repros_to_toolbar(self):
repro_names, file_names = self.repros.names_of_repros()
for rep, fn in zip(repro_names, file_names):
individual_repro_button = QAction(rep, self)
individual_repro_button.setStatusTip("Button")
individual_repro_button.triggered.connect(
lambda checked, n=rep, f=fn: self.run_repro(n, f)
)
self.toolbar.addAction(individual_repro_button)
def run_repro(self, n, fn):
self.text.appendPlainText(f"started Repro {n}, {fn}")
worker = Worker(self.repros.run_repro, self.nix_file, n, fn)
worker.signals.result.connect(self.print_output)
worker.signals.finished.connect(self.thread_complete)
worker.signals.progress.connect(self.progress_fn)
self.threadpool.start(worker)
def print_output(self, s):
print(s)
def thread_complete(self):
print("THREAD COMPLETE!")
def progress_fn(self, n):
print("%d%% done" % n)

View File

75
pyrelacs/worker.py Normal file
View File

@@ -0,0 +1,75 @@
import sys
import traceback
from PyQt6.QtCore import QRunnable, pyqtSlot, QObject, pyqtSignal
class WorkerSignals(QObject):
"""
Defines the signals available from a running worker thread.
Supported signals are:
finished
No data
error
tuple (exctype, value, traceback.format_exc() )
result
object data returned from processing, anything
progress
int indicating % progress
"""
finished = pyqtSignal()
error = pyqtSignal(tuple)
result = pyqtSignal(object)
progress = pyqtSignal(int)
class Worker(QRunnable):
"""
Worker thread
Inherits from QRunnable to handler worker thread setup, signals and wrap-up.
:param callback: The function callback to run on this worker thread. Supplied args and
kwargs will be passed through to the runner.
:type callback: function
:param args: Arguments to pass to the callback function
:param kwargs: Keywords to pass to the callback function
"""
def __init__(self, fn, *args, **kwargs):
super(Worker, self).__init__()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals()
# Add the callback to our kwargs
self.kwargs["progress_callback"] = self.signals.progress
@pyqtSlot()
def run(self):
"""
Initialise the runner function with passed args, kwargs.
"""
# Retrieve args/kwargs here; and fire processing using them
try:
result = self.fn(*self.args, **self.kwargs)
except:
traceback.print_exc()
exctype, value = sys.exc_info()[:2]
self.signals.error.emit((exctype, value, traceback.format_exc()))
else:
self.signals.result.emit(result) # Return the result of the processing
finally:
self.signals.finished.emit() # Done