interactive-pipe 0.8.8__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 (80) hide show
  1. {interactive_pipe-0.8.8/src/interactive_pipe.egg-info → interactive_pipe-0.8.9}/PKG-INFO +150 -118
  2. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/pyproject.toml +33 -8
  3. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/readme.md +146 -110
  4. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/__init__.py +25 -15
  5. interactive_pipe-0.8.9/src/interactive_pipe/core/backend.py +12 -0
  6. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/cache.py +2 -6
  7. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/context.py +17 -17
  8. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/engine.py +10 -17
  9. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/filter.py +23 -42
  10. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/graph.py +36 -21
  11. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/pipeline.py +44 -52
  12. interactive_pipe-0.8.9/src/interactive_pipe/core/signature.py +10 -0
  13. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/audio.py +16 -14
  14. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/curves.py +55 -73
  15. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/data.py +18 -22
  16. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/image.py +11 -5
  17. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/parameters.py +7 -12
  18. interactive_pipe-0.8.9/src/interactive_pipe/data_objects/table.py +328 -0
  19. interactive_pipe-0.8.9/src/interactive_pipe/graphical/gradio_control.py +201 -0
  20. interactive_pipe-0.8.9/src/interactive_pipe/graphical/gradio_gui.py +651 -0
  21. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/gui.py +64 -35
  22. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/mpl_control.py +56 -37
  23. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/mpl_gui.py +41 -41
  24. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/mpl_window.py +23 -10
  25. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/nb_control.py +52 -36
  26. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/nb_gui.py +7 -7
  27. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/qt_control.py +153 -99
  28. interactive_pipe-0.8.9/src/interactive_pipe/graphical/qt_gui.py +981 -0
  29. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/window.py +10 -7
  30. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/control.py +87 -44
  31. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/keyboard.py +27 -15
  32. interactive_pipe-0.8.9/src/interactive_pipe/headless/panel.py +141 -0
  33. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/pipeline.py +68 -79
  34. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/choose_backend.py +7 -5
  35. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/control_abbreviation.py +62 -72
  36. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/filter_decorator.py +16 -14
  37. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/keyword_args_analyzer.py +15 -26
  38. interactive_pipe-0.8.9/src/interactive_pipe/helper/pipeline_decorator.py +71 -0
  39. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9/src/interactive_pipe.egg-info}/PKG-INFO +150 -118
  40. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/SOURCES.txt +5 -1
  41. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/requires.txt +4 -7
  42. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_context.py +47 -35
  43. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_context_compatibility.py +11 -24
  44. interactive_pipe-0.8.9/test/test_control_abbreviations.py +83 -0
  45. interactive_pipe-0.8.9/test/test_controller.py +112 -0
  46. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_core.py +1 -3
  47. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_core_exceptions.py +9 -10
  48. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_curves.py +2 -4
  49. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_data_objects_exceptions.py +7 -7
  50. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_decorator.py +2 -1
  51. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_edge_cases.py +17 -6
  52. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_engine.py +6 -13
  53. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_filter.py +1 -3
  54. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_graphical_exceptions.py +33 -29
  55. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_headless.py +7 -11
  56. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_headless_exceptions.py +4 -5
  57. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_image.py +2 -2
  58. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_legacy_warnings.py +17 -39
  59. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_mutable_defaults.py +1 -0
  60. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_recorder.py +7 -6
  61. interactive_pipe-0.8.9/test/test_table.py +397 -0
  62. interactive_pipe-0.8.8/src/interactive_pipe/core/signature.py +0 -18
  63. interactive_pipe-0.8.8/src/interactive_pipe/graphical/gradio_control.py +0 -168
  64. interactive_pipe-0.8.8/src/interactive_pipe/graphical/gradio_gui.py +0 -428
  65. interactive_pipe-0.8.8/src/interactive_pipe/graphical/qt_gui.py +0 -650
  66. interactive_pipe-0.8.8/src/interactive_pipe/helper/pipeline_decorator.py +0 -64
  67. interactive_pipe-0.8.8/test/test_control_abbreviations.py +0 -125
  68. interactive_pipe-0.8.8/test/test_controller.py +0 -33
  69. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/LICENSE +0 -0
  70. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/setup.cfg +0 -0
  71. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/core/__init__.py +0 -0
  72. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/data_objects/__init__.py +0 -0
  73. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/graphical/__init__.py +0 -0
  74. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/headless/__init__.py +0 -0
  75. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/__init__.py +0 -0
  76. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe/helper/_private.py +0 -0
  77. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/dependency_links.txt +0 -0
  78. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/src/interactive_pipe.egg-info/top_level.txt +0 -0
  79. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_cache.py +0 -0
  80. {interactive_pipe-0.8.8 → interactive_pipe-0.8.9}/test/test_parameters.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: interactive_pipe
3
- Version: 0.8.8
3
+ Version: 0.8.9
4
4
  Summary: Library to create flexible interactive image processing pipelines and automatically add a graphical user interface without knowing anything about GUI coding!
5
5
  Author-email: Balthazar Neveu <balthazarneveu@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/balthazarneveu/interactive_pipe
@@ -15,13 +15,6 @@ Requires-Dist: matplotlib>=3.5.3
15
15
  Requires-Dist: numpy>=1.21.6
16
16
  Requires-Dist: Pillow>=9.0.1
17
17
  Requires-Dist: PyYAML>=5.4.1
18
- Requires-Dist: PyQt6>=6.5.2
19
- Requires-Dist: PyQt6_sip>=13.5.2
20
- Requires-Dist: opencv_python_headless>=4.8.1.78
21
- Requires-Dist: pytest>=6.2.5
22
- Requires-Dist: ipywidgets>=7.7.1
23
- Requires-Dist: pandas
24
- Requires-Dist: gradio
25
18
  Provides-Extra: qt6
26
19
  Requires-Dist: PyQt6>=6.5.2; extra == "qt6"
27
20
  Requires-Dist: PyQt6_sip>=13.5.2; extra == "qt6"
@@ -41,6 +34,9 @@ Requires-Dist: pytest>=6.2.5; extra == "full"
41
34
  Requires-Dist: ipywidgets>=7.7.1; extra == "full"
42
35
  Requires-Dist: pandas; extra == "full"
43
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"
44
40
  Dynamic: license-file
45
41
 
46
42
 
@@ -61,7 +57,7 @@ Dynamic: license-file
61
57
  - Develop an algorithm while debugging visually with plots, while checking robustness & continuity to parameters change.
62
58
  - Magically create a graphical interface to easily demonstrate a concept or simply tune your algorithm.
63
59
 
64
- :heart: **You do not need to learn anything about making a graphical user interface (GUI)** :heart:
60
+ ❤️ **You do not need to learn anything about making a graphical user interface (GUI)** ❤️
65
61
 
66
62
 
67
63
  ### Examples
@@ -86,13 +82,13 @@ pip install -e ".[full]"
86
82
  ----------------------
87
83
 
88
84
  ### Who is this for?
89
- #### :mortar_board: Scientific education
85
+ #### 🎓 Scientific education
90
86
  - Demonstrate concepts by interacting with curves / images.
91
87
  - Easy integration in Jupyter Notebooks (popular with Google Collab)
92
- #### :gift: DIY hobbyist
88
+ #### 🎁 DIY hobbyist
93
89
  - You can also use the declarative nature of interactive pipe to make a graphical interface in a few lines of codes.
94
90
  - For instance, it is possible to code a jukebox for a toddler on a RaspberryPi.
95
- #### :camera: Engineering *(computer vision, image/signal processing)*
91
+ #### 📷 Engineering *(computer vision, image/signal processing)*
96
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.
97
93
  - Tune your algorithms with a graphical interface and save your parameters for later batch processing.
98
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).
@@ -102,31 +98,30 @@ pip install -e ".[full]"
102
98
 
103
99
 
104
100
  ----------------------
105
- ## :scroll: Terminology
106
101
 
107
- ![interactive_pipe_concept](static/interact-pipe-concept.svg)
102
+ ## 📜 Features
108
103
 
109
- ## :scroll: Features
110
-
111
- **Version 0.8.8**
104
+ **Version 0.8.9**
112
105
  - Modular multi-image processing filters
113
106
  - Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
114
107
  - Support in jupyter notebooks
115
108
  - Tuning sliders & check buttons with a GUI
116
109
  - Cache intermediate results in RAM for much faster processing
117
110
  - `KeyboardControl` : no slider on UI but exactly the same internal mechanism, update on key press.
118
- - Support Curve plots (2D signals)
111
+ - Support Curve plots (2D signals).
119
112
  - Gradio backend (+allows sharing with others).
120
113
  - Audio support in Gradio (live audio or display several players by returning 1D numpy arrays)
121
114
  - Circular sliders for Qt Backend
122
115
  - Text prompt (`free_text=("Hello world!", None),`)
123
116
  - TimeControl (possibility to play/pause time using an incrementing timer)
124
- - :new: **Context API**: Direct access to shared context across filters via `get_context()`, `context`, `layout`, `audio`
125
- - :new: MIT License
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
126
121
 
127
122
 
128
123
 
129
- #### :keyboard: Keyboard shortcuts
124
+ #### ⌨️ Keyboard shortcuts
130
125
  Shortcuts while using the GUI (QT & matplotlib backends)
131
126
 
132
127
  - `F1` to show the help shortcuts in the terminal
@@ -142,27 +137,28 @@ Shortcuts while using the GUI (QT & matplotlib backends)
142
137
 
143
138
  # Status
144
139
  - supported backends
145
- - :ok: `gui='qt'` pyQt/pySide
146
- - :ok: `gui='mpl'` matplotlib
147
- - :ok: `gui='nb'` ipywidget for jupyter notebooks
148
- - :test_tube: `gui='gradio'` gradio wrapping (+use `share_gradio_app=True` to share your app with others)
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)
149
144
  - tested platforms
150
- - :ok: Linux (Ubuntu / KDE Neon)
151
- - :ok: RapsberryPi
152
- - :ok: On google collab (use `gui='nb'`)
145
+ - Linux (Ubuntu / KDE Neon)
146
+ - RapsberryPi
147
+ - On google collab (use `gui='nb'`)
153
148
 
154
149
 
155
150
 
156
- | :star: | *PyQt / PySide* | *Matplotlib* | *Jupyter notebooks including Google collab* | *Gradio* |
151
+ | | *PyQt / PySide* | *Matplotlib* | *Jupyter notebooks including Google collab* | *Gradio* |
157
152
  |:-----: |:-----:|:------:|:----: |:----: |
158
153
  | Backend name | `qt` | `mpl` | `nb`| `gradio` |
159
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)|
160
- | Plot curves | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark:|
161
- | Auto refreshed layout | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
162
- | Keyboard shortcuts / fullscreen| :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: |
163
- | Audio support | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: |
164
- | Image buttons| :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign:|
165
- | Circular slider| :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign:|
155
+ | Plot curves | | | | ✅|
156
+ | Change layout | | | | |
157
+ | Keyboard shortcuts / fullscreen| | | | |
158
+ | Audio support | | | | |
159
+ | Image buttons| | | | |
160
+ | Circular slider| | | | |
161
+ | Collapsible Panels | ✅ | ➖ | ➖ | ✅ |
166
162
 
167
163
 
168
164
 
@@ -189,7 +185,7 @@ Shortcuts while using the GUI (QT & matplotlib backends)
189
185
 
190
186
 
191
187
 
192
- ## :rocket: Ultra short code
188
+ ## 🚀 Ultra short code
193
189
 
194
190
 
195
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)*
@@ -202,9 +198,9 @@ By design:
202
198
  - keyword arguments are the parameters which can be later turned into interactive widgets.
203
199
  - output buffers are simply returned like you'd do in a regular function.
204
200
 
205
- We use the `@interactive()` wrapper which will turn each keyword parameters initialized to a **tuple/list** into a graphical interactive widgets (slider, tick box, dropdown men).
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).
206
202
 
207
- The syntax to turn keyword arguments into sliders is pretty simple `(default, [min, max], name)` will turn into a float slider for instance.
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.
208
204
 
209
205
  Finally, we need to the glue to combo these filters. This is where the sample_pipeline function comes in.
210
206
 
@@ -215,8 +211,11 @@ By decorating it with `@interactive_pipeline(gui="qt")`, calling this function w
215
211
  from interactive_pipe import interactive, interactive_pipeline
216
212
  import numpy as np
217
213
 
218
- @interactive()
219
- def exposure(img, coeff = (1., [0.5, 2.], "exposure"), bias=(0., [-0.2, 0.2])):
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.):
220
219
  '''Applies a multiplication by coeff & adds a constant bias to the image'''
221
220
  # In the GUI, the coeff will be labelled as "exposure".
222
221
  # As the default tuple provided to bias does not end up with a string,
@@ -224,15 +223,15 @@ def exposure(img, coeff = (1., [0.5, 2.], "exposure"), bias=(0., [-0.2, 0.2])):
224
223
  return img*coeff + bias
225
224
 
226
225
 
227
- @interactive()
228
- def black_and_white(img, bnw=(True, "black and white")):
226
+ @interactive(bnw=(True, "black and white"))
227
+ def black_and_white(img, bnw=True):
229
228
  '''Averages the 3 color channels (Black & White) if bnw=True
230
229
  '''
231
230
  # Special mention for booleans: using a tuple like (True,) allows creating the tick box.
232
231
  return np.repeat(np.expand_dims(np.average(img, axis=-1), -1), img.shape[-1], axis=-1) if bnw else img
233
232
 
234
- @interactive()
235
- def blend(img0, img1, blend_coeff=(0.5, [0., 1.])):
233
+ @interactive(blend_coeff=(0.5, [0., 1.]))
234
+ def blend(img0, img1, blend_coeff=0.5):
236
235
  '''Blends between two image.
237
236
  - when blend_coeff=0 -> image 0 [slider to the left ]
238
237
  - when blend_coeff=1 -> image 1 [slider to the right]
@@ -252,25 +251,25 @@ if __name__ == '__main__':
252
251
  sample_pipeline(input_image)
253
252
 
254
253
  ```
255
- :heart: This code shall display you a GUI with three images. The middle one is the result of the blend
254
+ ❤️ This code shall display you a GUI with three images. The middle one is the result of the blend
256
255
 
257
256
 
258
257
 
259
258
  Notes:
260
- - If you write `def blend(img0, img1, blend_coeff=0.5):`, blend_coeff will simply not be a slider on the GUI no more.
261
- - If you write `blend_coeff=[0., 1.]` , blend_coeff will be a slider initalized to 0.5
262
- - If you write `bnw=(True, "black and white", "k")`, the checkbox will disappear and be replaced by a keypress event (press `k` to enable/disable black & white)
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)
263
262
 
264
263
  -----------
265
- ## :bulb: Some more tips
264
+ ## 💡 Some more tips
266
265
 
267
266
  ```python
268
- from interactive_pipe import interactive, interactive_pipeline
267
+ from interactive_pipe import interactive, interactive_pipeline, context
269
268
  import numpy as np
270
269
 
271
270
  COLOR_DICT = {"red": [1., 0., 0.], "green": [0., 1.,0.], "blue": [0., 0., 1.], "gray": [0.5, 0.5, 0.5]}
272
- @interactive()
273
- def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"], context={}):
271
+ @interactive(color_choice=["red", "green", "blue", "gray"])
272
+ def generate_flat_colored_image(color_choice="red"):
274
273
  '''Generate a constant colorful image
275
274
  '''
276
275
  flat_array = np.array(COLOR_DICT.get(color_choice)) * np.ones((64, 64, 3))
@@ -282,15 +281,15 @@ def generate_flat_colored_image(color_choice=["red", "green", "blue", "gray"], c
282
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!
283
282
  ----------
284
283
 
285
- :bulb: Can filters communicate together?
286
- Yes, using the special keyword argument `context={}`.
287
- - Check carefully how we stored the image average of the flat image in context.
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.
288
287
  - This value will be available to other filters.
289
288
  `special_image_slice` is going to use that value to set the half bottom image to dark in case the average is high.
290
289
 
291
290
  ```python
292
- @interactive()
293
- def special_image_slice(img, context={}):
291
+ def special_image_slice(img):
292
+ out_img = img.copy()
294
293
  if context["avg"] > 0.4:
295
294
  out_img[out_img.shape[0]//2:, ...] = 0.
296
295
  return out_img
@@ -299,8 +298,8 @@ def special_image_slice(img, context={}):
299
298
 
300
299
 
301
300
  ```python
302
- @interactive()
303
- def switch_image(img1, img2, img3, image_index=(0, [0, 2], None, ["pagedown", "pageup", True])):
301
+ @interactive(image_index=(0, [0, 2], None, ["pagedown", "pageup", True]))
302
+ def switch_image(img1, img2, img3, image_index=0):
304
303
  '''Switch between 3 images
305
304
  '''
306
305
  return [img1, img2, img3][image_index]
@@ -308,8 +307,8 @@ def switch_image(img1, img2, img3, image_index=(0, [0, 2], None, ["pagedown", "p
308
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).
309
308
 
310
309
  ```python
311
- @interactive()
312
- def black_top_image_slice(img, top_slice_black=(True, "special", "k"), context={}):
310
+ @interactive(top_slice_black=(True, "special", "k"))
311
+ def black_top_image_slice(img, top_slice_black=True):
313
312
  out_img = img.copy()
314
313
  if top_slice_black:
315
314
  out_img[:out_img.shape[0]//2, ...] = 0.
@@ -332,6 +331,38 @@ if __name__ == '__main__':
332
331
 
333
332
  ### Release Notes
334
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
+
335
366
  #### Version 0.8.8 (January 2026)
336
367
 
337
368
  **New Features:**
@@ -357,27 +388,34 @@ if __name__ == '__main__':
357
388
  - Fixed pytest failures for optional dependencies in CI
358
389
  - Fixed various edge cases in error handling
359
390
 
360
- **Migration of old context or global_params:**
361
- - Use `global_params=SharedContext.injected()` to let code interpreters know that there's no need to pass this parameter.
391
+ **Migration from old `context={}` or `global_params={}` patterns:**
362
392
 
363
393
  ```python
364
- # Before
365
- def apply_brightness(img:np.ndarray, brightness: float = 0.5, global_params:dict =None):
366
- global_params["brightness"] = brightness
367
- return img * brightness
394
+ # OLD (deprecated) - using context={} or global_params={}
395
+ from interactive_pipe import interactive
368
396
 
369
- # Progressive migration
370
- def apply_brightness(img:np.ndarray, brightness: float = 0.5, global_params:dict ==SharedContext.injected()):
371
- global_params["brightness"] = brightness
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
372
401
  return img * brightness
373
402
 
374
- # Recommended version
375
- from interactive_pipe import context
403
+ # NEW (recommended) - using context and layout proxies
404
+ from interactive_pipe import interactive, context, layout
405
+
406
+ @interactive(brightness=(0.5, [0., 1.]))
376
407
  def apply_brightness(img:np.ndarray, brightness: float = 0.5):
377
- context.shared_brightness = brightness # shared with all filters
408
+ context["brightness"] = brightness # or: context.brightness = brightness
409
+ layout.style("output", title="Brightened")
378
410
  return img * brightness
379
411
  ```
380
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
+
381
419
 
382
420
 
383
421
 
@@ -395,7 +433,7 @@ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
395
433
 
396
434
 
397
435
  ### FAQ
398
- - :question: What is the recommended way to access shared context?
436
+ - What is the recommended way to access shared context?
399
437
  > **New in v0.8.8**: Use the clean context API for direct access:
400
438
  > ```python
401
439
  > from interactive_pipe import context, layout, audio, get_context
@@ -409,7 +447,7 @@ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
409
447
  > return img
410
448
  > ```
411
449
 
412
- - :question: 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.
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.
413
451
 
414
452
  > Use the `layout` helper to control image arrangement and styling:
415
453
  > ```python
@@ -434,16 +472,16 @@ def apply_brightness(img:np.ndarray, brightness: float = 0.5):
434
472
  > return result
435
473
  > ```
436
474
 
437
- - :question: Do I have to remove `KeyboardSlider` when using gradio or notebook backends?
475
+ - Do I have to remove `KeyboardSlider` when using gradio or notebook backends?
438
476
  > No, don't worry, these will be mapped back to regular sliders!
439
- - :question: How do I play audio live?
440
- > :sound: Inside a processing block, write the audio file to disk and use the audio helper:
477
+ - How do I play audio live?
478
+ > 🔊 Inside a processing block, write the audio file to disk and use the audio helper:
441
479
  > ```python
442
480
  > from interactive_pipe import audio
443
481
  > audio.set_audio(audio_file) # New clean API (v0.8.8)
444
482
  > # or legacy: context["__set_audio"](audio_file)
445
483
  > ```
446
- - :question: Do I have to decorate my processing block using the `@interactive`
484
+ - Do I have to decorate my processing block using the `@interactive`
447
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)
448
486
  ```python
449
487
  @interactive(angle=(0., [-360., 360.]))
@@ -465,11 +503,11 @@ from core_filter import processing_block
465
503
  def add_interactivity():
466
504
  interactive(angle=(0., [-360., 360.]))(processing_block)
467
505
  ```
468
- - :question: Can I call the pipeline in a command line/batch fashion?
469
- > Yes, headless mode is supported. :soon: documentation needed.
506
+ - Can I call the pipeline in a command line/batch fashion?
507
+ > Yes, headless mode is supported. 🔜 documentation needed.
470
508
 
471
509
 
472
- - :question: Can I use inplace operations?
510
+ - Can I use inplace operations?
473
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.
474
512
  ```python
475
513
  # Don't do that!
@@ -477,38 +515,18 @@ def bad_processing_block(inp):
477
515
  inp+=1
478
516
  ```
479
517
 
480
- - :question: Is there a difference between `global_params` and `context` ?
518
+ - Is there a difference between `global_params` and `context` ?
481
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).
482
- > :warning: The old `global_params={}` / `context={}` keyword argument style still works for backwards compatibility but is deprecated.
520
+ > ⚠️ The old `global_params={}` / `context={}` keyword argument style still works for backwards compatibility but is deprecated.
483
521
 
484
522
 
485
523
 
486
524
  # Roadmap and todos
487
- :bug: Want to contribute or interested in adding new features? Enter a new [Github issue](https://github.com/balthazarneveu/interactive_pipe/issues)
525
+ 🐛 Want to contribute or interested in adding new features? Enter a new [Github issue](https://github.com/balthazarneveu/interactive_pipe/issues)
488
526
 
489
- :gift: Want to dig into the code? Take a look at [code_architecture.md](/code_architecture.md)
527
+ 🎁 Want to dig into the code? Take a look at [code_architecture.md](/code_architecture.md)
490
528
 
491
- ## Short term roadmap
492
- - Backport previous features
493
- - Image class support in interactive pipe (Heatmaps/Float images)
494
529
 
495
- ## Long term roadmap
496
- - Advanced feature
497
- - Webcam based "slider" for dropdown menu (like "elephant" will trigget if an elephant is magically detected on the webcam)
498
- - Animations/While loops/Video source (Time slider)
499
- - Exploratory backends
500
- - Create a [textual](https://github.com/Textualize/textual) backend for simplified GUI (probably no images displayed)
501
- - Create a [Kivy](https://kivy.org/) backend
502
-
503
-
504
- ### Further examples
505
-
506
- #### [Minimalistic pytorch based ISP ](https://github.com/balthazarneveu/interactive_pipe/tree/sample_interactive_isp/samples/isp)
507
- [ISP means image signal processor](https://en.wikipedia.org/wiki/Image_processor)
508
-
509
- :warning: Work in progess (no proper demosaicking, no denoiser, no tone mapping.)
510
-
511
- ![Ultra simplistic ISP](/doc/images/isp_pipeline.png)
512
530
 
513
531
  ----------------------
514
532
 
@@ -516,30 +534,44 @@ def bad_processing_block(inp):
516
534
 
517
535
  #### Code quality checks
518
536
 
519
- Before committing, ensure your code passes the linters and tests. The CI runs these checks automatically:
537
+ Before committing, ensure your code passes the linters, type checker, and tests. The CI runs these checks automatically:
520
538
 
521
539
  **What CI does:**
522
- - **Black formatting check** (`.github/workflows/formatting.yaml`): Runs `black --check` to verify code formatting
523
- - **Flake8 linting** (`.github/workflows/flake8.yaml`): Runs `flake8` to check code quality
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)
524
543
  - **Pytest tests** (`.github/workflows/pytest.yaml`): Runs `pytest` on Python 3.9, 3.10, and 3.11
525
544
 
526
545
  **Local commands (match CI):**
527
546
 
528
547
  ```bash
529
- # Install linting tools and test dependencies
530
- pip install black flake8
531
- pip install -e ".[pytest]"
548
+ # Install development tools and test dependencies
549
+ pip install -e ".[dev,pytest]"
532
550
 
533
- # Check code formatting (Black) - matches CI
534
- black --check .
551
+ # Format code (Ruff) - matches CI
552
+ ruff format .
535
553
 
536
- # Auto-format code (Black) - run this if check fails
537
- black .
554
+ # Check formatting (Ruff) - matches CI
555
+ ruff format --check .
538
556
 
539
- # Run linting checks (flake8) - matches CI
540
- flake8 .
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/
541
565
 
542
566
  # Run tests (pytest) - matches CI
543
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
544
574
  ```
545
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.8"
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!"
@@ -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