From 2ce6d1d94d9753f86c565ccb7a5f634817dbd9b2 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Wed, 25 Dec 2024 12:35:13 +0000 Subject: [PATCH 01/16] Add magicgui dependencies --- pyproject.toml | 8 ++- uv.lock | 153 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 855e4f70..bf840ed4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ "gitpython", "semver", "aind_behavior_services>=0.8.0, <0.9.0", - "aind-slims-api<0.2" + "aind-slims-api<0.2", ] [project.urls] @@ -38,11 +38,17 @@ aind-services = [ "aind-data-schema<=1.2" ] +magicgui = [ + "magicgui[pyqt6]" +] + dev = [ "ruff", "codespell", "coverage", "aind-behavior-experiment-launcher[aind-services]", + "aind-behavior-experiment-launcher[magicgui]", + ] docs = [ diff --git a/uv.lock b/uv.lock index 75fb6b10..1d5db5d0 100644 --- a/uv.lock +++ b/uv.lock @@ -43,6 +43,7 @@ dev = [ { name = "aind-watchdog-service" }, { name = "codespell" }, { name = "coverage" }, + { name = "magicgui", extra = ["pyqt6"] }, { name = "ruff" }, ] docs = [ @@ -56,11 +57,15 @@ docs = [ { name = "sphinx-jsonschema" }, { name = "sphinx-mdinclude" }, ] +magicgui = [ + { name = "magicgui", extra = ["pyqt6"] }, +] [package.metadata] requires-dist = [ { name = "aind-behavior-experiment-launcher", extras = ["aind-services"], marker = "extra == 'dev'" }, { name = "aind-behavior-experiment-launcher", extras = ["aind-services"], marker = "extra == 'docs'" }, + { name = "aind-behavior-experiment-launcher", extras = ["magicgui"], marker = "extra == 'dev'" }, { name = "aind-behavior-services", specifier = ">=0.8.0,<0.9.0" }, { name = "aind-data-schema", marker = "extra == 'aind-services'", specifier = "<=1.2" }, { name = "aind-slims-api", specifier = "<0.2" }, @@ -70,6 +75,7 @@ requires-dist = [ { name = "coverage", marker = "extra == 'dev'" }, { name = "furo", marker = "extra == 'docs'" }, { name = "gitpython" }, + { name = "magicgui", extras = ["pyqt6"], marker = "extra == 'magicgui'" }, { name = "pydantic", specifier = ">=2.7,<3.0" }, { name = "ruff", marker = "extra == 'dev'" }, { name = "semver" }, @@ -539,6 +545,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 }, ] +[[package]] +name = "docstring-parser" +version = "0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533 }, +] + [[package]] name = "docutils" version = "0.20.1" @@ -740,6 +755,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459 }, ] +[[package]] +name = "magicgui" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docstring-parser" }, + { name = "psygnal" }, + { name = "qtpy" }, + { name = "superqt", extra = ["iconify"] }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/44/405f7028b00d94e29ddbaff00f2674e548d3bff8d343fbf7500bd77aa071/magicgui-0.10.0.tar.gz", hash = "sha256:56dbe28afc526809e09932cd6caad8fc1a8305fe66c8feca16f797a04b5aee7c", size = 20942460 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/42/7e4f03201dfc10b4a8d4d94d183c878d7d0d4f2eee173e95294c71828014/magicgui-0.10.0-py3-none-any.whl", hash = "sha256:836276d61b0d9752eb8a215ff9f140c9c07ed5659b6e2a3c78df1cc96399aecd", size = 126758 }, +] + +[package.optional-dependencies] +pyqt6 = [ + { name = "pyqt6" }, +] + [[package]] name = "markupsafe" version = "3.0.2" @@ -925,6 +961,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, ] +[[package]] +name = "psygnal" +version = "0.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/b0/194cfbcb76dbf9c4a1c4271ccc825b38406d20fb7f95fd18320c28708800/psygnal-0.11.1.tar.gz", hash = "sha256:f9b02ca246ab0adb108c4010b4a486e464f940543201074591e50370cd7b0cc0", size = 102103 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/a8/ed06fe70c8bd03f02ab0c1df020f53f079a6dbae056eba0a91823c0d1242/psygnal-0.11.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:3c04baec10f882cdf784a7312e23892416188417ad85607e6d1de2e8a9e70709", size = 427499 }, + { url = "https://files.pythonhosted.org/packages/25/92/6dcab17c3bb91fa3f250ebdbb66de55332436da836c4c547c26e3942877e/psygnal-0.11.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:8f77317cbd11fbed5bfdd40ea41b4e551ee0cf37881cdbc325b67322af577485", size = 453373 }, + { url = "https://files.pythonhosted.org/packages/84/6f/868f1d7d22c76b96e0c8a75f8eb196deaff83916ad2da7bd78d1d0f6a5df/psygnal-0.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24e69ea57ee39e3677298f38a18828af87cdc0bf0aa64685d44259e608bae3ec", size = 717571 }, + { url = "https://files.pythonhosted.org/packages/da/7d/24ca61d177b26e6ab89e9c520dca9c6341083920ab0ea8ac763a31b2b029/psygnal-0.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d77f1a71fe9859c0335c87d92afe1b17c520a4137326810e94351839342d8fc7", size = 695336 }, + { url = "https://files.pythonhosted.org/packages/33/5d/9b2d8f91a9198dda6ad0eaa276f975207b1314ac2d22a2f905f0a6e34524/psygnal-0.11.1-cp312-cp312-macosx_10_16_arm64.whl", hash = "sha256:0b55cb42e468f3a7de75392520778604fef2bc518b7df36c639b35ce4ed92016", size = 425244 }, + { url = "https://files.pythonhosted.org/packages/c4/66/e1bd57a8efef6582141939876d014f86792adbbb8853bd475a1cbf3649ca/psygnal-0.11.1-cp312-cp312-macosx_10_16_x86_64.whl", hash = "sha256:c7dd3cf809c9c1127d90c6b11fbbd1eb2d66d512ccd4d5cab048786f13d11220", size = 444681 }, + { url = "https://files.pythonhosted.org/packages/49/ad/8ee3f8ac1d59cf269ae2d55f7cac7c65fe3b3f41cada5d6a17bc2f4c5d6d/psygnal-0.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:885922a6e65ece9ff8ccf2b6810f435ca8067f410889f7a8fffb6b0d61421a0d", size = 743785 }, + { url = "https://files.pythonhosted.org/packages/14/54/b29b854dff0e27bdaf42a7c1edc65f6d3ea35866e9d9250f1dbabf6381a0/psygnal-0.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1c2388360a9ffcd1381e9b36d0f794287a270d58e69bf17658a194bbf86685c1", size = 725134 }, + { url = "https://files.pythonhosted.org/packages/68/76/d5c5bf5a932ec2dcdc4a23565815a1cc5fd96b03b26ff3f647cdff5ea62c/psygnal-0.11.1-py3-none-any.whl", hash = "sha256:04255fe28828060a80320f8fda937c47bc0c21ca14f55a13eb7c494b165ea395", size = 76998 }, +] + +[[package]] +name = "pyconify" +version = "0.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/21/8b703dd994e4833dcfa611536485ce2096c215aa74cbf610c2ff51a7dea8/pyconify-0.1.6.tar.gz", hash = "sha256:25272f7a29965f32ee698c9da6aab8bbbeb0756ca3bfeadd3d9effab689d239d", size = 22236 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/fc/be68d2acc43d22dbfe6d52d573dc83da14e4fd45821acaf42992e8803682/pyconify-0.1.6-py3-none-any.whl", hash = "sha256:63040836b344ec351c05531231609edd30425c145ff68478b6250f33135b8fc5", size = 18929 }, +] + [[package]] name = "pydantic" version = "2.9.2" @@ -1022,6 +1087,60 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, ] +[[package]] +name = "pyqt6" +version = "6.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyqt6-qt6" }, + { name = "pyqt6-sip" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/0a/accbebed526158ab2aedd5c84d238159754bd99f481082b3fe7f374c6a3b/PyQt6-6.8.0.tar.gz", hash = "sha256:6d8628de4c2a050f0b74462e4c9cb97f839bf6ffabbca91711722ffb281570d9", size = 1061357 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/ae/30f6f40c8cdf3d5319ec7add820c6edf96d8ed2461984fd548af9e2d9b51/PyQt6-6.8.0-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:8c5c05f5fdff31a5887dbc29b27615b09df467631238d7b449283809ffca6228", size = 12104917 }, + { url = "https://files.pythonhosted.org/packages/b4/7a/a3ecac3327345dde8f34e781d6db1339e4e3b8411ac8ae9747822a0d44e9/PyQt6-6.8.0-cp39-abi3-manylinux_2_35_x86_64.whl", hash = "sha256:452bae5840077bf0f146c798d7777f70d7bdd0c7dcfa9ee7a415c1daf2d10038", size = 8322144 }, + { url = "https://files.pythonhosted.org/packages/d0/52/1e60e38d216c232af83ac16bedf1ece419812368220485fc25af80c7a5d7/PyQt6-6.8.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:cf7123caea14e7ecf10bd12cae48e8d9970ef7caf627bc7d7988b0baa209adb3", size = 8101586 }, + { url = "https://files.pythonhosted.org/packages/eb/73/9bae1b8daedf2acff5a3668eae84783720ff513e402b0448a906ab3d5479/PyQt6-6.8.0-cp39-abi3-win_amd64.whl", hash = "sha256:a9913d479f1ffee804bf7f232079baea4fb4b221a8f4890117588917a54ea30d", size = 6661265 }, + { url = "https://files.pythonhosted.org/packages/23/a0/f564279285ed92b4fe38ed7b2a8fcddab414512391088b6a0f67a1107f24/PyQt6-6.8.0-cp39-abi3-win_arm64.whl", hash = "sha256:48bace7b87676bba5e6114482f3a20ca20be90c7f261b5d340464313f5f2bf5e", size = 5409032 }, +] + +[[package]] +name = "pyqt6-qt6" +version = "6.8.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/0a/c47a1cc272b418faff8af79b121f0cecd32b09d634253254e3a990432220/PyQt6_Qt6-6.8.1-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:1eb8460a1fdb38d0b2458c2974c01d471c1e59e4eb19ea63fc447aaba3ad530e", size = 65754939 }, + { url = "https://files.pythonhosted.org/packages/b1/e6/cc4fbc97a7d0955185e33add3ce00480f0023424d17ac6f864a504f60251/PyQt6_Qt6-6.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9f3790c4ce4dc576e48b8718d55fb8743057e6cbd53a6ca1dd253ffbac9b7287", size = 59956028 }, + { url = "https://files.pythonhosted.org/packages/01/22/c2997fe76d765d9ba960e9a099238cb419a316362bdde50fedacc23e7c7d/PyQt6_Qt6-6.8.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:d6ca5d2b9d2ec0ee4a814b2175f641a5c4299cb80b45e0f5f8356632663f89b3", size = 72561636 }, + { url = "https://files.pythonhosted.org/packages/a1/24/907c7451901e56e5b713d70c6f65191d80a3a3caac1095fea7f2a4b0b25b/PyQt6_Qt6-6.8.1-py3-none-manylinux_2_35_x86_64.whl", hash = "sha256:08065d595f1e6fc2dde9f4450eeff89082f4bad26f600a8e9b9cc5966716bfcf", size = 72573711 }, + { url = "https://files.pythonhosted.org/packages/2b/a8/d942bb9f83ad4abd4c296cd4bbf752d1147eaa14523cc132a2b8511a3960/PyQt6_Qt6-6.8.1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:20843cb86bd94942d1cd99e39bf1aeabb875b241a35a8ab273e4bbbfa63776db", size = 71077917 }, + { url = "https://files.pythonhosted.org/packages/ab/2c/0d45a74ea8eedf5a2a5a2628396fb2e26107ae07f6c73d6d22c7fa3d4403/PyQt6_Qt6-6.8.1-py3-none-win_amd64.whl", hash = "sha256:006d786693d0511fbcf184a862edbd339c6ed1bb3bd9de363d73a19ed4b23dff", size = 71647964 }, + { url = "https://files.pythonhosted.org/packages/c9/0d/b86b64f0ac5e08b1bd18edac9e949897a4626196725e9a86af4e9f628c80/PyQt6_Qt6-6.8.1-py3-none-win_arm64.whl", hash = "sha256:a8bc2ed4ee5e7c6ff4dd1c7db0b27705d151fee5dc232bbd1bf17618f937f515", size = 47836492 }, +] + +[[package]] +name = "pyqt6-sip" +version = "13.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/e4/f39ca1fd6de7d4823d964a3ec33e85b6f51a9c2a7a1e95956b7a92c8acc9/pyqt6_sip-13.9.1.tar.gz", hash = "sha256:15be741d1ae8c82bb7afe9a61f3cf8c50457f7d61229a1c39c24cd6e8f4d86dc", size = 92358 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/07/342ea1c95367d714eb86ae4b2159798347b80535dcba6c7f60256fc7c5e5/PyQt6_sip-13.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:57b5312ef13c1766bdf69b317041140b184eb24a51e1e23ce8fc5386ba8dffb2", size = 110717 }, + { url = "https://files.pythonhosted.org/packages/89/f2/13159c98929d2dec84cb98021a8de9e66e9429ebe44be08476779130af25/PyQt6_sip-13.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5004514b08b045ad76425cf3618187091a668d972b017677b1b4b193379ef553", size = 316880 }, + { url = "https://files.pythonhosted.org/packages/76/92/47fc35401d7f192bd690ed3552242909c6e77db5a97d8b2d980a948400fe/PyQt6_sip-13.9.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:accab6974b2758296400120fdcc9d1f37785b2ea2591f00656e1776f058ded6c", size = 294518 }, + { url = "https://files.pythonhosted.org/packages/39/7c/3c775c219d9c17bda783e1dbab1d16f09f7713b93920f761d2cc61aa3ad0/PyQt6_sip-13.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:1ec52e962f54137a19208b6e95b6bd9f7a403eb25d7237768a99306cd9db26d1", size = 53368 }, + { url = "https://files.pythonhosted.org/packages/81/bf/0700d0f5832e6e54ec56dbbdc912c63662ae54d1571f3241377b5acb37fa/PyQt6_sip-13.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:6e6c1e2592187934f4e790c0c099d0033e986dcef7bdd3c06e3895ffa995e9fc", size = 45013 }, + { url = "https://files.pythonhosted.org/packages/34/19/6df209c4f1a3fbae0a90c8f37b0205ce42d61d8d762e4a539fceeabee340/PyQt6_sip-13.9.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1fb405615970e85b622b13b4cad140ff1e4182eb8334a0b27a4698e6217b89b0", size = 112248 }, + { url = "https://files.pythonhosted.org/packages/6b/81/9df86e90a8dd1f6f5faa1199af03e208f5dd3193e462e541e276001d098c/PyQt6_sip-13.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c800db3464481e87b1d2b84523b075df1e8fc7856c6f9623dc243f89be1cb604", size = 322310 }, + { url = "https://files.pythonhosted.org/packages/dd/4d/e263f045c97c2398fbd762d07295140b628a611da5f37744324080a86a1d/PyQt6_sip-13.9.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c1942e107b0243ced9e510d507e0f27aeea9d6b13e0a1b7c06fd52a62e0d41f7", size = 303638 }, + { url = "https://files.pythonhosted.org/packages/28/9f/047a3b28265b39ff151981da84fde7895b87b94131db4b044a6125df9a4a/PyQt6_sip-13.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:552ff8fdc41f5769d3eccc661f022ed496f55f6e0a214c20aaf56e56385d61b6", size = 53498 }, + { url = "https://files.pythonhosted.org/packages/92/af/17a92b67db31a086bd693bf97d98aa2f45427e7982df53f59cdc26899979/PyQt6_sip-13.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:976c7758f668806d4df7a8853f390ac123d5d1f73591ed368bdb8963574ff589", size = 45335 }, + { url = "https://files.pythonhosted.org/packages/4b/10/43eab7a9cd26f3ba81220f2ec7a7c471c232df738dd5787affcc00d75aa9/PyQt6_sip-13.9.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:56ce0afb19cd8a8c63ff93ae506dffb74f844b88adaa6673ebc0dec43af48a76", size = 112278 }, + { url = "https://files.pythonhosted.org/packages/31/ba/b0cb5ad4d44265213d50b8e7b24a9c1605baf242019e51104dd87499d68f/PyQt6_sip-13.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d7726556d1ca7a7ed78e19ba53285b64a2a8f6ad7ff4cb18a1832efca1a3102", size = 322531 }, + { url = "https://files.pythonhosted.org/packages/ba/73/556391d6115e87972d8c88b757b4db614af4fb8579ce28030965fe793fe7/PyQt6_sip-13.9.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14f95c6352e3b85dc26bf59cfbf77a470ecbd5fcdcf00af4b648f0e1b9eefb9e", size = 303760 }, + { url = "https://files.pythonhosted.org/packages/d8/5b/7321bb2564673bc4facfed338553bc42542f2ae05cbea1c8b57a596ab28d/PyQt6_sip-13.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:3c269052c770c09b61fce2f2f9ea934a67dfc65f443d59629b4ccc8f89751890", size = 53509 }, + { url = "https://files.pythonhosted.org/packages/cf/b6/eabe1d0fdcc56c80325c5a2cc361ae3e57b3346cb7700b707481d3977a24/PyQt6_sip-13.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:8b2ac36d6e04db6099614b9c1178a2f87788c7ffc3826571fb63d36ddb4c401d", size = 45358 }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -1087,6 +1206,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, ] +[[package]] +name = "qtpy" +version = "2.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e5/10/51e0e50dd1e4a160c54ac0717b8ff11b2063d441e721c2037f61931cf38d/qtpy-2.4.2.tar.gz", hash = "sha256:9d6ec91a587cc1495eaebd23130f7619afa5cdd34a277acb87735b4ad7c65156", size = 66849 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/0c/58a1e48209b0b1220ca2368435573f39ff1fa3577b7eef913f8960c5d429/QtPy-2.4.2-py3-none-any.whl", hash = "sha256:5a696b1dd7a354cb330657da1d17c20c2190c72d4888ba923f8461da67aa1a1c", size = 95155 }, +] + [[package]] name = "referencing" version = "0.35.1" @@ -1211,6 +1342,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/86/29/88c2567bc893c84d88b4c48027367c3562ae69121d568e8a3f3a8d363f4d/ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52", size = 703012 }, { url = "https://files.pythonhosted.org/packages/11/46/879763c619b5470820f0cd6ca97d134771e502776bc2b844d2adb6e37753/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642", size = 704352 }, { url = "https://files.pythonhosted.org/packages/02/80/ece7e6034256a4186bbe50dee28cd032d816974941a6abf6a9d65e4228a7/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2", size = 737344 }, + { url = "https://files.pythonhosted.org/packages/f0/ca/e4106ac7e80efbabdf4bf91d3d32fc424e41418458251712f5672eada9ce/ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3", size = 714498 }, { url = "https://files.pythonhosted.org/packages/67/58/b1f60a1d591b771298ffa0428237afb092c7f29ae23bad93420b1eb10703/ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4", size = 100205 }, { url = "https://files.pythonhosted.org/packages/b4/4f/b52f634c9548a9291a70dfce26ca7ebce388235c93588a1068028ea23fcc/ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb", size = 118185 }, { url = "https://files.pythonhosted.org/packages/48/41/e7a405afbdc26af961678474a55373e1b323605a4f5e2ddd4a80ea80f628/ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632", size = 133433 }, @@ -1219,6 +1351,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/52/a9/d39f3c5ada0a3bb2870d7db41901125dbe2434fa4f12ca8c5b83a42d7c53/ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd", size = 706497 }, { url = "https://files.pythonhosted.org/packages/b0/fa/097e38135dadd9ac25aecf2a54be17ddf6e4c23e43d538492a90ab3d71c6/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31", size = 698042 }, { url = "https://files.pythonhosted.org/packages/ec/d5/a659ca6f503b9379b930f13bc6b130c9f176469b73b9834296822a83a132/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680", size = 745831 }, + { url = "https://files.pythonhosted.org/packages/db/5d/36619b61ffa2429eeaefaab4f3374666adf36ad8ac6330d855848d7d36fd/ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d", size = 715692 }, { url = "https://files.pythonhosted.org/packages/b1/82/85cb92f15a4231c89b95dfe08b09eb6adca929ef7df7e17ab59902b6f589/ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5", size = 98777 }, { url = "https://files.pythonhosted.org/packages/d7/8f/c3654f6f1ddb75daf3922c3d8fc6005b1ab56671ad56ffb874d908bfa668/ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4", size = 115523 }, { url = "https://files.pythonhosted.org/packages/29/00/4864119668d71a5fa45678f380b5923ff410701565821925c69780356ffa/ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a", size = 132011 }, @@ -1227,6 +1360,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e2/a9/28f60726d29dfc01b8decdb385de4ced2ced9faeb37a847bd5cf26836815/ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6", size = 701785 }, { url = "https://files.pythonhosted.org/packages/84/7e/8e7ec45920daa7f76046578e4f677a3215fe8f18ee30a9cb7627a19d9b4c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf", size = 693017 }, { url = "https://files.pythonhosted.org/packages/c5/b3/d650eaade4ca225f02a648321e1ab835b9d361c60d51150bac49063b83fa/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1", size = 741270 }, + { url = "https://files.pythonhosted.org/packages/87/b8/01c29b924dcbbed75cc45b30c30d565d763b9c4d540545a0eeecffb8f09c/ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01", size = 709059 }, { url = "https://files.pythonhosted.org/packages/30/8c/ed73f047a73638257aa9377ad356bea4d96125b305c34a28766f4445cc0f/ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6", size = 98583 }, { url = "https://files.pythonhosted.org/packages/b0/85/e8e751d8791564dd333d5d9a4eab0a7a115f7e349595417fd50ecae3395c/ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3", size = 115190 }, ] @@ -1538,6 +1672,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, ] +[[package]] +name = "superqt" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, + { name = "qtpy" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/7b/10b854842f76b20fc8a13ea5b62f78346c84bf9664b7d54ee82e53d88b6d/superqt-0.7.0.tar.gz", hash = "sha256:fa8014a0ffead7f618a85346ac355ba5e9529b177f50caf5093edeec566c6f5d", size = 97558 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/77/47289ce2c607f72446811230ec96406ace44db7669e443793a13fd7b3486/superqt-0.7.0-py3-none-any.whl", hash = "sha256:395d3f511fe5143882eba6e9ffb8d79bd4c782b6d571c1edb4141cea977612d3", size = 91179 }, +] + +[package.optional-dependencies] +iconify = [ + { name = "pyconify" }, +] + [[package]] name = "threadpoolctl" version = "3.5.0" From 94d6d17bb1dd0637a660a8c3e654e00bdc30094b Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Wed, 25 Dec 2024 22:21:55 +0000 Subject: [PATCH 02/16] Add magicgui wrapper to generate forms from pydantic models --- .../magicgui.py | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 src/aind_behavior_experiment_launcher/magicgui.py diff --git a/src/aind_behavior_experiment_launcher/magicgui.py b/src/aind_behavior_experiment_launcher/magicgui.py new file mode 100644 index 00000000..bbf64d72 --- /dev/null +++ b/src/aind_behavior_experiment_launcher/magicgui.py @@ -0,0 +1,194 @@ +from __future__ import annotations + +import importlib.util + +if importlib.util.find_spec("magicgui") is None: + raise ImportError( + "The 'magicgui' package is required to use this module. " + "Install the optional dependencies defined in `project.toml` " + "by running `pip install .[magicgui]`" + ) + + +from functools import partial +from typing import Any, Iterable, Optional, Type + +from magicgui import event_loop +from magicgui.types import Undefined +from magicgui.widgets import ( + Button, + CheckBox, + Container, + Textarea, + create_widget, +) +from magicgui.widgets.bases import ValueWidget +from pydantic import BaseModel, TypeAdapter, ValidationError +from pydantic.fields import FieldInfo, PydanticUndefined + + +class _ValidationErrorSink: + def __init__(self, widget: Optional[ValueWidget] = None): + self._widget = widget + self._error: Optional[Exception] = None + self.clear() + + def __call__(self, e: Exception | None): + self.update(e) + + @property + def widget(self) -> Optional[ValueWidget]: + return self._widget + + @property + def error(self) -> Optional[Exception]: + return self._error + + def update(self, e: Exception | None): + self._error = e + if self._widget is not None: + self._widget.value = f"{self.error}" + + def clear(self): + self.update(None) + + def register_widget(self, widget: ValueWidget): + self._widget = widget + + +def _container_updater(container, validator, error_sink: Optional[_ValidationErrorSink] = None): + # https://icon-sets.iconify.design/material-symbols/ + + null_checkbox: CheckBox = container[0] + input_widget: ValueWidget = container[1] + validation_button: Button = container[2] + if error_sink is None: + error_sink = _ValidationErrorSink() + + is_null = null_checkbox.value + input_widget.enabled = not is_null + try: + if is_null: + value = None + else: + value = input_widget.value + + value = validator(value) + if value is not None: + # in case the type adapter does something fancy + input_widget.value = value + + validation_button.set_icon("material-symbols:check-circle-outline-rounded", "#0b7500") + error_sink.clear() + except ValidationError as e: + validation_button.set_icon("material-symbols:cancel", "#fd0000") + error_sink(e) + + +def _make_widget_from_field( + field: FieldInfo, + name: str, + default: Any = Undefined, + *, + override_type: Optional[dict[str, Type[ValueWidget]]] = None, + validation_error_sink: Optional[_ValidationErrorSink] = None, +) -> Container: + # Each field will be rendered as: + # - Left button for nullability + # - Value input + # - Right button for validation + + if override_type is None: + override_type = {} + widget_type = override_type.get(name, None) + + if default is Undefined: + default = field.get_default(call_default_factory=True) + if default is PydanticUndefined or default is None: + default = Undefined + + container: Container = Container(layout="horizontal") + + container.append( + CheckBox( + value=(default is Undefined), + label="N", + ) + ) + widget = create_widget(default, field.annotation, name=name, widget_type=widget_type) + container.append(widget) + container.append(Button(icon="material-symbols:check-circle-outline-rounded", icon_color="#0b7500", enabled=True)) + container.tooltip = field.description + validator = TypeAdapter(field.rebuild_annotation()).validate_python + updater = partial(_container_updater, validator=validator, error_sink=validation_error_sink) + container.changed.connect(updater) + updater(container) + return container + + +def create_container_from_model( + model: Type[BaseModel] | BaseModel, + *, + fields: Optional[Iterable[str]] = None, + override_type: Optional[dict[str, Type[ValueWidget]]] = None, + validation_error_sink: Optional[_ValidationErrorSink] = None, + populate_with_instance: bool = False, +) -> "Container": + if fields is None: + fields = model.model_fields.keys() + + if populate_with_instance: + if not isinstance(model, BaseModel): + raise ValueError("Cannot populate with instance if model is not an instance") + + widgets = [ + _make_widget_from_field( + field, + name, + getattr(model, name) if populate_with_instance else Undefined, + override_type=override_type, + validation_error_sink=validation_error_sink, + ) + for name, field in model.model_fields.items() + if name in fields + ] + + container = Container(widgets=widgets) + return container + + +def create_form( + model: BaseModel | Type[BaseModel], + *, + fields: Optional[Iterable[str]] = None, + populate_with_instance: bool = False, + allow_errors: bool = False, +) -> dict[str, Any]: + submit_button = Button(text="Submit") + error_stack = Textarea(enabled=False) + error_sink = _ValidationErrorSink(error_stack) + model_widget = create_container_from_model( + model, fields=fields, validation_error_sink=error_sink, populate_with_instance=populate_with_instance + ) + container = Container( + widgets=[ + submit_button, + model_widget, + error_stack, + ] + ) + submit_button.changed.connect(container.close) + if isinstance(model, BaseModel): + container.native.setWindowTitle(type(model).__name__) + else: + container.native.setWindowTitle(model) + with event_loop(): + container.show() + + # TODO + # This is a big hack, but seems like investing + # too much time into an event driven solution is not worth it atm + if not allow_errors and error_sink.error is not None: + raise error_sink.error + + return {getattr(widget[1], "name"): getattr(widget[1], "value") for widget in model_widget} From d5c370789bbe1899d09372c38e769645e173b870 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Wed, 25 Dec 2024 22:22:04 +0000 Subject: [PATCH 03/16] Improve import check syntax --- .../data_mapper/aind_data_schema.py | 9 ++++----- .../data_transfer/aind_watchdog.py | 12 ++++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/aind_behavior_experiment_launcher/data_mapper/aind_data_schema.py b/src/aind_behavior_experiment_launcher/data_mapper/aind_data_schema.py index 617a1e96..0b390ffb 100644 --- a/src/aind_behavior_experiment_launcher/data_mapper/aind_data_schema.py +++ b/src/aind_behavior_experiment_launcher/data_mapper/aind_data_schema.py @@ -1,14 +1,13 @@ from __future__ import annotations -try: - import aind_data_schema -except ImportError as _e: - _e.add_note( +import importlib.util + +if importlib.util.find_spec("aind_data_schema") is None: + raise ImportError( "The 'aind-data-schema' package is required to use this module. \ Install the optional dependencies defined in `project.toml' \ by running `pip install .[aind-services]`" ) - raise _e import abc import logging diff --git a/src/aind_behavior_experiment_launcher/data_transfer/aind_watchdog.py b/src/aind_behavior_experiment_launcher/data_transfer/aind_watchdog.py index d552e22a..5a3b2136 100644 --- a/src/aind_behavior_experiment_launcher/data_transfer/aind_watchdog.py +++ b/src/aind_behavior_experiment_launcher/data_transfer/aind_watchdog.py @@ -1,12 +1,12 @@ -try: - import aind_watchdog_service # noqa: F401 -except ImportError as e: - e.add_note( - "The 'aind-watchdog-service' package is required to use this module. \ +import importlib.util + +if importlib.util.find_spec("aind_watchdog_service") is None: + raise ImportError( + "The 'aind_watchdog_service' package is required to use this module. \ Install the optional dependencies defined in `project.toml' \ by running `pip install .[aind-services]`" ) - raise + import datetime import json From dcf5f4c2096c20e839126134a87b65fe3e9525f6 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Thu, 26 Dec 2024 09:28:29 +0000 Subject: [PATCH 04/16] Add ability to optionally exclude fields --- .../magicgui.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/aind_behavior_experiment_launcher/magicgui.py b/src/aind_behavior_experiment_launcher/magicgui.py index bbf64d72..87517bfb 100644 --- a/src/aind_behavior_experiment_launcher/magicgui.py +++ b/src/aind_behavior_experiment_launcher/magicgui.py @@ -129,13 +129,16 @@ def _make_widget_from_field( def create_container_from_model( model: Type[BaseModel] | BaseModel, *, - fields: Optional[Iterable[str]] = None, + include_fields: Optional[Iterable[str]] = None, + exclude_fields: Optional[Iterable[str]] = None, override_type: Optional[dict[str, Type[ValueWidget]]] = None, validation_error_sink: Optional[_ValidationErrorSink] = None, populate_with_instance: bool = False, ) -> "Container": - if fields is None: - fields = model.model_fields.keys() + if include_fields is None: + include_fields = model.model_fields.keys() + if exclude_fields is None: + exclude_fields = [] if populate_with_instance: if not isinstance(model, BaseModel): @@ -150,7 +153,7 @@ def create_container_from_model( validation_error_sink=validation_error_sink, ) for name, field in model.model_fields.items() - if name in fields + if (name in include_fields) and (name not in exclude_fields) ] container = Container(widgets=widgets) @@ -160,7 +163,8 @@ def create_container_from_model( def create_form( model: BaseModel | Type[BaseModel], *, - fields: Optional[Iterable[str]] = None, + include_fields: Optional[Iterable[str]] = None, + exclude_fields: Optional[Iterable[str]] = None, populate_with_instance: bool = False, allow_errors: bool = False, ) -> dict[str, Any]: @@ -168,7 +172,11 @@ def create_form( error_stack = Textarea(enabled=False) error_sink = _ValidationErrorSink(error_stack) model_widget = create_container_from_model( - model, fields=fields, validation_error_sink=error_sink, populate_with_instance=populate_with_instance + model, + include_fields=include_fields, + exclude_fields=exclude_fields, + validation_error_sink=error_sink, + populate_with_instance=populate_with_instance, ) container = Container( widgets=[ From 39512ccb531a83618f285da5d1d9fe22cca8fd31 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Thu, 26 Dec 2024 09:42:49 +0000 Subject: [PATCH 05/16] Ensure set is done outside the update loop context --- src/aind_behavior_experiment_launcher/magicgui.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/aind_behavior_experiment_launcher/magicgui.py b/src/aind_behavior_experiment_launcher/magicgui.py index 87517bfb..2a2fcb1c 100644 --- a/src/aind_behavior_experiment_launcher/magicgui.py +++ b/src/aind_behavior_experiment_launcher/magicgui.py @@ -76,7 +76,8 @@ def _container_updater(container, validator, error_sink: Optional[_ValidationErr value = validator(value) if value is not None: # in case the type adapter does something fancy - input_widget.value = value + with input_widget.changed.blocked(): + input_widget.set_value(value) validation_button.set_icon("material-symbols:check-circle-outline-rounded", "#0b7500") error_sink.clear() @@ -187,9 +188,9 @@ def create_form( ) submit_button.changed.connect(container.close) if isinstance(model, BaseModel): - container.native.setWindowTitle(type(model).__name__) + container.native.setWindowTitle(model.__class__.__name__) else: - container.native.setWindowTitle(model) + container.native.setWindowTitle(model.__name__) with event_loop(): container.show() From 56420da4ee736a43bb5a471e69d1f606b0b5f4d1 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Thu, 26 Dec 2024 10:54:58 +0000 Subject: [PATCH 06/16] Block emissions of events when running ui element logic --- .../magicgui.py | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/aind_behavior_experiment_launcher/magicgui.py b/src/aind_behavior_experiment_launcher/magicgui.py index 2a2fcb1c..11756553 100644 --- a/src/aind_behavior_experiment_launcher/magicgui.py +++ b/src/aind_behavior_experiment_launcher/magicgui.py @@ -66,24 +66,24 @@ def _container_updater(container, validator, error_sink: Optional[_ValidationErr error_sink = _ValidationErrorSink() is_null = null_checkbox.value - input_widget.enabled = not is_null - try: - if is_null: - value = None - else: - value = input_widget.value - - value = validator(value) - if value is not None: - # in case the type adapter does something fancy - with input_widget.changed.blocked(): + with input_widget.changed.blocked(): + input_widget.enabled = not is_null + try: + if is_null: + value = None + else: + value = input_widget.value + + value = validator(value) + if value is not None: + # in case the type adapter does something fancy input_widget.set_value(value) - validation_button.set_icon("material-symbols:check-circle-outline-rounded", "#0b7500") - error_sink.clear() - except ValidationError as e: - validation_button.set_icon("material-symbols:cancel", "#fd0000") - error_sink(e) + validation_button.set_icon("material-symbols:check-circle-outline-rounded", "#0b7500") + error_sink.clear() + except ValidationError as e: + validation_button.set_icon("material-symbols:cancel", "#fd0000") + error_sink(e) def _make_widget_from_field( @@ -117,6 +117,7 @@ def _make_widget_from_field( ) ) widget = create_widget(default, field.annotation, name=name, widget_type=widget_type) + container.append(widget) container.append(Button(icon="material-symbols:check-circle-outline-rounded", icon_color="#0b7500", enabled=True)) container.tooltip = field.description From ac2a6ba9a98d9943be070cdf801cdc146320a1b9 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Thu, 26 Dec 2024 19:34:54 +0000 Subject: [PATCH 07/16] Add helper functions with gui backend --- .../gui_helper.py | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 src/aind_behavior_experiment_launcher/gui_helper.py diff --git a/src/aind_behavior_experiment_launcher/gui_helper.py b/src/aind_behavior_experiment_launcher/gui_helper.py new file mode 100644 index 00000000..cad666dc --- /dev/null +++ b/src/aind_behavior_experiment_launcher/gui_helper.py @@ -0,0 +1,144 @@ +from magicgui import widgets as widgets +import magicgui +import magicgui.types +from typing import Optional, TypeVar, List +from magicgui.widgets.bases import BaseValueWidget + +import os +from pathlib import Path + +T = TypeVar("T") + + +def prompt_pick_file_from_list( + available_files: list[os.PathLike[str]], + *, + allow_manual_input: bool = True, + prompt: str = "Select a file:", +) -> Optional[os.PathLike[str]]: + def _force_dst(src: BaseValueWidget, dst: BaseValueWidget): + with dst.changed.blocked(): + print(dst.get_value()) + dst.set_value(Path(src.get_value())) + + container: widgets.Container = widgets.Container(layout="vertical") + + # Add button to submit + submit_button = widgets.Button(text="Submit") + submit_button.changed.connect(container.close) + container.append(submit_button) + + # Make the picker + _paths = [Path(x) for x in available_files] + _path_names = [x.name for x in _paths] + files = widgets.ComboBox( + choices=_path_names, + label=prompt, + ) + container.append(files) + file_picker = widgets.FileEdit( + mode=magicgui.types.FileDialogMode.EXISTING_FILE, + label="Or provide a file path:", + ) + container.append(file_picker) + file_picker.enabled = allow_manual_input + + files.changed.connect(lambda _: _force_dst(files, file_picker)) + file_picker.changed.connect(lambda _: _force_dst(file_picker, files)) + + container.native.setWindowTitle(prompt) + with magicgui.event_loop(): + container.show() + + if files.get_value() is not None: + return _paths[_path_names.index(files.get_value())] + + _p = file_picker.get_value() + if isinstance(_p, tuple): + _p = _p[0] + return _p + + +def prompt_pick_from_list( + values: list[str], + *, + prompt: str = "Select an option:", +) -> Optional[str]: + container: widgets.Container = widgets.Container(layout="vertical") + + # Add button to submit + submit_button = widgets.Button(text="Submit") + submit_button.changed.connect(container.close) + container.append(submit_button) + + # Make the picker + files = widgets.ComboBox( + value=values, + label=prompt, + ) + container.append(files) + file_picker = widgets.FileEdit( + mode=magicgui.types.FileDialogMode.EXISTING_FILE, + label="Or provide a file path:", + ) + container.append(file_picker) + + container.native.setWindowTitle(prompt) + with magicgui.event_loop(): + container.show() + + return files.get_value() + + +def prompt_yes_no_question(*, prompt: str = "Select one option:") -> Optional[bool]: + _container_outer: widgets.Container = widgets.Container(layout="vertical") + _container_inner: widgets.Container = widgets.Container(layout="horizontal") + print(prompt) + _p = widgets.Label(value=prompt) + + _w_yes = widgets.Button(value=True, text="Yes") + _w_no = widgets.Button(value=False, text="No") + + _result: List[Optional[bool]] = [None] + _w_yes.changed.connect(lambda _: _result.__setitem__(0, True)) + _w_no.changed.connect(lambda _: _result.__setitem__(0, False)) + + _w_yes.changed.connect(_container_outer.close) + _w_no.changed.connect(_container_outer.close) + + _container_inner.append(_w_yes) + _container_inner.append(_w_no) + _container_outer.append(_p) + _container_outer.append(_container_inner) + + _container_outer.native.setWindowTitle(prompt) + with magicgui.event_loop(): + _container_outer.show() + + return _result[0] + + +def choose_subject(subject_list: SubjectDataBase) -> Optional[str]: + def _lock_to_index(index_widget: BaseValueWidget, dst: BaseValueWidget): + with dst.changed.blocked(): + dst.set_value(index_widget.get_value()) + + _subject = list(subject_list.subjects.keys()) + _task_logic = [str(getattr(x, "task_logic_target", None)) for x in subject_list.subjects.values()] + _choices = [f"{subject} => {task_logic}" for subject, task_logic in zip(_subject, _task_logic)] + + container = widgets.Container(layout="vertical") + subjects = widgets.Select(choices=_choices, allow_multiple=False, label="Subject") + btn = widgets.Button(text="Submit") + btn.changed.connect(container.close) + + container.append(btn) + container.append(subjects) + + container.show(run=True) + + v = subjects.get_value() + if v is None: + return None + else: + return _subject[_choices.index(v[0])] From 9ee2f70163947f895993599c91dc309129881316 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Fri, 27 Dec 2024 13:57:33 +0000 Subject: [PATCH 08/16] Add uv.lock check --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb5b066d..10564c9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,8 @@ jobs: - name: Set up Python ${{ matrix.python-version }} run: uv python install ${{ matrix.python-version }} + - name: Check if uv.lock is up-to-date + run: uv lock --check - name: Install dependencies run: uv sync --extra dev - name: Run ruff format From 3473bcf6688f96ea7b6dac4226c1e15d765eeb78 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Sun, 29 Dec 2024 09:58:56 +0000 Subject: [PATCH 09/16] Add prompt for experimenter name --- .../gui_helper.py | 52 ++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/src/aind_behavior_experiment_launcher/gui_helper.py b/src/aind_behavior_experiment_launcher/gui_helper.py index cad666dc..b8cb98e1 100644 --- a/src/aind_behavior_experiment_launcher/gui_helper.py +++ b/src/aind_behavior_experiment_launcher/gui_helper.py @@ -1,11 +1,16 @@ -from magicgui import widgets as widgets +import logging +import os +from pathlib import Path +from typing import List, Optional, TypeVar + import magicgui import magicgui.types -from typing import Optional, TypeVar, List +from aind_behavior_services.db_utils import SubjectDataBase +from magicgui import widgets as widgets +from magicgui.types import Undefined, _Undefined from magicgui.widgets.bases import BaseValueWidget -import os -from pathlib import Path +logger = logging.getLogger(__name__) T = TypeVar("T") @@ -127,7 +132,7 @@ def _lock_to_index(index_widget: BaseValueWidget, dst: BaseValueWidget): _task_logic = [str(getattr(x, "task_logic_target", None)) for x in subject_list.subjects.values()] _choices = [f"{subject} => {task_logic}" for subject, task_logic in zip(_subject, _task_logic)] - container = widgets.Container(layout="vertical") + container: widgets.Container = widgets.Container(layout="vertical") subjects = widgets.Select(choices=_choices, allow_multiple=False, label="Subject") btn = widgets.Button(text="Submit") btn.changed.connect(container.close) @@ -135,10 +140,45 @@ def _lock_to_index(index_widget: BaseValueWidget, dst: BaseValueWidget): container.append(btn) container.append(subjects) - container.show(run=True) + with magicgui.event_loop(): + container.show() v = subjects.get_value() if v is None: return None else: return _subject[_choices.index(v[0])] + + +def prompt_experimenter( + *, default: List[str] | _Undefined = Undefined, allow_empty_string: bool = True +) -> Optional[List[str]]: + def _close_if_valid(container: widgets.Container, list_edit: widgets.ListEdit): + if r := list_edit.get_value(): + if len(r) > 0: + container.close() + + if default is Undefined: + default = [""] + + container: widgets.Container = widgets.Container(layout="vertical") + w_experimenter = widgets.ListEdit(value=default) + btn = widgets.Button(text="Submit") + btn.changed.connect(lambda _: _close_if_valid(container, w_experimenter)) + container.append(w_experimenter) + container.append(btn) + + container.native.setWindowTitle("Enter experimenters: ") + with magicgui.event_loop(): + container.show() + + experimenter = w_experimenter.get_value() + if experimenter is None: + return None + experimenter = [x.strip() for x in experimenter if x.strip() != ""] + if len(experimenter) == 0: + if not allow_empty_string: + logger.error("Experimenter name is not valid.") + else: + return None + return experimenter From eae4eb8b9be96a4eed551bc479c7973bc428b190 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:08:20 -0800 Subject: [PATCH 10/16] Remove reliance on ui_helper to builder metadata information --- .../{magicgui.py => _magicgui.py} | 0 .../launcher/_base.py | 12 ++++----- .../ui_helper.py | 25 +++---------------- 3 files changed, 10 insertions(+), 27 deletions(-) rename src/aind_behavior_experiment_launcher/{magicgui.py => _magicgui.py} (100%) diff --git a/src/aind_behavior_experiment_launcher/magicgui.py b/src/aind_behavior_experiment_launcher/_magicgui.py similarity index 100% rename from src/aind_behavior_experiment_launcher/magicgui.py rename to src/aind_behavior_experiment_launcher/_magicgui.py diff --git a/src/aind_behavior_experiment_launcher/launcher/_base.py b/src/aind_behavior_experiment_launcher/launcher/_base.py index ece95b90..5f99430a 100644 --- a/src/aind_behavior_experiment_launcher/launcher/_base.py +++ b/src/aind_behavior_experiment_launcher/launcher/_base.py @@ -179,13 +179,13 @@ def main(self) -> None: return def _ui_prompt(self) -> Self: - logger.info( - self._ui_helper.make_header( - task_logic_schema_model=self.task_logic_schema_model, - rig_schema_model=self.rig_schema_model, - session_schema_model=self.session_schema_model, - ) + self._ui_helper.make_header() + _str = ( + f"Rig ({self.rig_schema_model.__name__}) Schema Version: {self.rig_schema_model.model_construct().version}\n" + f"Session ({self.session_schema_model.__name__}) Schema Version: {self.session_schema_model.model_construct().version}\n" + f"TaskLogic ({self.task_logic_schema_model.__name__}) Schema Version: {self.task_logic_schema_model.model_construct().version}\n" ) + logger.info(_str) if self._debug_mode: self._print_diagnosis() diff --git a/src/aind_behavior_experiment_launcher/ui_helper.py b/src/aind_behavior_experiment_launcher/ui_helper.py index 2bdf6bb7..a4ffca77 100644 --- a/src/aind_behavior_experiment_launcher/ui_helper.py +++ b/src/aind_behavior_experiment_launcher/ui_helper.py @@ -3,9 +3,6 @@ from typing import Any, Callable, List, Optional, TypeVar from aind_behavior_services.db_utils import SubjectDataBase -from aind_behavior_services.rig import AindBehaviorRigModel -from aind_behavior_services.session import AindBehaviorSessionModel -from aind_behavior_services.task_logic import AindBehaviorTaskLogicModel from pydantic import BaseModel, TypeAdapter logger = logging.getLogger(__name__) @@ -88,35 +85,21 @@ def prompt_get_notes() -> str: notes = str(input("Enter notes:")) return notes - def make_header( - self, - task_logic_schema_model: type[AindBehaviorTaskLogicModel], - rig_schema_model: type[AindBehaviorRigModel], - session_schema_model: type[AindBehaviorSessionModel], - ) -> str: + def make_header(self) -> None: _HEADER = r""" - ██████╗██╗ █████╗ ██████╗ ███████╗ + ██████╗██╗ █████╗ ██████╗ ███████╗ ██╔════╝██║ ██╔══██╗██╔══██╗██╔════╝ ██║ ██║ ███████║██████╔╝█████╗ ██║ ██║ ██╔══██║██╔══██╗██╔══╝ ╚██████╗███████╗██║ ██║██████╔╝███████╗ - ╚═════╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚══════╝ + ╚═════╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚══════╝ Command-line-interface Launcher for AIND Behavior Experiments Press Control+C to exit at any time. """ - _str = ( - "-------------------------------\n" - f"{_HEADER}\n" - f"TaskLogic ({task_logic_schema_model.__name__}) Schema Version: {task_logic_schema_model.model_construct().version}\n" - f"Rig ({rig_schema_model.__name__}) Schema Version: {rig_schema_model.model_construct().version}\n" - f"Session ({session_schema_model.__name__}) Schema Version: {session_schema_model.model_construct().version}\n" - "-------------------------------" - ) - - return _str + self._print(_HEADER) TModel = TypeVar("TModel", bound=BaseModel) From abf5b3b17c848c49b401ddf3657c2277270e0a1b Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:08:41 -0800 Subject: [PATCH 11/16] Add notes and header ui elements --- .../gui_helper.py | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/aind_behavior_experiment_launcher/gui_helper.py b/src/aind_behavior_experiment_launcher/gui_helper.py index b8cb98e1..011dae74 100644 --- a/src/aind_behavior_experiment_launcher/gui_helper.py +++ b/src/aind_behavior_experiment_launcher/gui_helper.py @@ -9,6 +9,7 @@ from magicgui import widgets as widgets from magicgui.types import Undefined, _Undefined from magicgui.widgets.bases import BaseValueWidget +from qtpy.QtCore import Qt logger = logging.getLogger(__name__) @@ -98,7 +99,6 @@ def prompt_pick_from_list( def prompt_yes_no_question(*, prompt: str = "Select one option:") -> Optional[bool]: _container_outer: widgets.Container = widgets.Container(layout="vertical") _container_inner: widgets.Container = widgets.Container(layout="horizontal") - print(prompt) _p = widgets.Label(value=prompt) _w_yes = widgets.Button(value=True, text="Yes") @@ -182,3 +182,56 @@ def _close_if_valid(container: widgets.Container, list_edit: widgets.ListEdit): else: return None return experimenter + + +def prompt_get_notes() -> str: + container: widgets.Container = widgets.Container(layout="vertical") + + # Add button to submit + submit_button = widgets.Button(text="Submit") + submit_button.changed.connect(container.close) + container.append(submit_button) + + # Make the picker + notes_ui = widgets.TextEdit() + container.append(notes_ui) + container.native.setWindowTitle("Enter notes:") + + with magicgui.event_loop(): + container.show() + + return notes_ui.get_value() + + +def make_header() -> None: + _HEADER = """\n + ██████╗██╗ █████╗ ██████╗ ███████╗ + ██╔════╝██║ ██╔══██╗██╔══██╗██╔════╝ + ██║ ██║ ███████║██████╔╝█████╗ + ██║ ██║ ██╔══██║██╔══██╗██╔══╝ + ╚██████╗███████╗██║ ██║██████╔╝███████╗ + ╚═════╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚══════╝ + """ + + sub_text = """ + Command-line-interface Launcher for AIND Behavior Experiments + Press Control+C to exit at any time. + """ + + container = widgets.Container() + text_ui = widgets.Label(value=_HEADER) + text_ui.native.setTextFormat(Qt.TextFormat.MarkdownText) + font = text_ui.native.font() + font.setPointSize(15) + font.setFamily("Courier New") + text_ui.native.setFont(font) + sub_text_ui = widgets.Label(value=sub_text) + ok_ui = widgets.Button(text="Close") + container.append(text_ui) + container.append(sub_text_ui) + container.append(ok_ui) + ok_ui.clicked.connect(container.close) + + container.native.setWindowTitle("CLABE") + with magicgui.event_loop(): + container.show() From c14e96ccf98dd310fd14633b9297dcfa24f963bd Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:52:25 -0800 Subject: [PATCH 12/16] Remove print statement --- src/aind_behavior_experiment_launcher/gui_helper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/aind_behavior_experiment_launcher/gui_helper.py b/src/aind_behavior_experiment_launcher/gui_helper.py index 011dae74..8e29d341 100644 --- a/src/aind_behavior_experiment_launcher/gui_helper.py +++ b/src/aind_behavior_experiment_launcher/gui_helper.py @@ -24,7 +24,6 @@ def prompt_pick_file_from_list( ) -> Optional[os.PathLike[str]]: def _force_dst(src: BaseValueWidget, dst: BaseValueWidget): with dst.changed.blocked(): - print(dst.get_value()) dst.set_value(Path(src.get_value())) container: widgets.Container = widgets.Container(layout="vertical") From 49304d992a8f8ea82254dcc5416bc239869766cb Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Fri, 14 Mar 2025 18:09:15 -0700 Subject: [PATCH 13/16] Update lock file --- uv.lock | 207 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 108 insertions(+), 99 deletions(-) diff --git a/uv.lock b/uv.lock index 46b6e417..2d87338f 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 1 requires-python = ">=3.11" resolution-markers = [ "python_full_version >= '3.12'", @@ -66,8 +67,10 @@ magicgui = [ requires-dist = [ { name = "aind-behavior-experiment-launcher", extras = ["aind-services"], marker = "extra == 'dev'" }, { name = "aind-behavior-experiment-launcher", extras = ["aind-services"], marker = "extra == 'docs'" }, - { name = "aind-behavior-services", specifier = ">=0.8.0,<0.9.0" }, - { name = "aind-data-schema", marker = "extra == 'aind-services'", specifier = "<=1.2" }, + { name = "aind-behavior-experiment-launcher", extras = ["magicgui"], marker = "extra == 'dev'" }, + { name = "aind-behavior-services", specifier = "<1" }, + { name = "aind-behavior-video-transformation", marker = "extra == 'aind-services'" }, + { name = "aind-data-schema", marker = "extra == 'aind-services'", specifier = "<2" }, { name = "aind-slims-api", specifier = "<0.2" }, { name = "aind-watchdog-service", marker = "extra == 'aind-services'" }, { name = "autodoc-pydantic", marker = "extra == 'docs'" }, @@ -86,6 +89,7 @@ requires-dist = [ { name = "sphinx-jsonschema", marker = "extra == 'docs'" }, { name = "sphinx-mdinclude", marker = "extra == 'docs'" }, ] +provides-extras = ["aind-services", "magicgui", "dev", "docs"] [[package]] name = "aind-behavior-services" @@ -106,14 +110,14 @@ wheels = [ [[package]] name = "aind-behavior-video-transformation" -version = "0.1.6" +version = "0.1.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aind-data-transformation" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c7/dd/8b377119685163d3ffc4704c9e403cc60f6bca42420b91b09f2a22a0cdc4/aind_behavior_video_transformation-0.1.6.tar.gz", hash = "sha256:1d1d01ade2ffa4c4993931c89ad6c688bcbd330e9a68113f2192ed4d8bab6640", size = 590008 } +sdist = { url = "https://files.pythonhosted.org/packages/21/48/c6dccbeaafa363aa54077d678b2c479d9264203eb043265835e8c3f97cc3/aind_behavior_video_transformation-0.1.7.tar.gz", hash = "sha256:23044acf06398408b22ebfbddacffa96b815a55724ea6af08f9c1fba920e2ea0", size = 589474 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/64/abba18b73f359fe254644b6e27209250f82bbaca01e1aa17523f4acea5b6/aind_behavior_video_transformation-0.1.6-py3-none-any.whl", hash = "sha256:c67726b1944d665007742a4a500c7b2b6e3af7c67a56087ac85004347ab257ba", size = 12430 }, + { url = "https://files.pythonhosted.org/packages/9c/9b/a372ef07f6ed7407e20b48519c86706b0fe5d9b8c47097695a6483c548e3/aind_behavior_video_transformation-0.1.7-py3-none-any.whl", hash = "sha256:fb9de926129a69b7ab3fe2a43353982b720b3b60c3ec72c0926db0eea6f8226e", size = 12433 }, ] [[package]] @@ -656,18 +660,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 }, ] -[[package]] -name = "markdown-it-py" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, -] - [[package]] name = "magicgui" version = "0.10.0" @@ -689,6 +681,18 @@ pyqt6 = [ { name = "pyqt6" }, ] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + [[package]] name = "markupsafe" version = "3.0.2" @@ -885,31 +889,38 @@ wheels = [ [[package]] name = "psygnal" -version = "0.11.1" +version = "0.12.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/b0/194cfbcb76dbf9c4a1c4271ccc825b38406d20fb7f95fd18320c28708800/psygnal-0.11.1.tar.gz", hash = "sha256:f9b02ca246ab0adb108c4010b4a486e464f940543201074591e50370cd7b0cc0", size = 102103 } +sdist = { url = "https://files.pythonhosted.org/packages/34/7f/ef01fa880529b0cbdf33a02e690cbca7868ee0ee291bcb2ebce53f3b3043/psygnal-0.12.0.tar.gz", hash = "sha256:8d2a99803f3152c469d3642d36c04d680213a20e114245558e026695adf9a9c2", size = 104400 } wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/a8/ed06fe70c8bd03f02ab0c1df020f53f079a6dbae056eba0a91823c0d1242/psygnal-0.11.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:3c04baec10f882cdf784a7312e23892416188417ad85607e6d1de2e8a9e70709", size = 427499 }, - { url = "https://files.pythonhosted.org/packages/25/92/6dcab17c3bb91fa3f250ebdbb66de55332436da836c4c547c26e3942877e/psygnal-0.11.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:8f77317cbd11fbed5bfdd40ea41b4e551ee0cf37881cdbc325b67322af577485", size = 453373 }, - { url = "https://files.pythonhosted.org/packages/84/6f/868f1d7d22c76b96e0c8a75f8eb196deaff83916ad2da7bd78d1d0f6a5df/psygnal-0.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24e69ea57ee39e3677298f38a18828af87cdc0bf0aa64685d44259e608bae3ec", size = 717571 }, - { url = "https://files.pythonhosted.org/packages/da/7d/24ca61d177b26e6ab89e9c520dca9c6341083920ab0ea8ac763a31b2b029/psygnal-0.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d77f1a71fe9859c0335c87d92afe1b17c520a4137326810e94351839342d8fc7", size = 695336 }, - { url = "https://files.pythonhosted.org/packages/33/5d/9b2d8f91a9198dda6ad0eaa276f975207b1314ac2d22a2f905f0a6e34524/psygnal-0.11.1-cp312-cp312-macosx_10_16_arm64.whl", hash = "sha256:0b55cb42e468f3a7de75392520778604fef2bc518b7df36c639b35ce4ed92016", size = 425244 }, - { url = "https://files.pythonhosted.org/packages/c4/66/e1bd57a8efef6582141939876d014f86792adbbb8853bd475a1cbf3649ca/psygnal-0.11.1-cp312-cp312-macosx_10_16_x86_64.whl", hash = "sha256:c7dd3cf809c9c1127d90c6b11fbbd1eb2d66d512ccd4d5cab048786f13d11220", size = 444681 }, - { url = "https://files.pythonhosted.org/packages/49/ad/8ee3f8ac1d59cf269ae2d55f7cac7c65fe3b3f41cada5d6a17bc2f4c5d6d/psygnal-0.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:885922a6e65ece9ff8ccf2b6810f435ca8067f410889f7a8fffb6b0d61421a0d", size = 743785 }, - { url = "https://files.pythonhosted.org/packages/14/54/b29b854dff0e27bdaf42a7c1edc65f6d3ea35866e9d9250f1dbabf6381a0/psygnal-0.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1c2388360a9ffcd1381e9b36d0f794287a270d58e69bf17658a194bbf86685c1", size = 725134 }, - { url = "https://files.pythonhosted.org/packages/68/76/d5c5bf5a932ec2dcdc4a23565815a1cc5fd96b03b26ff3f647cdff5ea62c/psygnal-0.11.1-py3-none-any.whl", hash = "sha256:04255fe28828060a80320f8fda937c47bc0c21ca14f55a13eb7c494b165ea395", size = 76998 }, + { url = "https://files.pythonhosted.org/packages/48/57/d6b488dac03f65e731843c984e4677a30c48dd4c5dae3c04df7993ce3168/psygnal-0.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a9ee1e6c441074fe71765b0a96c75b19d72c8198ec5bdea7e97e06a6fe9bd41", size = 458923 }, + { url = "https://files.pythonhosted.org/packages/7a/fa/bab2170fc8b47a4c591e7ab821fc1fe3d4b10292753f47acba7323eb3d66/psygnal-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0cdb387d1d6f00649c970a8084e4ae3fcd3e38ac12b5c51d086fc9e01d8f7530", size = 430113 }, + { url = "https://files.pythonhosted.org/packages/2e/58/91359b72fe0413626be8857122897bb9238fa7b1dd53a3ed299183a17cb6/psygnal-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b410dab639353320044856cef68bd9aa940f8e1399da2f57522356b42bc4cf5d", size = 765465 }, + { url = "https://files.pythonhosted.org/packages/9c/ee/869a2d6741ba3848d6cadf35a1f08535115eab67b7b1c41b2d45f467da7a/psygnal-0.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cd075d7bbe82f0615cfef953ed19ca54feba08f1686b42655c02d6ade0b0beb5", size = 751927 }, + { url = "https://files.pythonhosted.org/packages/68/eb/c59c13a6da8263f3119a3d9faa7790e58d4fe541458197de4b2370927d52/psygnal-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:cc763dbab05fb75f4517c8bd31ede6a4f27e68c59adca55b81a9d7bc875156e0", size = 377665 }, + { url = "https://files.pythonhosted.org/packages/9b/2e/6cff528f8f5dc7f60221fddca85ed52131a90b606a72c5a4085606d5217d/psygnal-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dac134c8890e3d0e413ab701fcb56a882f9b151a6a9d625080736c36833b26ed", size = 469384 }, + { url = "https://files.pythonhosted.org/packages/55/9d/774d547ed1fcb079de7fc41b1e3103b261eebae7f34da5cf05909a040470/psygnal-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc2324cef7ba3f4d30d32895f8cb7d5cf9ad7bcfdb7955aa92a0fbfe7537ec3f", size = 426617 }, + { url = "https://files.pythonhosted.org/packages/b4/89/300991108d86c00e6aa84ea85e9c998e27eed59fdcb1abd3e69b9f4d62f5/psygnal-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae2bd6edcf911fbff34ed75150e8f8dfb246ebf203514c7c1e4397cabbb1368a", size = 787401 }, + { url = "https://files.pythonhosted.org/packages/9e/ae/8cc64c0f1eebbc4be74a953e76e431431c770a30f28b71b48edc7f6b8288/psygnal-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d6fbeee192beab90ca23d9d3ee3bf1eb7ef5f00e815fa53e23e402feee617242", size = 781293 }, + { url = "https://files.pythonhosted.org/packages/84/09/f00841834b7ae543bd232c22e557914d63d0d0430d32980883421d5981bb/psygnal-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:25a9f2db710a6cd2566b3e0e03cf6e04d56276f36ac86b42fa22d81f9a4ac0f2", size = 381816 }, + { url = "https://files.pythonhosted.org/packages/cb/b4/64a06b1d9b7628c84c9ea68a6cdc9d54378bae04695e7173addb9cf46607/psygnal-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2f4c1fed9337f57778109c397b6b9591961123ce4bbeb068115c0468964fc2b4", size = 468346 }, + { url = "https://files.pythonhosted.org/packages/78/be/b3df7dac845f5f6b9897e60d19c3eaed27b56b024099588db92c3b76bb21/psygnal-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d5a953a50fc8263bb23bc558b926cf691f70c9c781c68c64c983fb8cbead910", size = 426482 }, + { url = "https://files.pythonhosted.org/packages/df/36/0017e838d3c63081a64e6d2252c8dda368a6d0898c7ecf689ba678fe4127/psygnal-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a67ec8e0c8a6553dd56ed653f87c46ef652b0c512bb8c8f8c5adcff3907751f", size = 785239 }, + { url = "https://files.pythonhosted.org/packages/67/d0/7057151debcd5c7d8ce7789d276e18681d5c141c9222c9cf99ce3a418680/psygnal-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:742abb2d0e230521b208161eeab06abb682a19239e734e543a269214c84a54d2", size = 779690 }, + { url = "https://files.pythonhosted.org/packages/5e/ae/a3d6815db583b6d05878b3647ea0e2aa21ce6941d03c9d2c6caad1afbcf6/psygnal-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:d779f20c6977ec9d5b9fece23b4b28bbcf0a7773539a4a176b5527aea5da27c7", size = 382622 }, + { url = "https://files.pythonhosted.org/packages/eb/fa/84fc30ad391081cb119099aae5491ba4a9ebd34ce5139bf05b31813abb84/psygnal-0.12.0-py3-none-any.whl", hash = "sha256:15f39abd8bee2926e79da76bec31a258d03dbe3e61d22d6251f65caefbae5d54", size = 78492 }, ] [[package]] name = "pyconify" -version = "0.1.6" +version = "0.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dd/21/8b703dd994e4833dcfa611536485ce2096c215aa74cbf610c2ff51a7dea8/pyconify-0.1.6.tar.gz", hash = "sha256:25272f7a29965f32ee698c9da6aab8bbbeb0756ca3bfeadd3d9effab689d239d", size = 22236 } +sdist = { url = "https://files.pythonhosted.org/packages/fd/7f/94d424dc756a6287271cf40cf1b2a44c10e3f137bf3246a2b4a7416ca3d3/pyconify-0.2.1.tar.gz", hash = "sha256:8dd53757d9fbed41711434460932b2b5dbc25da720cd9f9a44af0187b2dfc07d", size = 22478 } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/fc/be68d2acc43d22dbfe6d52d573dc83da14e4fd45821acaf42992e8803682/pyconify-0.1.6-py3-none-any.whl", hash = "sha256:63040836b344ec351c05531231609edd30425c145ff68478b6250f33135b8fc5", size = 18929 }, + { url = "https://files.pythonhosted.org/packages/72/40/50dd2e8bfec81676e4619903bd452c10dc0d8efac1533e79e67cc76759b5/pyconify-0.2.1-py3-none-any.whl", hash = "sha256:d3b53eee1f8a2d60c1d135610f42e789774dbe71c6d8af68af0a21d3b3ec9eb7", size = 19459 }, ] [[package]] @@ -1017,56 +1028,55 @@ wheels = [ [[package]] name = "pyqt6" -version = "6.8.0" +version = "6.8.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyqt6-qt6" }, { name = "pyqt6-sip" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e9/0a/accbebed526158ab2aedd5c84d238159754bd99f481082b3fe7f374c6a3b/PyQt6-6.8.0.tar.gz", hash = "sha256:6d8628de4c2a050f0b74462e4c9cb97f839bf6ffabbca91711722ffb281570d9", size = 1061357 } +sdist = { url = "https://files.pythonhosted.org/packages/ce/bf/ff284a136b39cb1873c18e4fca4a40a8847c84a1910c5fb38c6a77868968/pyqt6-6.8.1.tar.gz", hash = "sha256:91d937d6166274fafd70f4dee11a8da6dbfdb0da53de05f5d62361ddf775e256", size = 1064723 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/ae/30f6f40c8cdf3d5319ec7add820c6edf96d8ed2461984fd548af9e2d9b51/PyQt6-6.8.0-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:8c5c05f5fdff31a5887dbc29b27615b09df467631238d7b449283809ffca6228", size = 12104917 }, - { url = "https://files.pythonhosted.org/packages/b4/7a/a3ecac3327345dde8f34e781d6db1339e4e3b8411ac8ae9747822a0d44e9/PyQt6-6.8.0-cp39-abi3-manylinux_2_35_x86_64.whl", hash = "sha256:452bae5840077bf0f146c798d7777f70d7bdd0c7dcfa9ee7a415c1daf2d10038", size = 8322144 }, - { url = "https://files.pythonhosted.org/packages/d0/52/1e60e38d216c232af83ac16bedf1ece419812368220485fc25af80c7a5d7/PyQt6-6.8.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:cf7123caea14e7ecf10bd12cae48e8d9970ef7caf627bc7d7988b0baa209adb3", size = 8101586 }, - { url = "https://files.pythonhosted.org/packages/eb/73/9bae1b8daedf2acff5a3668eae84783720ff513e402b0448a906ab3d5479/PyQt6-6.8.0-cp39-abi3-win_amd64.whl", hash = "sha256:a9913d479f1ffee804bf7f232079baea4fb4b221a8f4890117588917a54ea30d", size = 6661265 }, - { url = "https://files.pythonhosted.org/packages/23/a0/f564279285ed92b4fe38ed7b2a8fcddab414512391088b6a0f67a1107f24/PyQt6-6.8.0-cp39-abi3-win_arm64.whl", hash = "sha256:48bace7b87676bba5e6114482f3a20ca20be90c7f261b5d340464313f5f2bf5e", size = 5409032 }, + { url = "https://files.pythonhosted.org/packages/09/da/70971b3d7f53a68644ea32544d3786dfbbb162d18572ac1defcf5a6481d5/PyQt6-6.8.1-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:0425f9eebdd5d4e57ab36424c9382f2ea06670c3c550fa0028c2b19bd0a1d7bd", size = 12213924 }, + { url = "https://files.pythonhosted.org/packages/be/25/a4392c323a0fb97eb5f449b7594f37e93d9794b900756b43cd65772def77/PyQt6-6.8.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:36bf48e3df3a6ff536e703315d155480ef4e260396eb5469eb7a875bc5bb7ab4", size = 8238120 }, + { url = "https://files.pythonhosted.org/packages/de/a3/e528b4cc3394f2ae15b531c17f27b53de756a8c0404dfa9c184502367c48/PyQt6-6.8.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:2eac2267a34828b8db7660dd3cc3b3b5fd76a92e61ad45471565b01221cb558b", size = 8173996 }, + { url = "https://files.pythonhosted.org/packages/f2/69/11404cfcb916bd7207805c21432ecab0401779361d48b67f28ae9337f70d/PyQt6-6.8.1-cp39-abi3-win_amd64.whl", hash = "sha256:70bad7b890a8f9e9e5fb9598c544b832d9d9d99a9519e0009cb29c1e15e96632", size = 6723466 }, + { url = "https://files.pythonhosted.org/packages/00/2a/21a555aea9bc8abc4f09017b922dbdf509c421f70506d4c83d2e8f4315b2/PyQt6-6.8.1-cp39-abi3-win_arm64.whl", hash = "sha256:a40f878e8e5eeeb0bba995152d07eeef9375ea0116df0f4aad0a6b97c8ad1175", size = 5463379 }, ] [[package]] name = "pyqt6-qt6" -version = "6.8.1" +version = "6.8.2" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/0a/c47a1cc272b418faff8af79b121f0cecd32b09d634253254e3a990432220/PyQt6_Qt6-6.8.1-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:1eb8460a1fdb38d0b2458c2974c01d471c1e59e4eb19ea63fc447aaba3ad530e", size = 65754939 }, - { url = "https://files.pythonhosted.org/packages/b1/e6/cc4fbc97a7d0955185e33add3ce00480f0023424d17ac6f864a504f60251/PyQt6_Qt6-6.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9f3790c4ce4dc576e48b8718d55fb8743057e6cbd53a6ca1dd253ffbac9b7287", size = 59956028 }, - { url = "https://files.pythonhosted.org/packages/01/22/c2997fe76d765d9ba960e9a099238cb419a316362bdde50fedacc23e7c7d/PyQt6_Qt6-6.8.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:d6ca5d2b9d2ec0ee4a814b2175f641a5c4299cb80b45e0f5f8356632663f89b3", size = 72561636 }, - { url = "https://files.pythonhosted.org/packages/a1/24/907c7451901e56e5b713d70c6f65191d80a3a3caac1095fea7f2a4b0b25b/PyQt6_Qt6-6.8.1-py3-none-manylinux_2_35_x86_64.whl", hash = "sha256:08065d595f1e6fc2dde9f4450eeff89082f4bad26f600a8e9b9cc5966716bfcf", size = 72573711 }, - { url = "https://files.pythonhosted.org/packages/2b/a8/d942bb9f83ad4abd4c296cd4bbf752d1147eaa14523cc132a2b8511a3960/PyQt6_Qt6-6.8.1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:20843cb86bd94942d1cd99e39bf1aeabb875b241a35a8ab273e4bbbfa63776db", size = 71077917 }, - { url = "https://files.pythonhosted.org/packages/ab/2c/0d45a74ea8eedf5a2a5a2628396fb2e26107ae07f6c73d6d22c7fa3d4403/PyQt6_Qt6-6.8.1-py3-none-win_amd64.whl", hash = "sha256:006d786693d0511fbcf184a862edbd339c6ed1bb3bd9de363d73a19ed4b23dff", size = 71647964 }, - { url = "https://files.pythonhosted.org/packages/c9/0d/b86b64f0ac5e08b1bd18edac9e949897a4626196725e9a86af4e9f628c80/PyQt6_Qt6-6.8.1-py3-none-win_arm64.whl", hash = "sha256:a8bc2ed4ee5e7c6ff4dd1c7db0b27705d151fee5dc232bbd1bf17618f937f515", size = 47836492 }, + { url = "https://files.pythonhosted.org/packages/1e/a4/3d764e05955382b3dc7227cbfde090700edd63431147f1c66d428ccac45c/PyQt6_Qt6-6.8.2-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:470dd4211fe5a67b0565e0202e7aa67816e5dcf7d713528b88327adaebd0934e", size = 66121240 }, + { url = "https://files.pythonhosted.org/packages/d6/b3/6d4f8257b46554fb2c89b33a6773a3f05ed961b3cd83828caee5dc79899f/PyQt6_Qt6-6.8.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:40cda901a3e1617e79225c354fe9d89b80249f0a6c6aaa18b40938e05bbf7d1f", size = 60286219 }, + { url = "https://files.pythonhosted.org/packages/92/95/0036435b9e2cbd22e08f14eec2362c32fc641660c6e4aea6f59d165cb5fc/PyQt6_Qt6-6.8.2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:fb6d0acdd7d43c33fb8b9d2dd7922d381cdedd00da316049fbe01fc1973e6f05", size = 81263397 }, + { url = "https://files.pythonhosted.org/packages/6e/fb/c01dde044eca1542d88cac72fc99369af76a981cc2f52790236efa566e01/PyQt6_Qt6-6.8.2-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:5970c85d22cbe5c476418994549161b23ed938e25b04fc4ca8fabf6dcac7b03f", size = 79832921 }, + { url = "https://files.pythonhosted.org/packages/1a/f7/31f03a9f5e6c7cc23ceb2bd0d9c2df0518837f7af0e693e15b6e0881b8b0/PyQt6_Qt6-6.8.2-py3-none-win_amd64.whl", hash = "sha256:28e2bb641f05b01e498503c3ef01c8a919d6e0e96b50230301c0baac2b7d1433", size = 71934164 }, + { url = "https://files.pythonhosted.org/packages/00/c9/102c9537795ca11c12120ec9d5f554d9437787f52d8e23fbc8269e6a2699/PyQt6_Qt6-6.8.2-py3-none-win_arm64.whl", hash = "sha256:912afdddd0dfc666ce1c16bc4695e2acd680db72343e4f7a2b7c053a0146b4bc", size = 48120018 }, ] [[package]] name = "pyqt6-sip" -version = "13.9.1" +version = "13.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/e4/f39ca1fd6de7d4823d964a3ec33e85b6f51a9c2a7a1e95956b7a92c8acc9/pyqt6_sip-13.9.1.tar.gz", hash = "sha256:15be741d1ae8c82bb7afe9a61f3cf8c50457f7d61229a1c39c24cd6e8f4d86dc", size = 92358 } +sdist = { url = "https://files.pythonhosted.org/packages/90/18/0405c54acba0c8e276dd6f0601890e6e735198218d031a6646104870fe22/pyqt6_sip-13.10.0.tar.gz", hash = "sha256:d6daa95a0bd315d9ec523b549e0ce97455f61ded65d5eafecd83ed2aa4ae5350", size = 92464 } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/07/342ea1c95367d714eb86ae4b2159798347b80535dcba6c7f60256fc7c5e5/PyQt6_sip-13.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:57b5312ef13c1766bdf69b317041140b184eb24a51e1e23ce8fc5386ba8dffb2", size = 110717 }, - { url = "https://files.pythonhosted.org/packages/89/f2/13159c98929d2dec84cb98021a8de9e66e9429ebe44be08476779130af25/PyQt6_sip-13.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5004514b08b045ad76425cf3618187091a668d972b017677b1b4b193379ef553", size = 316880 }, - { url = "https://files.pythonhosted.org/packages/76/92/47fc35401d7f192bd690ed3552242909c6e77db5a97d8b2d980a948400fe/PyQt6_sip-13.9.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:accab6974b2758296400120fdcc9d1f37785b2ea2591f00656e1776f058ded6c", size = 294518 }, - { url = "https://files.pythonhosted.org/packages/39/7c/3c775c219d9c17bda783e1dbab1d16f09f7713b93920f761d2cc61aa3ad0/PyQt6_sip-13.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:1ec52e962f54137a19208b6e95b6bd9f7a403eb25d7237768a99306cd9db26d1", size = 53368 }, - { url = "https://files.pythonhosted.org/packages/81/bf/0700d0f5832e6e54ec56dbbdc912c63662ae54d1571f3241377b5acb37fa/PyQt6_sip-13.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:6e6c1e2592187934f4e790c0c099d0033e986dcef7bdd3c06e3895ffa995e9fc", size = 45013 }, - { url = "https://files.pythonhosted.org/packages/34/19/6df209c4f1a3fbae0a90c8f37b0205ce42d61d8d762e4a539fceeabee340/PyQt6_sip-13.9.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1fb405615970e85b622b13b4cad140ff1e4182eb8334a0b27a4698e6217b89b0", size = 112248 }, - { url = "https://files.pythonhosted.org/packages/6b/81/9df86e90a8dd1f6f5faa1199af03e208f5dd3193e462e541e276001d098c/PyQt6_sip-13.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c800db3464481e87b1d2b84523b075df1e8fc7856c6f9623dc243f89be1cb604", size = 322310 }, - { url = "https://files.pythonhosted.org/packages/dd/4d/e263f045c97c2398fbd762d07295140b628a611da5f37744324080a86a1d/PyQt6_sip-13.9.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c1942e107b0243ced9e510d507e0f27aeea9d6b13e0a1b7c06fd52a62e0d41f7", size = 303638 }, - { url = "https://files.pythonhosted.org/packages/28/9f/047a3b28265b39ff151981da84fde7895b87b94131db4b044a6125df9a4a/PyQt6_sip-13.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:552ff8fdc41f5769d3eccc661f022ed496f55f6e0a214c20aaf56e56385d61b6", size = 53498 }, - { url = "https://files.pythonhosted.org/packages/92/af/17a92b67db31a086bd693bf97d98aa2f45427e7982df53f59cdc26899979/PyQt6_sip-13.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:976c7758f668806d4df7a8853f390ac123d5d1f73591ed368bdb8963574ff589", size = 45335 }, - { url = "https://files.pythonhosted.org/packages/4b/10/43eab7a9cd26f3ba81220f2ec7a7c471c232df738dd5787affcc00d75aa9/PyQt6_sip-13.9.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:56ce0afb19cd8a8c63ff93ae506dffb74f844b88adaa6673ebc0dec43af48a76", size = 112278 }, - { url = "https://files.pythonhosted.org/packages/31/ba/b0cb5ad4d44265213d50b8e7b24a9c1605baf242019e51104dd87499d68f/PyQt6_sip-13.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d7726556d1ca7a7ed78e19ba53285b64a2a8f6ad7ff4cb18a1832efca1a3102", size = 322531 }, - { url = "https://files.pythonhosted.org/packages/ba/73/556391d6115e87972d8c88b757b4db614af4fb8579ce28030965fe793fe7/PyQt6_sip-13.9.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14f95c6352e3b85dc26bf59cfbf77a470ecbd5fcdcf00af4b648f0e1b9eefb9e", size = 303760 }, - { url = "https://files.pythonhosted.org/packages/d8/5b/7321bb2564673bc4facfed338553bc42542f2ae05cbea1c8b57a596ab28d/PyQt6_sip-13.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:3c269052c770c09b61fce2f2f9ea934a67dfc65f443d59629b4ccc8f89751890", size = 53509 }, - { url = "https://files.pythonhosted.org/packages/cf/b6/eabe1d0fdcc56c80325c5a2cc361ae3e57b3346cb7700b707481d3977a24/PyQt6_sip-13.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:8b2ac36d6e04db6099614b9c1178a2f87788c7ffc3826571fb63d36ddb4c401d", size = 45358 }, + { url = "https://files.pythonhosted.org/packages/7a/c4/97446339ff086ad6530af0970b6905a6930d8868214c182a0ca0e0e9638e/PyQt6_sip-13.10.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e78fb8036b18f6258a1af0956c5a3cec1dd3d8dd5196ecd89a31b529bf40e82", size = 110770 }, + { url = "https://files.pythonhosted.org/packages/2b/4b/d41d9d1a5496948426b1a30b778df1d87625245d4ba5b7aa55810f2899c5/PyQt6_sip-13.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e19d5887fa3003a635419644dfed3158cb15eb566fc27b1ed56913a5767a71dc", size = 316994 }, + { url = "https://files.pythonhosted.org/packages/ff/81/62e36cddb4641c7ce6e2779e0783635555a3ecfdb50f46f9200f61af24be/PyQt6_sip-13.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:079bb946edc3960f08d92b3a8eebff55d3abb51bc2a0583b6683dfd9f77a616a", size = 293804 }, + { url = "https://files.pythonhosted.org/packages/2f/4d/23e9e23a331d5a608217349c931b1d9b5cf9419640033e73ae9895e7f5bd/PyQt6_sip-13.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:90974f5dbba1f5d1d2ca9b1cfdfd5258e5e3cfacead03f0df674d54c69973ea7", size = 53444 }, + { url = "https://files.pythonhosted.org/packages/2a/0c/2caff1607ddf7dcb8d53f1657160d3b334a8e9d9a9bf700e79971677ab89/PyQt6_sip-13.10.0-cp311-cp311-win_arm64.whl", hash = "sha256:bbefd5539eeda4dec37e8b6dfc362ba240ec31279060336bcceaff572807dac8", size = 45057 }, + { url = "https://files.pythonhosted.org/packages/69/81/66d9bdacb790592a0641378749a047f12e3b254cdc2cb51f7ed636cf01d2/PyQt6_sip-13.10.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:48791db2914fc39c3218519a02d2a5fd3fcd354a1be3141a57bf2880701486f2", size = 112334 }, + { url = "https://files.pythonhosted.org/packages/26/2c/4796c209009a018e0d4a5c406d5a519234c5a378f370dc679d0ad5f455b2/PyQt6_sip-13.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:466d6b4791973c9fcbdc2e0087ed194b9ea802a8c3948867a849498f0841c70c", size = 322334 }, + { url = "https://files.pythonhosted.org/packages/99/34/2ec54bd475f0a811df1d32be485f2344cf9e8b388ce7adb26b46ce5552d4/PyQt6_sip-13.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ae15358941f127cd3d1ab09c1ebd45c4dabb0b2e91587b9eebde0279d0039c54", size = 303798 }, + { url = "https://files.pythonhosted.org/packages/0c/e4/82099bb4ab8bc152b5718541e93c0b3adf7566c0f307c9e58e2368b8c517/PyQt6_sip-13.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad573184fa8b00041944e5a17d150ab0d08db2d2189e39c9373574ebab3f2e58", size = 53569 }, + { url = "https://files.pythonhosted.org/packages/e3/09/90e0378887a3cb9664da77061229cf8e97e6ec25a5611b7dbc9cc3e02c78/PyQt6_sip-13.10.0-cp312-cp312-win_arm64.whl", hash = "sha256:2d579d810d0047d40bde9c6aef281d6ed218db93c9496ebc9e55b9e6f27a229d", size = 45430 }, + { url = "https://files.pythonhosted.org/packages/6b/0c/8d1de48b45b565a46bf4757341f13f9b1853a7d2e6b023700f0af2c213ab/PyQt6_sip-13.10.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7b6e250c2e7c14702a623f2cc1479d7fb8db2b6eee9697cac10d06fe79c281bb", size = 112343 }, + { url = "https://files.pythonhosted.org/packages/af/13/e2cc2b667a9f5d44c2d0e18fa6e1066fca3f4521dcb301f4b5374caeb33e/PyQt6_sip-13.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fcb30756568f8cd59290f9ef2ae5ee3e72ff9cdd61a6f80c9e3d3b95ae676be", size = 322527 }, + { url = "https://files.pythonhosted.org/packages/20/1a/5c6fcae85edb65cf236c9dc6d23b279b5316e94cdca1abdee6d0a217ddbb/PyQt6_sip-13.10.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:757ac52c92b2ef0b56ecc7cd763b55a62d3c14271d7ea8d03315af85a70090ff", size = 303407 }, + { url = "https://files.pythonhosted.org/packages/b9/db/6924ec985be7d746772806b96ab81d24263ef72f0249f0573a82adaed75e/PyQt6_sip-13.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:571900c44a3e38738d696234d94fe2043972b9de0633505451c99e2922cb6a34", size = 53580 }, + { url = "https://files.pythonhosted.org/packages/77/c3/9e44729b582ee7f1d45160e8c292723156889f3e38ce6574f88d5ab8fa02/PyQt6_sip-13.10.0-cp313-cp313-win_arm64.whl", hash = "sha256:39cba2cc71cf80a99b4dc8147b43508d4716e128f9fb99f5eb5860a37f082282", size = 45446 }, ] [[package]] @@ -1135,16 +1145,15 @@ wheels = [ ] [[package]] -name = "referencing" -version = "0.35.1" +name = "qtpy" +version = "2.4.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "attrs" }, - { name = "rpds-py" }, + { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/99/5b/73ca1f8e72fff6fa52119dbd185f73a907b1989428917b24cff660129b6d/referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", size = 62991 } +sdist = { url = "https://files.pythonhosted.org/packages/70/01/392eba83c8e47b946b929d7c46e0f04b35e9671f8bb6fc36b6f7945b4de8/qtpy-2.4.3.tar.gz", hash = "sha256:db744f7832e6d3da90568ba6ccbca3ee2b3b4a890c3d6fbbc63142f6e4cdf5bb", size = 66982 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/59/2056f61236782a2c86b33906c025d4f4a0b17be0161b63b70fd9e8775d36/referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de", size = 26684 }, + { url = "https://files.pythonhosted.org/packages/69/76/37c0ccd5ab968a6a438f9c623aeecc84c202ab2fabc6a8fd927580c15b5a/QtPy-2.4.3-py3-none-any.whl", hash = "sha256:72095afe13673e017946cc258b8d5da43314197b741ed2890e563cf384b51aa1", size = 95045 }, ] [[package]] @@ -1249,27 +1258,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.9.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/c3/418441a8170e8d53d05c0b9dad69760dbc7b8a12c10dbe6db1e1205d2377/ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933", size = 3717448 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/c3/2c4afa9ba467555d074b146d9aed0633a56ccdb900839fb008295d037b89/ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367", size = 10027252 }, - { url = "https://files.pythonhosted.org/packages/33/d1/439e58487cf9eac26378332e25e7d5ade4b800ce1eec7dc2cfc9b0d7ca96/ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7", size = 10840721 }, - { url = "https://files.pythonhosted.org/packages/50/44/fead822c38281ba0122f1b76b460488a175a9bd48b130650a6fb6dbcbcf9/ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d", size = 10161439 }, - { url = "https://files.pythonhosted.org/packages/11/ae/d404a2ab8e61ddf6342e09cc6b7f7846cce6b243e45c2007dbe0ca928a5d/ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a", size = 10336264 }, - { url = "https://files.pythonhosted.org/packages/6a/4e/7c268aa7d84cd709fb6f046b8972313142cffb40dfff1d2515c5e6288d54/ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe", size = 9908774 }, - { url = "https://files.pythonhosted.org/packages/cc/26/c618a878367ef1b76270fd027ca93692657d3f6122b84ba48911ef5f2edc/ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c", size = 11428127 }, - { url = "https://files.pythonhosted.org/packages/d7/9a/c5588a93d9bfed29f565baf193fe802fa676a0c837938137ea6cf0576d8c/ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be", size = 12133187 }, - { url = "https://files.pythonhosted.org/packages/3e/ff/e7980a7704a60905ed7e156a8d73f604c846d9bd87deda9cabfa6cba073a/ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590", size = 11602937 }, - { url = "https://files.pythonhosted.org/packages/24/78/3690444ad9e3cab5c11abe56554c35f005b51d1d118b429765249095269f/ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb", size = 13771698 }, - { url = "https://files.pythonhosted.org/packages/6e/bf/e477c2faf86abe3988e0b5fd22a7f3520e820b2ee335131aca2e16120038/ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0", size = 11249026 }, - { url = "https://files.pythonhosted.org/packages/f7/82/cdaffd59e5a8cb5b14c408c73d7a555a577cf6645faaf83e52fe99521715/ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17", size = 10220432 }, - { url = "https://files.pythonhosted.org/packages/fe/a4/2507d0026225efa5d4412b6e294dfe54725a78652a5c7e29e6bd0fc492f3/ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1", size = 9874602 }, - { url = "https://files.pythonhosted.org/packages/d5/be/f3aab1813846b476c4bcffe052d232244979c3cd99d751c17afb530ca8e4/ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57", size = 10851212 }, - { url = "https://files.pythonhosted.org/packages/8b/45/8e5fd559bea0d2f57c4e12bf197a2fade2fac465aa518284f157dfbca92b/ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e", size = 11327490 }, - { url = "https://files.pythonhosted.org/packages/42/55/e6c90f13880aeef327746052907e7e930681f26a164fe130ddac28b08269/ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1", size = 10227912 }, - { url = "https://files.pythonhosted.org/packages/35/b2/da925693cb82a1208aa34966c0f36cb222baca94e729dd22a587bc22d0f3/ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1", size = 11355632 }, - { url = "https://files.pythonhosted.org/packages/31/d8/de873d1c1b020d668d8ec9855d390764cb90cf8f6486c0983da52be8b7b7/ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf", size = 10435860 }, +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/77/2b/7ca27e854d92df5e681e6527dc0f9254c9dc06c8408317893cf96c851cdd/ruff-0.11.0.tar.gz", hash = "sha256:e55c620690a4a7ee6f1cccb256ec2157dc597d109400ae75bbf944fc9d6462e2", size = 3799407 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/40/3d0340a9e5edc77d37852c0cd98c5985a5a8081fc3befaeb2ae90aaafd2b/ruff-0.11.0-py3-none-linux_armv6l.whl", hash = "sha256:dc67e32bc3b29557513eb7eeabb23efdb25753684b913bebb8a0c62495095acb", size = 10098158 }, + { url = "https://files.pythonhosted.org/packages/ec/a9/d8f5abb3b87b973b007649ac7bf63665a05b2ae2b2af39217b09f52abbbf/ruff-0.11.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:38c23fd9bdec4eb437b4c1e3595905a0a8edfccd63a790f818b28c78fe345639", size = 10879071 }, + { url = "https://files.pythonhosted.org/packages/ab/62/aaa198614c6211677913ec480415c5e6509586d7b796356cec73a2f8a3e6/ruff-0.11.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7c8661b0be91a38bd56db593e9331beaf9064a79028adee2d5f392674bbc5e88", size = 10247944 }, + { url = "https://files.pythonhosted.org/packages/9f/52/59e0a9f2cf1ce5e6cbe336b6dd0144725c8ea3b97cac60688f4e7880bf13/ruff-0.11.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6c0e8d3d2db7e9f6efd884f44b8dc542d5b6b590fc4bb334fdbc624d93a29a2", size = 10421725 }, + { url = "https://files.pythonhosted.org/packages/a6/c3/dcd71acc6dff72ce66d13f4be5bca1dbed4db678dff2f0f6f307b04e5c02/ruff-0.11.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c3156d3f4b42e57247275a0a7e15a851c165a4fc89c5e8fa30ea6da4f7407b8", size = 9954435 }, + { url = "https://files.pythonhosted.org/packages/a6/9a/342d336c7c52dbd136dee97d4c7797e66c3f92df804f8f3b30da59b92e9c/ruff-0.11.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:490b1e147c1260545f6d041c4092483e3f6d8eba81dc2875eaebcf9140b53905", size = 11492664 }, + { url = "https://files.pythonhosted.org/packages/84/35/6e7defd2d7ca95cc385ac1bd9f7f2e4a61b9cc35d60a263aebc8e590c462/ruff-0.11.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1bc09a7419e09662983b1312f6fa5dab829d6ab5d11f18c3760be7ca521c9329", size = 12207856 }, + { url = "https://files.pythonhosted.org/packages/22/78/da669c8731bacf40001c880ada6d31bcfb81f89cc996230c3b80d319993e/ruff-0.11.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcfa478daf61ac8002214eb2ca5f3e9365048506a9d52b11bea3ecea822bb844", size = 11645156 }, + { url = "https://files.pythonhosted.org/packages/ee/47/e27d17d83530a208f4a9ab2e94f758574a04c51e492aa58f91a3ed7cbbcb/ruff-0.11.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6fbb2aed66fe742a6a3a0075ed467a459b7cedc5ae01008340075909d819df1e", size = 13884167 }, + { url = "https://files.pythonhosted.org/packages/9f/5e/42ffbb0a5d4b07bbc642b7d58357b4e19a0f4774275ca6ca7d1f7b5452cd/ruff-0.11.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92c0c1ff014351c0b0cdfdb1e35fa83b780f1e065667167bb9502d47ca41e6db", size = 11348311 }, + { url = "https://files.pythonhosted.org/packages/c8/51/dc3ce0c5ce1a586727a3444a32f98b83ba99599bb1ebca29d9302886e87f/ruff-0.11.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e4fd5ff5de5f83e0458a138e8a869c7c5e907541aec32b707f57cf9a5e124445", size = 10305039 }, + { url = "https://files.pythonhosted.org/packages/60/e0/475f0c2f26280f46f2d6d1df1ba96b3399e0234cf368cc4c88e6ad10dcd9/ruff-0.11.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:96bc89a5c5fd21a04939773f9e0e276308be0935de06845110f43fd5c2e4ead7", size = 9937939 }, + { url = "https://files.pythonhosted.org/packages/e2/d3/3e61b7fd3e9cdd1e5b8c7ac188bec12975c824e51c5cd3d64caf81b0331e/ruff-0.11.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a9352b9d767889ec5df1483f94870564e8102d4d7e99da52ebf564b882cdc2c7", size = 10923259 }, + { url = "https://files.pythonhosted.org/packages/30/32/cd74149ebb40b62ddd14bd2d1842149aeb7f74191fb0f49bd45c76909ff2/ruff-0.11.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:049a191969a10897fe052ef9cc7491b3ef6de79acd7790af7d7897b7a9bfbcb6", size = 11406212 }, + { url = "https://files.pythonhosted.org/packages/00/ef/033022a6b104be32e899b00de704d7c6d1723a54d4c9e09d147368f14b62/ruff-0.11.0-py3-none-win32.whl", hash = "sha256:3191e9116b6b5bbe187447656f0c8526f0d36b6fd89ad78ccaad6bdc2fad7df2", size = 10310905 }, + { url = "https://files.pythonhosted.org/packages/ed/8a/163f2e78c37757d035bd56cd60c8d96312904ca4a6deeab8442d7b3cbf89/ruff-0.11.0-py3-none-win_amd64.whl", hash = "sha256:c58bfa00e740ca0a6c43d41fb004cd22d165302f360aaa56f7126d544db31a21", size = 11411730 }, + { url = "https://files.pythonhosted.org/packages/4e/f7/096f6efabe69b49d7ca61052fc70289c05d8d35735c137ef5ba5ef423662/ruff-0.11.0-py3-none-win_arm64.whl", hash = "sha256:868364fc23f5aa122b00c6f794211e85f7e78f5dffdf7c590ab90b8c4e69b657", size = 10538956 }, ] [[package]] @@ -1372,7 +1381,7 @@ wheels = [ [[package]] name = "slims-python-api" -version = "6.9.0" +version = "7.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "deprecation" }, @@ -1380,9 +1389,9 @@ dependencies = [ { name = "requests" }, { name = "requests-oauthlib" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b0/a0/c3e4894d833692f8693351d77a82631d907f26b5ea2bf64a72186141fba1/slims-python-api-6.9.0.tar.gz", hash = "sha256:195c6b30520425d75dea7ab8d326e59a2125efcd8d14cc966d5cfade93712265", size = 21087 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/21/62fb94273fa5708681de873f0b91bf1dfedde59700e2259052403dc66e62/slims_python_api-7.2.0.tar.gz", hash = "sha256:2dc23bbf7d20ddf0d712a622f34fc4341b3df8b099d0ced111b532b1f4694634", size = 21349 } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/6a/223b7fec5f5a9552c894d9600db5df78589be5d7639dd9cb815a05863009/slims_python_api-6.9.0-py2.py3-none-any.whl", hash = "sha256:15ddc0fc5da62329d2a4fd8cae2f5349202cd09aead90593a67e54d3c86e4798", size = 17005 }, + { url = "https://files.pythonhosted.org/packages/77/1f/938934b209e2a09f9c7a31015218449d2e3eb8c0c29d713c858822c1352f/slims_python_api-7.2.0-py2.py3-none-any.whl", hash = "sha256:88e13e24e0612a55964cdc1f11ba56c47e8befc3ea3ab526e4fc19d622bcab2e", size = 17144 }, ] [[package]] @@ -1560,16 +1569,16 @@ wheels = [ [[package]] name = "superqt" -version = "0.7.0" +version = "0.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pygments" }, { name = "qtpy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4a/7b/10b854842f76b20fc8a13ea5b62f78346c84bf9664b7d54ee82e53d88b6d/superqt-0.7.0.tar.gz", hash = "sha256:fa8014a0ffead7f618a85346ac355ba5e9529b177f50caf5093edeec566c6f5d", size = 97558 } +sdist = { url = "https://files.pythonhosted.org/packages/52/28/3b3afeb6e1efd9095d18a43a60d851dede470e95297ff694d3d75e704926/superqt-0.7.1.tar.gz", hash = "sha256:dade2953916e9adff912e08a337e322b5f4603c780ed92505515489598d73960", size = 101188 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/77/47289ce2c607f72446811230ec96406ace44db7669e443793a13fd7b3486/superqt-0.7.0-py3-none-any.whl", hash = "sha256:395d3f511fe5143882eba6e9ffb8d79bd4c782b6d571c1edb4141cea977612d3", size = 91179 }, + { url = "https://files.pythonhosted.org/packages/cb/43/4f2393a5203b53e94dfd9f7e408bc4caeed5ead51868b091578c6a5aaaf5/superqt-0.7.1-py3-none-any.whl", hash = "sha256:c40df8d63bc06e0b7a3ec01c2c41c901fee03e2177c17eafabd6cfd157aa99ed", size = 95107 }, ] [package.optional-dependencies] @@ -1579,11 +1588,11 @@ iconify = [ [[package]] name = "threadpoolctl" -version = "3.5.0" +version = "3.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/55/b5148dcbf72f5cde221f8bfe3b6a540da7aa1842f6b491ad979a6c8b84af/threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107", size = 41936 } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467", size = 18414 }, + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638 }, ] [[package]] From 81c2229af215c881789b6e4b12460e03254ecd1d Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Fri, 14 Mar 2025 18:12:57 -0700 Subject: [PATCH 14/16] Revert change --- src/aind_behavior_experiment_launcher/launcher/_base.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/aind_behavior_experiment_launcher/launcher/_base.py b/src/aind_behavior_experiment_launcher/launcher/_base.py index 20487ab9..a5864463 100644 --- a/src/aind_behavior_experiment_launcher/launcher/_base.py +++ b/src/aind_behavior_experiment_launcher/launcher/_base.py @@ -281,13 +281,7 @@ def main(self) -> None: return def _ui_prompt(self) -> Self: - self._ui_helper.make_header() - _str = ( - f"Rig ({self.rig_schema_model.__name__}) Schema Version: {self.rig_schema_model.model_construct().version}\n" - f"Session ({self.session_schema_model.__name__}) Schema Version: {self.session_schema_model.model_construct().version}\n" - f"TaskLogic ({self.task_logic_schema_model.__name__}) Schema Version: {self.task_logic_schema_model.model_construct().version}\n" - ) - logger.info(_str) + logger.info(self.make_header()) if self._debug_mode: self._print_debug() From 3aa708e557d83a6bc5884dd2cb2a61e92e7a23cd Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Sun, 16 Mar 2025 17:34:03 -0700 Subject: [PATCH 15/16] Refactor to dev --- examples/behavior_launcher.py | 3 +- .../gui_helper.py | 236 ------------------ .../launcher/behavior_launcher.py | 49 +--- .../{ => ui}/_magicgui.py | 0 .../ui/gui_helper.py | 127 ++++++++++ tests/test_ui.py | 12 - 6 files changed, 131 insertions(+), 296 deletions(-) delete mode 100644 src/aind_behavior_experiment_launcher/gui_helper.py rename src/aind_behavior_experiment_launcher/{ => ui}/_magicgui.py (100%) create mode 100644 src/aind_behavior_experiment_launcher/ui/gui_helper.py diff --git a/examples/behavior_launcher.py b/examples/behavior_launcher.py index debb2da6..890e73a4 100644 --- a/examples/behavior_launcher.py +++ b/examples/behavior_launcher.py @@ -17,6 +17,7 @@ BehaviorServicesFactoryManager, DefaultBehaviorPicker, ) +from aind_behavior_experiment_launcher.ui import gui_helper logger = logging.getLogger(__name__) @@ -104,7 +105,7 @@ def make_launcher(): session_schema_model=AindBehaviorSessionModel, task_logic_schema_model=TaskLogicModel, data_dir=data_dir, - picker=DefaultBehaviorPicker(config_library_dir=Path(LIB_CONFIG)), + picker=DefaultBehaviorPicker(config_library_dir=Path(LIB_CONFIG), ui_helper=gui_helper.GuiHelper()), temp_dir=Path(r"./local/.temp"), allow_dirty=True, skip_hardware_validation=True, diff --git a/src/aind_behavior_experiment_launcher/gui_helper.py b/src/aind_behavior_experiment_launcher/gui_helper.py deleted file mode 100644 index 8e29d341..00000000 --- a/src/aind_behavior_experiment_launcher/gui_helper.py +++ /dev/null @@ -1,236 +0,0 @@ -import logging -import os -from pathlib import Path -from typing import List, Optional, TypeVar - -import magicgui -import magicgui.types -from aind_behavior_services.db_utils import SubjectDataBase -from magicgui import widgets as widgets -from magicgui.types import Undefined, _Undefined -from magicgui.widgets.bases import BaseValueWidget -from qtpy.QtCore import Qt - -logger = logging.getLogger(__name__) - -T = TypeVar("T") - - -def prompt_pick_file_from_list( - available_files: list[os.PathLike[str]], - *, - allow_manual_input: bool = True, - prompt: str = "Select a file:", -) -> Optional[os.PathLike[str]]: - def _force_dst(src: BaseValueWidget, dst: BaseValueWidget): - with dst.changed.blocked(): - dst.set_value(Path(src.get_value())) - - container: widgets.Container = widgets.Container(layout="vertical") - - # Add button to submit - submit_button = widgets.Button(text="Submit") - submit_button.changed.connect(container.close) - container.append(submit_button) - - # Make the picker - _paths = [Path(x) for x in available_files] - _path_names = [x.name for x in _paths] - files = widgets.ComboBox( - choices=_path_names, - label=prompt, - ) - container.append(files) - file_picker = widgets.FileEdit( - mode=magicgui.types.FileDialogMode.EXISTING_FILE, - label="Or provide a file path:", - ) - container.append(file_picker) - file_picker.enabled = allow_manual_input - - files.changed.connect(lambda _: _force_dst(files, file_picker)) - file_picker.changed.connect(lambda _: _force_dst(file_picker, files)) - - container.native.setWindowTitle(prompt) - with magicgui.event_loop(): - container.show() - - if files.get_value() is not None: - return _paths[_path_names.index(files.get_value())] - - _p = file_picker.get_value() - if isinstance(_p, tuple): - _p = _p[0] - return _p - - -def prompt_pick_from_list( - values: list[str], - *, - prompt: str = "Select an option:", -) -> Optional[str]: - container: widgets.Container = widgets.Container(layout="vertical") - - # Add button to submit - submit_button = widgets.Button(text="Submit") - submit_button.changed.connect(container.close) - container.append(submit_button) - - # Make the picker - files = widgets.ComboBox( - value=values, - label=prompt, - ) - container.append(files) - file_picker = widgets.FileEdit( - mode=magicgui.types.FileDialogMode.EXISTING_FILE, - label="Or provide a file path:", - ) - container.append(file_picker) - - container.native.setWindowTitle(prompt) - with magicgui.event_loop(): - container.show() - - return files.get_value() - - -def prompt_yes_no_question(*, prompt: str = "Select one option:") -> Optional[bool]: - _container_outer: widgets.Container = widgets.Container(layout="vertical") - _container_inner: widgets.Container = widgets.Container(layout="horizontal") - _p = widgets.Label(value=prompt) - - _w_yes = widgets.Button(value=True, text="Yes") - _w_no = widgets.Button(value=False, text="No") - - _result: List[Optional[bool]] = [None] - _w_yes.changed.connect(lambda _: _result.__setitem__(0, True)) - _w_no.changed.connect(lambda _: _result.__setitem__(0, False)) - - _w_yes.changed.connect(_container_outer.close) - _w_no.changed.connect(_container_outer.close) - - _container_inner.append(_w_yes) - _container_inner.append(_w_no) - _container_outer.append(_p) - _container_outer.append(_container_inner) - - _container_outer.native.setWindowTitle(prompt) - with magicgui.event_loop(): - _container_outer.show() - - return _result[0] - - -def choose_subject(subject_list: SubjectDataBase) -> Optional[str]: - def _lock_to_index(index_widget: BaseValueWidget, dst: BaseValueWidget): - with dst.changed.blocked(): - dst.set_value(index_widget.get_value()) - - _subject = list(subject_list.subjects.keys()) - _task_logic = [str(getattr(x, "task_logic_target", None)) for x in subject_list.subjects.values()] - _choices = [f"{subject} => {task_logic}" for subject, task_logic in zip(_subject, _task_logic)] - - container: widgets.Container = widgets.Container(layout="vertical") - subjects = widgets.Select(choices=_choices, allow_multiple=False, label="Subject") - btn = widgets.Button(text="Submit") - btn.changed.connect(container.close) - - container.append(btn) - container.append(subjects) - - with magicgui.event_loop(): - container.show() - - v = subjects.get_value() - if v is None: - return None - else: - return _subject[_choices.index(v[0])] - - -def prompt_experimenter( - *, default: List[str] | _Undefined = Undefined, allow_empty_string: bool = True -) -> Optional[List[str]]: - def _close_if_valid(container: widgets.Container, list_edit: widgets.ListEdit): - if r := list_edit.get_value(): - if len(r) > 0: - container.close() - - if default is Undefined: - default = [""] - - container: widgets.Container = widgets.Container(layout="vertical") - w_experimenter = widgets.ListEdit(value=default) - btn = widgets.Button(text="Submit") - btn.changed.connect(lambda _: _close_if_valid(container, w_experimenter)) - container.append(w_experimenter) - container.append(btn) - - container.native.setWindowTitle("Enter experimenters: ") - with magicgui.event_loop(): - container.show() - - experimenter = w_experimenter.get_value() - if experimenter is None: - return None - experimenter = [x.strip() for x in experimenter if x.strip() != ""] - if len(experimenter) == 0: - if not allow_empty_string: - logger.error("Experimenter name is not valid.") - else: - return None - return experimenter - - -def prompt_get_notes() -> str: - container: widgets.Container = widgets.Container(layout="vertical") - - # Add button to submit - submit_button = widgets.Button(text="Submit") - submit_button.changed.connect(container.close) - container.append(submit_button) - - # Make the picker - notes_ui = widgets.TextEdit() - container.append(notes_ui) - container.native.setWindowTitle("Enter notes:") - - with magicgui.event_loop(): - container.show() - - return notes_ui.get_value() - - -def make_header() -> None: - _HEADER = """\n - ██████╗██╗ █████╗ ██████╗ ███████╗ - ██╔════╝██║ ██╔══██╗██╔══██╗██╔════╝ - ██║ ██║ ███████║██████╔╝█████╗ - ██║ ██║ ██╔══██║██╔══██╗██╔══╝ - ╚██████╗███████╗██║ ██║██████╔╝███████╗ - ╚═════╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚══════╝ - """ - - sub_text = """ - Command-line-interface Launcher for AIND Behavior Experiments - Press Control+C to exit at any time. - """ - - container = widgets.Container() - text_ui = widgets.Label(value=_HEADER) - text_ui.native.setTextFormat(Qt.TextFormat.MarkdownText) - font = text_ui.native.font() - font.setPointSize(15) - font.setFamily("Courier New") - text_ui.native.setFont(font) - sub_text_ui = widgets.Label(value=sub_text) - ok_ui = widgets.Button(text="Close") - container.append(text_ui) - container.append(sub_text_ui) - container.append(ok_ui) - ok_ui.clicked.connect(container.close) - - container.native.setWindowTitle("CLABE") - with magicgui.event_loop(): - container.show() diff --git a/src/aind_behavior_experiment_launcher/launcher/behavior_launcher.py b/src/aind_behavior_experiment_launcher/launcher/behavior_launcher.py index d29a1f83..be972985 100644 --- a/src/aind_behavior_experiment_launcher/launcher/behavior_launcher.py +++ b/src/aind_behavior_experiment_launcher/launcher/behavior_launcher.py @@ -521,7 +521,7 @@ def pick_rig(self) -> TRig: else: while True: try: - path = self.prompt_pick_file_from_list(available_rigs, prompt="Choose a rig:", zero_label=None) + path = self.ui_helper.prompt_pick_from_list(available_rigs, prompt="Choose a rig:") if not isinstance(path, str): raise ValueError("Invalid choice.") rig = model_from_json_file(path, self.launcher.rig_schema_model) @@ -606,7 +606,7 @@ def pick_task_logic(self) -> TTaskLogic: available_files = glob.glob(os.path.join(_path, "*.json")) if len(available_files) == 0: break - path = self.prompt_pick_file_from_list(available_files, prompt="Choose a task logic:", zero_label=None) + path = self.ui_helper.prompt_pick_from_list(available_files, prompt="Choose a task logic:") if not isinstance(path, str): raise ValueError("Invalid choice.") if not os.path.isfile(path): @@ -622,51 +622,6 @@ def pick_task_logic(self) -> TTaskLogic: raise ValueError("No task logic file found.") return task_logic - def prompt_pick_file_from_list( - self, - available_files: list[str], - prompt: str = "Choose a file:", - zero_label: Optional[str] = None, - zero_value: Optional[_T] = None, - zero_as_input: bool = True, - zero_as_input_label: str = "Enter manually", - ) -> Optional[str | _T]: - """ - Prompts the user to pick a file from a list of available files. - - Args: - available_files (list[str]): List of file paths to choose from. - prompt (str): The prompt message to display. - zero_label (Optional[str]): Label for the "zero" option. - zero_value (Optional[_T]): Value to return for the "zero" option. - zero_as_input (bool): Whether to allow manual input for the "zero" option. - zero_as_input_label (str): Label for manual input prompt. - - Returns: - Optional[str | _T]: The selected file path or the zero value. - - Raises: - ValueError: If an invalid choice is made. - """ - self.ui_helper.print(prompt) - if zero_label is not None: - self.ui_helper.print(f"0: {zero_label}") - for i, file in enumerate(available_files): - self.ui_helper.print(f"{i + 1}: {os.path.split(file)[1]}") - choice = int(input("Choice: ")) - if choice < 0 or choice >= len(available_files) + 1: - raise ValueError - if choice == 0: - if zero_label is None: - raise ValueError - else: - if zero_as_input: - return str(input(zero_as_input_label)) - else: - return zero_value - else: - return available_files[choice - 1] - def choose_subject(self, directory: str | os.PathLike) -> str: """ Prompts the user to select or manually enter a subject name. diff --git a/src/aind_behavior_experiment_launcher/_magicgui.py b/src/aind_behavior_experiment_launcher/ui/_magicgui.py similarity index 100% rename from src/aind_behavior_experiment_launcher/_magicgui.py rename to src/aind_behavior_experiment_launcher/ui/_magicgui.py diff --git a/src/aind_behavior_experiment_launcher/ui/gui_helper.py b/src/aind_behavior_experiment_launcher/ui/gui_helper.py new file mode 100644 index 00000000..454048b6 --- /dev/null +++ b/src/aind_behavior_experiment_launcher/ui/gui_helper.py @@ -0,0 +1,127 @@ +import logging +from typing import List, Optional, TypeVar + +import magicgui +import magicgui.types +from magicgui import widgets as widgets +from qtpy.QtCore import Qt +from typing_extensions import override + +from aind_behavior_experiment_launcher.ui import UiHelper + +logger = logging.getLogger(__name__) + +T = TypeVar("T") + + +class GuiHelper(UiHelper): + def prompt_pick_from_list( + self, value: List[str], prompt: str, allow_0_as_none: bool = True, **kwargs + ) -> Optional[str]: + container: widgets.Container = widgets.Container(layout="vertical") + + # Add button to submit + submit_button = widgets.Button(text="Submit") + submit_button.changed.connect(container.close) + container.append(submit_button) + + # Make the picker + files = widgets.ComboBox( + value=None if allow_0_as_none else value[0], + choices=[None] + value if allow_0_as_none else value, + label=prompt, + ) + container.append(files) + file_picker = widgets.FileEdit( + mode=magicgui.types.FileDialogMode.EXISTING_FILE, + label="Or provide a file path:", + ) + container.append(file_picker) + + container.native.setWindowTitle(prompt) + with magicgui.event_loop(): + container.show() + + return files.get_value() + + @override + def prompt_yes_no_question(self, prompt: str) -> bool: + _container_outer: widgets.Container = widgets.Container(layout="vertical") + _container_inner: widgets.Container = widgets.Container(layout="horizontal") + _p = widgets.Label(value=prompt) + + _w_yes = widgets.Button(value=True, text="Yes") + _w_no = widgets.Button(value=False, text="No") + + _result: List[Optional[bool]] = [None] + _w_yes.changed.connect(lambda _: _result.__setitem__(0, True)) + _w_no.changed.connect(lambda _: _result.__setitem__(0, False)) + + _w_yes.changed.connect(_container_outer.close) + _w_no.changed.connect(_container_outer.close) + + _container_inner.append(_w_yes) + _container_inner.append(_w_no) + _container_outer.append(_p) + _container_outer.append(_container_inner) + + _container_outer.native.setWindowTitle(prompt) + with magicgui.event_loop(): + _container_outer.show() + + if _result[0] is None: + return False + + return _result[0] + + def prompt_text(self, prompt: str) -> str: + container: widgets.Container = widgets.Container(layout="vertical") + + # Add button to submit + submit_button = widgets.Button(text="Submit") + submit_button.changed.connect(container.close) + container.append(submit_button) + + # Make the picker + notes_ui = widgets.TextEdit() + container.append(notes_ui) + container.native.setWindowTitle(f"{prompt}") + + with magicgui.event_loop(): + container.show() + + return notes_ui.get_value() + + +def make_header() -> None: + _HEADER = """\n + ██████╗██╗ █████╗ ██████╗ ███████╗ + ██╔════╝██║ ██╔══██╗██╔══██╗██╔════╝ + ██║ ██║ ███████║██████╔╝█████╗ + ██║ ██║ ██╔══██║██╔══██╗██╔══╝ + ╚██████╗███████╗██║ ██║██████╔╝███████╗ + ╚═════╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚══════╝ + """ + + sub_text = """ + Command-line-interface Launcher for AIND Behavior Experiments + Press Control+C to exit at any time. + """ + + container = widgets.Container() + text_ui = widgets.Label(value=_HEADER) + text_ui.native.setTextFormat(Qt.TextFormat.MarkdownText) + font = text_ui.native.font() + font.setPointSize(15) + font.setFamily("Courier New") + text_ui.native.setFont(font) + sub_text_ui = widgets.Label(value=sub_text) + ok_ui = widgets.Button(text="Close") + container.append(text_ui) + container.append(sub_text_ui) + container.append(ok_ui) + ok_ui.clicked.connect(container.close) + + container.native.setWindowTitle("CLABE") + with magicgui.event_loop(): + container.show() diff --git a/tests/test_ui.py b/tests/test_ui.py index 9d0ce74c..0bde7f94 100644 --- a/tests/test_ui.py +++ b/tests/test_ui.py @@ -34,18 +34,6 @@ def setUp(self): ) self.picker = self.launcher.picker - @patch("builtins.input", side_effect=["1"]) - def test_prompt_pick_file_from_list(self, mock_input): - files = ["file1.txt", "file2.txt"] - result = self.picker.prompt_pick_file_from_list(files) - self.assertEqual(result, "file1.txt") - - @patch("builtins.input", side_effect=["0", "manual_entry"]) - def test_prompt_pick_file_from_list_manual_entry(self, mock_input): - files = ["file1.txt", "file2.txt"] - result = self.picker.prompt_pick_file_from_list(files, zero_label="Manual Entry", zero_as_input=True) - self.assertEqual(result, "manual_entry") - @patch("os.path.isdir", return_value=True) @patch("os.listdir", return_value=["subjects/subject1", "subjects/subject2"]) @patch("builtins.input", side_effect=["1"]) From 145d71f6f57f449a9f074e41d32acacd7f2e8f54 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Sun, 16 Mar 2025 17:37:08 -0700 Subject: [PATCH 16/16] Remove reference to gui from example --- examples/behavior_launcher.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/behavior_launcher.py b/examples/behavior_launcher.py index 890e73a4..debb2da6 100644 --- a/examples/behavior_launcher.py +++ b/examples/behavior_launcher.py @@ -17,7 +17,6 @@ BehaviorServicesFactoryManager, DefaultBehaviorPicker, ) -from aind_behavior_experiment_launcher.ui import gui_helper logger = logging.getLogger(__name__) @@ -105,7 +104,7 @@ def make_launcher(): session_schema_model=AindBehaviorSessionModel, task_logic_schema_model=TaskLogicModel, data_dir=data_dir, - picker=DefaultBehaviorPicker(config_library_dir=Path(LIB_CONFIG), ui_helper=gui_helper.GuiHelper()), + picker=DefaultBehaviorPicker(config_library_dir=Path(LIB_CONFIG)), temp_dir=Path(r"./local/.temp"), allow_dirty=True, skip_hardware_validation=True,