interactive-pipe 0.8.7__tar.gz → 0.8.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. interactive_pipe-0.8.9/LICENSE +21 -0
  2. interactive_pipe-0.8.9/PKG-INFO +577 -0
  3. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/pyproject.toml +34 -9
  4. interactive_pipe-0.8.9/readme.md +536 -0
  5. interactive_pipe-0.8.9/src/interactive_pipe/__init__.py +43 -0
  6. interactive_pipe-0.8.9/src/interactive_pipe/core/backend.py +12 -0
  7. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/core/cache.py +7 -10
  8. interactive_pipe-0.8.9/src/interactive_pipe/core/context.py +471 -0
  9. interactive_pipe-0.8.9/src/interactive_pipe/core/engine.py +174 -0
  10. interactive_pipe-0.8.9/src/interactive_pipe/core/filter.py +191 -0
  11. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/core/graph.py +45 -20
  12. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/core/pipeline.py +74 -60
  13. interactive_pipe-0.8.9/src/interactive_pipe/core/signature.py +10 -0
  14. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/audio.py +29 -18
  15. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/curves.py +112 -91
  16. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/data.py +34 -26
  17. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/image.py +25 -12
  18. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/parameters.py +13 -14
  19. interactive_pipe-0.8.9/src/interactive_pipe/data_objects/table.py +328 -0
  20. interactive_pipe-0.8.9/src/interactive_pipe/graphical/gradio_control.py +201 -0
  21. interactive_pipe-0.8.9/src/interactive_pipe/graphical/gradio_gui.py +651 -0
  22. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/gui.py +102 -40
  23. interactive_pipe-0.8.9/src/interactive_pipe/graphical/mpl_control.py +162 -0
  24. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/mpl_gui.py +54 -44
  25. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/mpl_window.py +28 -11
  26. interactive_pipe-0.8.9/src/interactive_pipe/graphical/nb_control.py +167 -0
  27. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/nb_gui.py +18 -14
  28. interactive_pipe-0.8.9/src/interactive_pipe/graphical/qt_control.py +442 -0
  29. interactive_pipe-0.8.9/src/interactive_pipe/graphical/qt_gui.py +981 -0
  30. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/window.py +10 -7
  31. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/control.py +117 -49
  32. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/keyboard.py +33 -18
  33. interactive_pipe-0.8.9/src/interactive_pipe/headless/panel.py +141 -0
  34. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/pipeline.py +112 -83
  35. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/choose_backend.py +7 -5
  36. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/control_abbreviation.py +62 -72
  37. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/filter_decorator.py +22 -18
  38. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/keyword_args_analyzer.py +15 -26
  39. interactive_pipe-0.8.9/src/interactive_pipe/helper/pipeline_decorator.py +71 -0
  40. interactive_pipe-0.8.9/src/interactive_pipe.egg-info/PKG-INFO +577 -0
  41. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/SOURCES.txt +15 -3
  42. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/requires.txt +4 -7
  43. interactive_pipe-0.8.9/test/test_context.py +642 -0
  44. interactive_pipe-0.8.9/test/test_context_compatibility.py +414 -0
  45. interactive_pipe-0.8.9/test/test_control_abbreviations.py +83 -0
  46. interactive_pipe-0.8.9/test/test_controller.py +112 -0
  47. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/test/test_core.py +1 -3
  48. interactive_pipe-0.8.9/test/test_core_exceptions.py +229 -0
  49. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/test/test_curves.py +4 -6
  50. interactive_pipe-0.8.9/test/test_data_objects_exceptions.py +297 -0
  51. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/test/test_decorator.py +2 -1
  52. interactive_pipe-0.8.9/test/test_edge_cases.py +259 -0
  53. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/test/test_engine.py +6 -13
  54. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/test/test_filter.py +1 -4
  55. interactive_pipe-0.8.9/test/test_graphical_exceptions.py +326 -0
  56. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/test/test_headless.py +13 -17
  57. interactive_pipe-0.8.9/test/test_headless_exceptions.py +145 -0
  58. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/test/test_image.py +2 -2
  59. interactive_pipe-0.8.9/test/test_legacy_warnings.py +154 -0
  60. interactive_pipe-0.8.9/test/test_mutable_defaults.py +135 -0
  61. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/test/test_parameters.py +2 -2
  62. interactive_pipe-0.8.9/test/test_table.py +397 -0
  63. interactive_pipe-0.8.7/LICENSE +0 -24
  64. interactive_pipe-0.8.7/PKG-INFO +0 -413
  65. interactive_pipe-0.8.7/readme.md +0 -369
  66. interactive_pipe-0.8.7/src/interactive_pipe/__init__.py +0 -21
  67. interactive_pipe-0.8.7/src/interactive_pipe/core/engine.py +0 -92
  68. interactive_pipe-0.8.7/src/interactive_pipe/core/filter.py +0 -162
  69. interactive_pipe-0.8.7/src/interactive_pipe/core/signature.py +0 -18
  70. interactive_pipe-0.8.7/src/interactive_pipe/graphical/gradio_control.py +0 -127
  71. interactive_pipe-0.8.7/src/interactive_pipe/graphical/gradio_gui.py +0 -409
  72. interactive_pipe-0.8.7/src/interactive_pipe/graphical/mpl_control.py +0 -126
  73. interactive_pipe-0.8.7/src/interactive_pipe/graphical/nb_control.py +0 -109
  74. interactive_pipe-0.8.7/src/interactive_pipe/graphical/qt_control.py +0 -344
  75. interactive_pipe-0.8.7/src/interactive_pipe/graphical/qt_gui.py +0 -630
  76. interactive_pipe-0.8.7/src/interactive_pipe/helper/pipeline_decorator.py +0 -64
  77. interactive_pipe-0.8.7/src/interactive_pipe/thirdparty/images_openai_api.py +0 -159
  78. interactive_pipe-0.8.7/src/interactive_pipe/thirdparty/music_spotify.py +0 -55
  79. interactive_pipe-0.8.7/src/interactive_pipe.egg-info/PKG-INFO +0 -413
  80. interactive_pipe-0.8.7/test/test_control_abbreviations.py +0 -110
  81. interactive_pipe-0.8.7/test/test_controller.py +0 -33
  82. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/setup.cfg +0 -0
  83. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/core/__init__.py +0 -0
  84. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/__init__.py +0 -0
  85. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/__init__.py +0 -0
  86. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/__init__.py +0 -0
  87. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/__init__.py +0 -0
  88. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/_private.py +0 -0
  89. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/dependency_links.txt +0 -0
  90. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/top_level.txt +0 -0
  91. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/test/test_cache.py +0 -0
  92. {interactive_pipe-0.8.7 → interactive_pipe-0.8.9}/test/test_recorder.py +7 -7
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Balthazar Neveu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,577 @@
1
+ Metadata-Version: 2.4
2
+ Name: interactive_pipe
3
+ Version: 0.8.9
4
+ Summary: Library to create flexible interactive image processing pipelines and automatically add a graphical user interface without knowing anything about GUI coding!
5
+ Author-email: Balthazar Neveu <balthazarneveu@gmail.com>
6
+ Project-URL: Homepage, https://github.com/balthazarneveu/interactive_pipe
7
+ Project-URL: Bug Tracker, https://github.com/balthazarneveu/interactive_pipe/issues
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.7
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: matplotlib>=3.5.3
15
+ Requires-Dist: numpy>=1.21.6
16
+ Requires-Dist: Pillow>=9.0.1
17
+ Requires-Dist: PyYAML>=5.4.1
18
+ Provides-Extra: qt6
19
+ Requires-Dist: PyQt6>=6.5.2; extra == "qt6"
20
+ Requires-Dist: PyQt6_sip>=13.5.2; extra == "qt6"
21
+ Provides-Extra: qt5
22
+ Requires-Dist: PyQt5>=5.15.9; extra == "qt5"
23
+ Requires-Dist: PyQt5_sip>=12.12.1; extra == "qt5"
24
+ Provides-Extra: notebook
25
+ Requires-Dist: ipywidgets>=7.7.1; extra == "notebook"
26
+ Provides-Extra: pytest
27
+ Requires-Dist: opencv_python_headless>=4.8.1.78; extra == "pytest"
28
+ Requires-Dist: pytest>=6.2.5; extra == "pytest"
29
+ Provides-Extra: full
30
+ Requires-Dist: PyQt6>=6.5.2; extra == "full"
31
+ Requires-Dist: PyQt6_sip>=13.5.2; extra == "full"
32
+ Requires-Dist: opencv_python_headless>=4.8.1.78; extra == "full"
33
+ Requires-Dist: pytest>=6.2.5; extra == "full"
34
+ Requires-Dist: ipywidgets>=7.7.1; extra == "full"
35
+ Requires-Dist: pandas; extra == "full"
36
+ Requires-Dist: gradio; extra == "full"
37
+ Provides-Extra: dev
38
+ Requires-Dist: ruff>=0.9.0; extra == "dev"
39
+ Requires-Dist: pyright>=1.1.390; extra == "dev"
40
+ Dynamic: license-file
41
+
42
+
43
+ <center>
44
+
45
+ | ![Interactive pipe](/static/interact-pipe-logo-horizontal-rgb.svg) |
46
+ |:--:|
47
+ |Quick setup `pip install interactive-pipe` |
48
+ | [Project website](https://balthazarneveu.github.io/interactive_pipe/) |
49
+ |[![Interactive pipe python package](https://github.com/balthazarneveu/interactive_pipe/actions/workflows/pytest.yaml/badge.svg)](https://github.com/balthazarneveu/interactive_pipe/actions/workflows/pytest.yaml) |
50
+
51
+ </center>
52
+
53
+ # Interactive-pipe code
54
+ ### Concept
55
+
56
+
57
+ - Develop an algorithm while debugging visually with plots, while checking robustness & continuity to parameters change.
58
+ - Magically create a graphical interface to easily demonstrate a concept or simply tune your algorithm.
59
+
60
+ ❤️ **You do not need to learn anything about making a graphical user interface (GUI)** ❤️
61
+
62
+
63
+ ### Examples
64
+
65
+
66
+ | Science notebook | Toddler DIY Jukebox on a raspberry Pi |
67
+ |:-----: |:-----:|
68
+ | ![science notebook](/doc/images/scientific_notebook.jpg) | ![jukebox](/doc/images/jukebox.jpg)
69
+ | Sliders are added automatically in your jupyter notebook. This works on Google Collab and the code takes about 40 lines of code. No Widgets, event handlers or matplotlib knowledge required.| Plays some music when you touch the icon. Caption added through the title mechanism. Music samples generated by prompting [MusicGen](https://huggingface.co/spaces/facebook/MusicGen) |
70
+ | [Demo notebook on collab](https://colab.research.google.com/drive/1AwHyjZH8MnzZqwsvbmxBoB15btuMIwtk?usp=sharing) | [jukebox_demo.py demo code](/demo/jukebox_demo.py)|
71
+
72
+
73
+ ----------------------
74
+
75
+ ### Local setup
76
+ ```
77
+ git clone git@github.com:balthazarneveu/interactive_pipe.git
78
+ cd interactive-pipe
79
+ pip install -e ".[full]"
80
+ ```
81
+
82
+ ----------------------
83
+
84
+ ### Who is this for?
85
+ #### 🎓 Scientific education
86
+ - Demonstrate concepts by interacting with curves / images.
87
+ - Easy integration in Jupyter Notebooks (popular with Google Collab)
88
+ #### 🎁 DIY hobbyist
89
+ - You can also use the declarative nature of interactive pipe to make a graphical interface in a few lines of codes.
90
+ - For instance, it is possible to code a jukebox for a toddler on a RaspberryPi.
91
+ #### 📷 Engineering *(computer vision, image/signal processing)*
92
+ - While prototyping an algorithm or testing a neural network, you may be interested in making small experiments with visual checks. Instead of making a draft quick & dirty code that you'll never keep, you can use interactive pipe to show your team how your library works. A visual demo is always good, it shows that the algorithm is not buggy if anyone can play with it.
93
+ - Tune your algorithms with a graphical interface and save your parameters for later batch processing.
94
+ - Ready to batch under the hood, the processing engine can be ran without GUI (therefore allowing to use the same code for tuning & batch processing if needed).
95
+ - Do not spoil your production code with a huge amount of graphical interface code, keep your algorithms library untouched and simply decorate it.
96
+
97
+
98
+
99
+
100
+ ----------------------
101
+
102
+ ## 📜 Features
103
+
104
+ **Version 0.8.9**
105
+ - Modular multi-image processing filters
106
+ - Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
107
+ - Support in jupyter notebooks
108
+ - Tuning sliders & check buttons with a GUI
109
+ - Cache intermediate results in RAM for much faster processing
110
+ - `KeyboardControl` : no slider on UI but exactly the same internal mechanism, update on key press.
111
+ - Support Curve plots (2D signals).
112
+ - Gradio backend (+allows sharing with others).
113
+ - Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
114
+ - Circular sliders for Qt Backend
115
+ - Text prompt (`free_text=("Hello world!", None),`)
116
+ - TimeControl (possibility to play/pause time using an incrementing timer)
117
+ - 🆕 **Context API**: Direct access to shared context across filters via `get_context()`, `context`, `layout`, `audio`
118
+ - 🆕 MIT License
119
+ - 🆕 Panel **Panel System**: group the sliders in defined panels. allows fine control on GUI layout.
120
+ - 🆕 Support Table outputs
121
+
122
+
123
+
124
+ #### ⌨️ Keyboard shortcuts
125
+ Shortcuts while using the GUI (QT & matplotlib backends)
126
+
127
+ - `F1` to show the help shortcuts in the terminal
128
+ - `F11` toggle fullscreen mode
129
+ - `W` to write full resolution image to disk
130
+ - `R` to reset parameters
131
+ - `I` to print parameters dictionary in the command line
132
+ - `E` to export parameters dictionary to a yaml file
133
+ - `O` to import parameters dictionary from a yaml file (sliders will update)
134
+ - `G` to export a pipeline diagram for your interactive pipe (requires graphviz)
135
+
136
+
137
+
138
+ # Status
139
+ - supported backends
140
+ - ✅ `gui='qt'` pyQt/pySide
141
+ - ✅ `gui='mpl'` matplotlib
142
+ - ✅ `gui='nb'` ipywidget for jupyter notebooks
143
+ - 🧪 `gui='gradio'` gradio wrapping (+use `share_gradio_app=True` to share your app with others)
144
+ - tested platforms
145
+ - ✅ Linux (Ubuntu / KDE Neon)
146
+ - ✅ RapsberryPi
147
+ - ✅ On google collab (use `gui='nb'`)
148
+
149
+
150
+
151
+ | ⭐ | *PyQt / PySide* | *Matplotlib* | *Jupyter notebooks including Google collab* | *Gradio* |
152
+ |:-----: |:-----:|:------:|:----: |:----: |
153
+ | Backend name | `qt` | `mpl` | `nb`| `gradio` |
154
+ | Preview | ![qt backend](/doc/images/qt_backend.jpg) | ![mpl backend](/doc/images/mpl_backend.jpg) | ![nb backend](/doc/images/notebook_backend.jpg) | ![mpl backend](/doc/images/gradio_backend.jpg)|
155
+ | Plot curves | ✅ | ✅ | ✅ | ✅|
156
+ | Change layout | ✅ | ✅ | ✅ | ➖ |
157
+ | Keyboard shortcuts / fullscreen| ✅ | ✅ | ➖ | ➖ |
158
+ | Audio support | ✅ | ➖ | ➖ | ✅ |
159
+ | Image buttons| ✅ | ➖ | ➖ | ➖ |
160
+ | Circular slider| ✅ | ➖ | ➖ | ➖ |
161
+ | Collapsible Panels | ✅ | ➖ | ➖ | ✅ |
162
+
163
+
164
+
165
+ # Tutorials
166
+ ### [Main tutorial](https://huggingface.co/spaces/balthou/interactive-pipe-tutorial)
167
+ ![tuto](/doc/images/tutorial.png)
168
+
169
+ [Tutorial on Hugging Face space](https://huggingface.co/spaces/balthou/interactive-pipe-tutorial)
170
+
171
+
172
+ [Tutorial in a Colab notebook](https://colab.research.google.com/github/livalgo/interactive-pipe-examples/blob/main/interactive_pipe_tutorial.ipynb#scrollTo=U38Oh7coVKwf)
173
+
174
+ ### Learn by examples
175
+ #### [Basic image processing (python code sample for PyQT GUI)](/demo/multi_image.py)
176
+ | GUI | Pipeline |
177
+ |:--:|:--:|
178
+ |![](/doc/images/image_selector.png) | ![](/doc/images/image_selector_graph.png) |
179
+
180
+ ![](/doc/images/demo_multi_image.gif)
181
+
182
+ #### [Speech exploration notebook (colab, signal processing)](https://colab.research.google.com/drive/1mUX2FW0qflWn-v3nIx90P_KvRxnXlBpz#scrollTo=qDTaIwvaJQ6R)
183
+ ![Speech processing exploration in a notebook](/doc/images/jupyter_integration_speech.png)
184
+
185
+
186
+
187
+
188
+ ## 🚀 Ultra short code
189
+
190
+
191
+ *Since ipywidgets in notebooks are supported, the tutorial is also available in a [google collab notebook](https://colab.research.google.com/drive/1PZn8P_5TABVCugT3IcLespvZG-gxnFbO?usp=sharing)*
192
+
193
+
194
+ Let's define 3 image processing very basic filters `exposure`, `black_and_white` & `blend`.
195
+
196
+ By design:
197
+ - image buffers inputs are arguments
198
+ - keyword arguments are the parameters which can be later turned into interactive widgets.
199
+ - output buffers are simply returned like you'd do in a regular function.
200
+
201
+ We use the `@interactive()` decorator to specify which parameters become interactive widgets. Parameters defined in the decorator as **tuple/list** will become graphical interactive widgets (slider, tick box, dropdown menu).
202
+
203
+ The syntax to turn keyword arguments into sliders is pretty simple: `@interactive(param=(default, [min, max], name))` will create a float slider for instance.
204
+
205
+ Finally, we need to the glue to combo these filters. This is where the sample_pipeline function comes in.
206
+
207
+ By decorating it with `@interactive_pipeline(gui="qt")`, calling this function will magically turn into a GUI powered image processing pipeline.
208
+
209
+
210
+ ```python
211
+ from interactive_pipe import interactive, interactive_pipeline
212
+ import numpy as np
213
+
214
+ @interactive(
215
+ coeff=(1., [0.5, 2.], "exposure"),
216
+ bias=(0., [-0.2, 0.2])
217
+ )
218
+ def exposure(img, coeff=1., bias=0.):
219
+ '''Applies a multiplication by coeff & adds a constant bias to the image'''
220
+ # In the GUI, the coeff will be labelled as "exposure".
221
+ # As the default tuple provided to bias does not end up with a string,
222
+ # the widget label will be "bias", simply named after the keyword arg.
223
+ return img*coeff + bias
224
+
225
+
226
+ @interactive(bnw=(True, "black and white"))
227
+ def black_and_white(img, bnw=True):
228
+ '''Averages the 3 color channels (Black & White) if bnw=True
229
+ '''
230
+ # Special mention for booleans: using a tuple like (True,) allows creating the tick box.
231
+ return np.repeat(np.expand_dims(np.average(img, axis=-1), -1), img.shape[-1], axis=-1) if bnw else img
232
+
233
+ @interactive(blend_coeff=(0.5, [0., 1.]))
234
+ def blend(img0, img1, blend_coeff=0.5):
235
+ '''Blends between two image.
236
+ - when blend_coeff=0 -> image 0 [slider to the left ]
237
+ - when blend_coeff=1 -> image 1 [slider to the right]
238
+ '''
239
+ return (1-blend_coeff)*img0+ blend_coeff*img1
240
+
241
+ # you can change the backend to mpl instead of Qt here.
242
+ @interactive_pipeline(gui="qt", size="fullscreen")
243
+ def sample_pipeline(input_image):
244
+ exposed = exposure(input_image)
245
+ bnw_image = black_and_white(input_image)
246
+ blended = blend(exposed, bnw_image)
247
+ return exposed, blended, bnw_image
248
+
249
+ if __name__ == '__main__':
250
+ input_image = np.array([0., 0.5, 0.8])*np.ones((256, 512, 3))
251
+ sample_pipeline(input_image)
252
+
253
+ ```
254
+ ❤️ This code shall display you a GUI with three images. The middle one is the result of the blend
255
+
256
+
257
+
258
+ Notes:
259
+ - If you write `@interactive()` with `def blend(img0, img1, blend_coeff=0.5):`, blend_coeff will simply not be a slider on the GUI.
260
+ - If you write `@interactive(blend_coeff=[0., 1.])` in the decorator, blend_coeff will be a slider initialized to 0.5
261
+ - If you write `@interactive(bnw=(True, "black and white", "k"))`, the checkbox will disappear and be replaced by a keypress event (press `k` to enable/disable black & white)
262
+
263
+ -----------
264
+ ## 💡 Some more tips
265
+
266
+ ```python
267
+ from interactive_pipe import interactive, interactive_pipeline, context
268
+ import numpy as np
269
+
270
+ COLOR_DICT = {"red": [1., 0., 0.], "green": [0., 1.,0.], "blue": [0., 0., 1.], "gray": [0.5, 0.5, 0.5]}
271
+ @interactive(color_choice=["red", "green", "blue", "gray"])
272
+ def generate_flat_colored_image(color_choice="red"):
273
+ '''Generate a constant colorful image
274
+ '''
275
+ flat_array = np.array(COLOR_DICT.get(color_choice)) * np.ones((64, 64, 3))
276
+ context["avg"] = np.average(flat_array)
277
+ return flat_array
278
+ ```
279
+
280
+ - Note that you can also create filters which take no inputs and simply "generate" images.
281
+ - The `color_choice` list will be turned into a nice dropdown menu. Default value here will be red as this is the first element of the list!
282
+ ----------
283
+
284
+ 💡 Can filters communicate together?
285
+ Yes, using the `context` proxy from `interactive_pipe`.
286
+ - Check carefully how we stored the image average of the flat image in context.
287
+ - This value will be available to other filters.
288
+ `special_image_slice` is going to use that value to set the half bottom image to dark in case the average is high.
289
+
290
+ ```python
291
+ def special_image_slice(img):
292
+ out_img = img.copy()
293
+ if context["avg"] > 0.4:
294
+ out_img[out_img.shape[0]//2:, ...] = 0.
295
+ return out_img
296
+ ```
297
+ ---
298
+
299
+
300
+ ```python
301
+ @interactive(image_index=(0, [0, 2], None, ["pagedown", "pageup", True]))
302
+ def switch_image(img1, img2, img3, image_index=0):
303
+ '''Switch between 3 images
304
+ '''
305
+ return [img1, img2, img3][image_index]
306
+ ```
307
+ Note that you can create a filter to switch between several images. In `["pagedown", "pageup", True]`, True means that the image_index will wrap around. (it will return to 0 as soon as it goes above the maximum value of 2).
308
+
309
+ ```python
310
+ @interactive(top_slice_black=(True, "special", "k"))
311
+ def black_top_image_slice(img, top_slice_black=True):
312
+ out_img = img.copy()
313
+ if top_slice_black:
314
+ out_img[:out_img.shape[0]//2, ...] = 0.
315
+ return out_img
316
+
317
+
318
+ @interactive_pipeline(gui="qt", size="fullscreen")
319
+ def sample_pipeline_generated_image():
320
+ flat_img = generate_flat_colored_image()
321
+ top_slice_modified = black_top_image_slice(flat_img)
322
+ bottom_slice_modified_image = special_image_slice(flat_img)
323
+ chosen = switch_image(flat_img, top_slice_modified, bottom_slice_modified_image)
324
+ return chosen
325
+
326
+ if __name__ == '__main__':
327
+ sample_pipeline_generated_image()
328
+ ```
329
+
330
+ ----------
331
+
332
+ ### Release Notes
333
+
334
+
335
+ #### Version 0.8.9 (February 2026)
336
+
337
+ **New Features:**
338
+ - **Panel System**: Control panel layout and organization
339
+ - Flexible panel positioning (left, right, top, bottom)
340
+ - Detached control panels for separate windows
341
+ - Nested panels and subpanels support
342
+ - Grouped controls within panels
343
+ - Improved spacing and borders for better visual organization
344
+ - Full backend support (Qt, Gradio, matplotlib, notebook)
345
+
346
+ - **Table Data Type**: Display tabular data natively
347
+ - Core Table functionality without external dependencies
348
+ - Optional pandas DataFrame support for advanced use cases
349
+ - Rendering support across all backends (Qt, Gradio, matplotlib)
350
+ - Headerless tables option
351
+
352
+ - **TimeControl Enhancements**: Better time-based parameter control
353
+ - Improved slider help display
354
+ - Additional demos showcasing time-based animations
355
+
356
+ **API Improvements:**
357
+ - Context support at pipeline initialization
358
+ - Backend selection via enum (string format still supported)
359
+ - Graph visualization for GUI pipelines (press `G`)
360
+
361
+ **Deprecations:**
362
+ - Inline syntax deprecated (use decorator syntax instead)
363
+ - `output_canvas` argument removed
364
+ - Context aliases (`global_params`, `states` etc...) deprecated at initialization
365
+
366
+ #### Version 0.8.8 (January 2026)
367
+
368
+ **New Features:**
369
+ - **Clean Context API**: Access shared context directly without `global_params` pollution
370
+ - `get_context()` - Get the shared context dictionary
371
+ - `context` - Direct dict-like access to context
372
+ - `layout` - Access layout configuration directly
373
+ - `audio` - Access audio functionality directly
374
+
375
+ **Code Quality Improvements:**
376
+ - Replaced all assertions with proper exceptions (`ValueError`, `TypeError`, `RuntimeError`)
377
+ - Fixed all mutable default arguments across the codebase (prevents shared state bugs)
378
+ - Improved type hints with proper `Optional` and `Any` types
379
+ - Better error messages for debugging
380
+
381
+ **UX Improvements:**
382
+ - Dropdown menus are now hidden when only a single choice is available
383
+ - Helpful message displayed when Graphviz is not available (when pressing `G`)
384
+ - Fixed warning in linestyle for curves
385
+
386
+ **Bug Fixes:**
387
+ - Fixed audio initialization order in Qt backend
388
+ - Fixed pytest failures for optional dependencies in CI
389
+ - Fixed various edge cases in error handling
390
+
391
+ **Migration from old `context={}` or `global_params={}` patterns:**
392
+
393
+ ```python
394
+ # OLD (deprecated) - using context={} or global_params={}
395
+ from interactive_pipe import interactive
396
+
397
+ @interactive(brightness=(0.5, [0., 1.]))
398
+ def apply_brightness(img:np.ndarray, brightness: float = 0.5, global_params={}):
399
+ global_params["brightness"] = brightness # Storing shared data
400
+ global_params["__output_styles"]["output"] = {"title": "Brightened"} # Setting layout
401
+ return img * brightness
402
+
403
+ # NEW (recommended) - using context and layout proxies
404
+ from interactive_pipe import interactive, context, layout
405
+
406
+ @interactive(brightness=(0.5, [0., 1.]))
407
+ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
408
+ context["brightness"] = brightness # or: context.brightness = brightness
409
+ layout.style("output", title="Brightened")
410
+ return img * brightness
411
+ ```
412
+
413
+ The new API provides:
414
+ - `context` - For sharing data between filters (replaces `global_params["key"]`)
415
+ - `layout` - For controlling output display (replaces `global_params["__output_styles"]`)
416
+ - `audio` - For audio playback control
417
+ - `get_context()` - Get the shared context dictionary directly
418
+
419
+
420
+
421
+
422
+ **License:**
423
+ - Updated to MIT License
424
+
425
+ ---
426
+
427
+ ### History
428
+ - Interactive pipe was initially developed by [Balthazar Neveu](https://github.com/balthazarneveu) as part of the [irdrone project](https://github.com/wisescootering/infrareddrone/tree/master/interactive) based on matplotlib.
429
+ - Later, more contributions were also made by [Giuseppe Moschetti](https://github.com/g-moschetti) and Sylvain Leroy.
430
+ - August 2023: rewriting the whole core and supporting several graphical backends!
431
+ - September 2024: Gradio backend
432
+ - January 2026: Clean Context API and code quality improvements (v0.8.8)
433
+
434
+
435
+ ### FAQ
436
+ - ❓ What is the recommended way to access shared context?
437
+ > **New in v0.8.8**: Use the clean context API for direct access:
438
+ > ```python
439
+ > from interactive_pipe import context, layout, audio, get_context
440
+ >
441
+ > @interactive()
442
+ > def my_filter(img):
443
+ > context["shared_key"] = "shared_value" # Direct dict-like access
444
+ > context.brightness = 0.5
445
+ > layout.set_title("output_image", "My Image") # Layout helpers
446
+ >
447
+ > return img
448
+ > ```
449
+
450
+ - ❓ How do I change the layout? *Can I change the grid layout of images live? (like you compare 2 images side by side and you want to start comparing 4 images in a 2x2 fashion for debugging purpose)*. It is possible with Qt backend.
451
+
452
+ > Use the `layout` helper to control image arrangement and styling:
453
+ > ```python
454
+ > from interactive_pipe import layout
455
+ >
456
+ > def change_layout(layout: str="side_by_side"):
457
+ > # Arrange outputs in a 2x2 grid
458
+ > if layout == "side_by_side":
459
+ > layout.grid([["input", "result"]])
460
+ > if layout == "grid2x2":
461
+ > layout.grid([["input", "processed"], ["histogram_graph", "result"]])
462
+ > # Style individual outputs
463
+ > layout.style("result", title="Final Result")
464
+ > # Note that the string "input", "processed", "histogram_graph", "result"
465
+ > # are the variables used in the pipeline (see below)!
466
+ >
467
+ > def pipeline(input):
468
+ > processed = denoise(input)
469
+ > result = change_brightness(processed)
470
+ > histogram_graph = compute_histo(processed)
471
+ > change_layout()
472
+ > return result
473
+ > ```
474
+
475
+ - ❓ Do I have to remove `KeyboardSlider` when using gradio or notebook backends?
476
+ > No, don't worry, these will be mapped back to regular sliders!
477
+ - ❓ How do I play audio live?
478
+ > 🔊 Inside a processing block, write the audio file to disk and use the audio helper:
479
+ > ```python
480
+ > from interactive_pipe import audio
481
+ > audio.set_audio(audio_file) # New clean API (v0.8.8)
482
+ > # or legacy: context["__set_audio"](audio_file)
483
+ > ```
484
+ - ❓ Do I have to decorate my processing block using the `@interactive`
485
+ > If you use the `@` decoration style, your function won't be useable in a regular manner (wich may be problematic in a serious development environment)
486
+ ```python
487
+ @interactive(angle=(0., [-360., 360.]))
488
+ def processing_block(angle=0.):
489
+ ...
490
+ ```
491
+
492
+ > An alternative is to decorate the processing block outside... in a file dedicated to interactivity for instance
493
+ ```python
494
+ # core_filter.py
495
+ def processing_block(angle=0.):
496
+ ...
497
+ ```
498
+
499
+ ```python
500
+ # graphical.py
501
+ from core_filter import processing_block
502
+
503
+ def add_interactivity():
504
+ interactive(angle=(0., [-360., 360.]))(processing_block)
505
+ ```
506
+ - ❓ Can I call the pipeline in a command line/batch fashion?
507
+ > Yes, headless mode is supported. 🔜 documentation needed.
508
+
509
+
510
+ - ❓ Can I use inplace operations?
511
+ > Better avoid these in general. To avoid making extra copies, computing hashes everywhere and avoid loosing precious computation time, there are no checks that inputs are not modified in place.
512
+ ```python
513
+ # Don't do that!
514
+ def bad_processing_block(inp):
515
+ inp+=1
516
+ ```
517
+
518
+ - ❓ Is there a difference between `global_params` and `context` ?
519
+ > No, `global_params`, `global_parameters`, `global_state`, `global_context`, `context`, `state` all mean the same thing and are all supported for legacy reasons. `context` is the preferred wording. However, we now recommend using the clean context API (see above).
520
+ > ⚠️ The old `global_params={}` / `context={}` keyword argument style still works for backwards compatibility but is deprecated.
521
+
522
+
523
+
524
+ # Roadmap and todos
525
+ 🐛 Want to contribute or interested in adding new features? Enter a new [Github issue](https://github.com/balthazarneveu/interactive_pipe/issues)
526
+
527
+ 🎁 Want to dig into the code? Take a look at [code_architecture.md](/code_architecture.md)
528
+
529
+
530
+
531
+ ----------------------
532
+
533
+ ### Development
534
+
535
+ #### Code quality checks
536
+
537
+ Before committing, ensure your code passes the linters, type checker, and tests. The CI runs these checks automatically:
538
+
539
+ **What CI does:**
540
+ - **Ruff formatting check** (`.github/workflows/ruff-format.yaml`): Runs `ruff format --check` to verify code formatting
541
+ - **Ruff linting** (`.github/workflows/ruff-lint.yaml`): Runs `ruff check` to check code quality (replaces flake8)
542
+ - **Pyright type checking** (`.github/workflows/pyright.yaml`): Runs `pyright` for static type checking (informational, non-blocking)
543
+ - **Pytest tests** (`.github/workflows/pytest.yaml`): Runs `pytest` on Python 3.9, 3.10, and 3.11
544
+
545
+ **Local commands (match CI):**
546
+
547
+ ```bash
548
+ # Install development tools and test dependencies
549
+ pip install -e ".[dev,pytest]"
550
+
551
+ # Format code (Ruff) - matches CI
552
+ ruff format .
553
+
554
+ # Check formatting (Ruff) - matches CI
555
+ ruff format --check .
556
+
557
+ # Lint code (Ruff) - matches CI (auto-fixes when possible)
558
+ ruff check .
559
+
560
+ # Auto-fix linting issues (Ruff)
561
+ ruff check --fix .
562
+
563
+ # Type check (Pyright) - matches CI (informational)
564
+ pyright src/
565
+
566
+ # Run tests (pytest) - matches CI
567
+ pytest
568
+
569
+ # Pre-commit checklist (run all before committing)
570
+ ruff format .
571
+ ruff check --fix .
572
+ pyright src/ # Optional, won't block commit
573
+ pytest
574
+ ```
575
+
576
+ **Note:** Ruff replaces both Black (formatting) and Flake8 (linting) in a single, faster tool. Pyright provides static type checking to catch type errors early.
577
+
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "interactive_pipe"
8
- version = "0.8.7"
8
+ version = "0.8.9"
9
9
  authors = [
10
10
  { name="Balthazar Neveu", email="balthazarneveu@gmail.com" },
11
11
  ]
@@ -14,13 +14,6 @@ dependencies = [
14
14
  "numpy>=1.21.6",
15
15
  "Pillow>=9.0.1",
16
16
  "PyYAML>=5.4.1",
17
- "PyQt6>=6.5.2",
18
- "PyQt6_sip>=13.5.2",
19
- "opencv_python_headless>=4.8.1.78",
20
- "pytest>=6.2.5",
21
- "ipywidgets>=7.7.1",
22
- "pandas",
23
- "gradio",
24
17
  ]
25
18
 
26
19
  description = "Library to create flexible interactive image processing pipelines and automatically add a graphical user interface without knowing anything about GUI coding!"
@@ -28,7 +21,7 @@ readme = "readme.md"
28
21
  requires-python = ">=3.7"
29
22
  classifiers = [
30
23
  "Programming Language :: Python :: 3",
31
- "License :: OSI Approved :: The Unlicense (Unlicense)",
24
+ "License :: OSI Approved :: MIT License",
32
25
  "Operating System :: OS Independent",
33
26
  ]
34
27
 
@@ -65,3 +58,35 @@ full=[
65
58
  "pandas",
66
59
  "gradio",
67
60
  ]
61
+
62
+ dev=[
63
+ "ruff>=0.9.0",
64
+ "pyright>=1.1.390",
65
+ ]
66
+
67
+ [tool.ruff]
68
+ line-length = 120
69
+ target-version = "py39"
70
+ exclude = ["__init__.py", "*/__pycache__/*", "*venv*/*"]
71
+
72
+ [tool.ruff.lint]
73
+ select = ["E", "F", "W", "I"] # E/F/W = flake8 rules, I = isort
74
+ ignore = ["E203"] # E203: whitespace before ':' (conflicts with black formatting)
75
+
76
+ [tool.ruff.lint.isort]
77
+ # isort compatibility
78
+
79
+ [tool.ruff.format]
80
+ # Black-compatible formatting (default)
81
+
82
+ [tool.pyright]
83
+ pythonVersion = "3.9"
84
+ typeCheckingMode = "basic"
85
+ include = ["src"]
86
+ exclude = ["**/venv/**", "**/__pycache__/**", "test/**"]
87
+ reportMissingImports = "none" # Qt libs and other optional dependencies
88
+ reportMissingTypeStubs = false
89
+ reportUnknownMemberType = false
90
+ reportUnknownArgumentType = false
91
+ reportUnknownVariableType = false
92
+ reportPrivateUsage = false