interactive-pipe 0.7.0__tar.gz → 0.7.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {interactive_pipe-0.7.0/src/interactive_pipe.egg-info → interactive_pipe-0.7.1}/PKG-INFO +2 -2
  2. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/pyproject.toml +1 -1
  3. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/readme.md +1 -1
  4. interactive_pipe-0.7.1/src/interactive_pipe/data_objects/audio.py +32 -0
  5. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/gradio_control.py +10 -2
  6. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/gradio_gui.py +102 -12
  7. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1/src/interactive_pipe.egg-info}/PKG-INFO +2 -2
  8. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe.egg-info/SOURCES.txt +1 -0
  9. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/LICENSE +0 -0
  10. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/setup.cfg +0 -0
  11. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/__init__.py +0 -0
  12. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/core/__init__.py +0 -0
  13. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/core/cache.py +0 -0
  14. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/core/engine.py +0 -0
  15. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/core/filter.py +0 -0
  16. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/core/graph.py +0 -0
  17. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/core/pipeline.py +0 -0
  18. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/core/signature.py +0 -0
  19. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/data_objects/__init__.py +0 -0
  20. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/data_objects/curves.py +0 -0
  21. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/data_objects/data.py +0 -0
  22. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/data_objects/image.py +0 -0
  23. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/data_objects/parameters.py +0 -0
  24. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/__init__.py +0 -0
  25. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/gui.py +0 -0
  26. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/mpl_control.py +0 -0
  27. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/mpl_gui.py +0 -0
  28. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/mpl_window.py +0 -0
  29. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/nb_control.py +0 -0
  30. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/nb_gui.py +0 -0
  31. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/qt_control.py +0 -0
  32. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/qt_gui.py +0 -0
  33. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/graphical/window.py +0 -0
  34. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/headless/__init__.py +0 -0
  35. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/headless/control.py +0 -0
  36. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/headless/keyboard.py +0 -0
  37. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/headless/pipeline.py +0 -0
  38. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/helper/__init__.py +0 -0
  39. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/helper/_private.py +0 -0
  40. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/helper/choose_backend.py +0 -0
  41. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/helper/control_abbreviation.py +0 -0
  42. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/helper/filter_decorator.py +0 -0
  43. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/helper/keyword_args_analyzer.py +0 -0
  44. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/helper/pipeline_decorator.py +0 -0
  45. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/thirdparty/images_openai_api.py +0 -0
  46. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe/thirdparty/music_spotify.py +0 -0
  47. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe.egg-info/dependency_links.txt +0 -0
  48. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe.egg-info/requires.txt +0 -0
  49. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/src/interactive_pipe.egg-info/top_level.txt +0 -0
  50. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_cache.py +0 -0
  51. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_control_abbreviations.py +0 -0
  52. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_controller.py +0 -0
  53. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_core.py +0 -0
  54. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_curves.py +0 -0
  55. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_decorator.py +0 -0
  56. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_engine.py +0 -0
  57. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_filter.py +0 -0
  58. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_headless.py +0 -0
  59. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_image.py +0 -0
  60. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_parameters.py +0 -0
  61. {interactive_pipe-0.7.0 → interactive_pipe-0.7.1}/test/test_recorder.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: interactive_pipe
3
- Version: 0.7.0
3
+ Version: 0.7.1
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
@@ -95,7 +95,7 @@ pip install -e ".[full]"
95
95
 
96
96
  ## :scroll: Features
97
97
 
98
- **Version 0.7.0**
98
+ **Version 0.7.1**
99
99
  - Modular multi-image processing filters
100
100
  - Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
101
101
  - Support in jupyter notebooks
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "interactive_pipe"
8
- version = "0.7.0"
8
+ version = "0.7.1"
9
9
  authors = [
10
10
  { name="Balthazar Neveu", email="balthazarneveu@gmail.com" },
11
11
  ]
@@ -51,7 +51,7 @@ pip install -e ".[full]"
51
51
 
52
52
  ## :scroll: Features
53
53
 
54
- **Version 0.7.0**
54
+ **Version 0.7.1**
55
55
  - Modular multi-image processing filters
56
56
  - Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
57
57
  - Support in jupyter notebooks
@@ -0,0 +1,32 @@
1
+ from pathlib import Path
2
+ import numpy as np
3
+ from io import BytesIO
4
+ import base64
5
+ import logging
6
+ from typing import Union, Tuple
7
+ WAVIO_AVAILABLE = False
8
+ try:
9
+ WAVIO_AVAILABLE = True
10
+ import wavio
11
+ except ImportError:
12
+ logging.info("Cannot import wavio")
13
+
14
+
15
+ def audio_to_html(audio: Union[None, str, Path, Tuple[int, np.ndarray]]) -> str:
16
+ if audio is None:
17
+ logging.debug("No audio to display")
18
+ return ""
19
+ if isinstance(audio, str) or isinstance(audio, Path):
20
+ audio_base64 = base64.b64encode(open(audio, "rb").read()).decode("utf-8")
21
+ elif isinstance(audio, tuple):
22
+ assert WAVIO_AVAILABLE, "wavio is not available"
23
+ assert len(audio) == 2, "audio tuple should have 2 elements: (rate, data)"
24
+ assert isinstance(audio[0], int), "audio[0] should be an integer"
25
+ assert isinstance(audio[1], np.ndarray), "audio[1] should be a numpy array"
26
+ audio_bytes = BytesIO()
27
+ wavio.write(audio_bytes, audio[1].astype(np.float32), audio[0], sampwidth=4)
28
+ audio_bytes.seek(0)
29
+ audio_base64 = base64.b64encode(audio_bytes.read()).decode("utf-8")
30
+ audio_player = f'<audio src="data:audio/mpeg;base64,{audio_base64}" controls autoplay></audio>'
31
+
32
+ return audio_player
@@ -49,7 +49,12 @@ class IntSliderControl(BaseControl):
49
49
 
50
50
  def create(self) -> gr.Slider:
51
51
  self.control_widget = gr.Slider(
52
- value=self.ctrl.value_default, minimum=self.ctrl.value_range[0], maximum=self.ctrl.value_range[1], label=self.name, step=1)
52
+ value=self.ctrl.value_default,
53
+ minimum=self.ctrl.value_range[0],
54
+ maximum=self.ctrl.value_range[1],
55
+ label=self.name,
56
+ step=1
57
+ )
53
58
  return self.control_widget
54
59
 
55
60
 
@@ -60,7 +65,10 @@ class FloatSliderControl(BaseControl):
60
65
  def create(self) -> gr.Slider:
61
66
  self.control_widget = gr.Slider(
62
67
  value=self.ctrl.value_default,
63
- minimum=self.ctrl.value_range[0], maximum=self.ctrl.value_range[1], label=self.name)
68
+ minimum=self.ctrl.value_range[0],
69
+ maximum=self.ctrl.value_range[1],
70
+ label=self.name,
71
+ )
64
72
 
65
73
  return self.control_widget
66
74
 
@@ -1,11 +1,13 @@
1
1
  import gradio as gr
2
2
  import numpy as np
3
+ import math
3
4
  from typing import List
4
5
  from interactive_pipe.headless.pipeline import HeadlessPipeline
5
6
  from interactive_pipe.graphical.gradio_control import ControlFactory
6
7
  from interactive_pipe.graphical.window import InteractivePipeWindow
7
8
  from interactive_pipe.graphical.gui import InteractivePipeGUI
8
9
  from interactive_pipe.headless.control import Control
10
+ from interactive_pipe.data_objects.audio import audio_to_html
9
11
  import logging
10
12
  PYQTVERSION = None
11
13
  MPL_SUPPORT = False
@@ -21,23 +23,48 @@ except ImportError:
21
23
  class InteractivePipeGradio(InteractivePipeGUI):
22
24
  def init_app(self, **kwargs):
23
25
  self.window = MainWindow(controls=self.controls, name=self.name,
24
- pipeline=self.pipeline, size=self.size, main_gui=self, **kwargs)
26
+ pipeline=self.pipeline, size=self.size, main_gui=self, audio=self.audio, **kwargs)
25
27
  self.pipeline.global_params["__pipeline"] = self.pipeline
26
- # self.set_default_key_bindings()
28
+ if self.audio:
29
+ self.pipeline.global_params["__set_audio"] = self.__set_audio
30
+ self.pipeline.global_params["__play"] = self.__play
31
+ self.pipeline.global_params["__stop"] = self.__stop
32
+ self.pipeline.global_params["__pause"] = self.__pause
33
+ self.__set_audio(None)
34
+
35
+ def __set_audio(self, audio_content):
36
+ self.window.audio_content = audio_content
37
+
38
+ def __play(self):
39
+ pass
40
+
41
+ def __pause(self):
42
+ self.__set_audio(None)
43
+
44
+ def __stop(self):
45
+ self.__set_audio(None)
27
46
 
28
47
  def run(self) -> list:
29
48
  assert self.pipeline._PipelineCore__initialized_inputs, "Did you forget to initialize the pipeline inputs?"
30
49
  out_list = self.window.process_inputs_fn(*self.window.default_values)
50
+ self.window.refresh_display(out_list)
31
51
  out_list_gradio_containers = []
32
52
  for idx in range(len(out_list)):
33
53
  for idy in range(len(out_list[idx])):
54
+ title = self.window.image_canvas[idx][idy].get("title", f"{idx} {idy}")
55
+ title = title.replace("_", " ")
34
56
  if isinstance(out_list[idx][idy], np.ndarray):
35
- out_list_gradio_containers.append(gr.Image())
57
+ out_list_gradio_containers.append(gr.Image(label=title))
36
58
  elif MPL_SUPPORT and isinstance(out_list[idx][idy], Curve):
37
- out_list_gradio_containers.append(gr.Plot())
59
+ # if out_list[idx][idy].title is not None:
60
+ # title = out_list[idx][idy].title
61
+ out_list_gradio_containers.append(gr.Plot(label=title))
38
62
  else:
39
63
  raise NotImplementedError(
40
64
  f"output type {type(out_list[idx][idy])} not supported")
65
+ if self.window.audio:
66
+ self.window.audio = gr.HTML()
67
+ self.pipeline.global_params["__audio"] = self.window.audio
41
68
  self.window.instantiate_gradio_interface(out_list_gradio_containers)
42
69
  self.window.refresh()
43
70
  self.custom_end()
@@ -48,14 +75,18 @@ class InteractivePipeGradio(InteractivePipeGUI):
48
75
 
49
76
 
50
77
  class MainWindow(InteractivePipeWindow):
51
- def __init__(self, *args, controls=[], name="", pipeline: HeadlessPipeline = None, size=None, share_gradio_app=False, **kwargs):
78
+ def __init__(self, *args, controls=[], name="", pipeline: HeadlessPipeline = None, size=None, share_gradio_app=False, markdown_description=None, sliders_layout=None, sliders_per_row_layout=None, audio=False, **kwargs):
52
79
  InteractivePipeWindow.__init__(
53
80
  self, name=name, pipeline=pipeline, size=size)
81
+ self.markdown_description = markdown_description
82
+ self.sliders_layout = sliders_layout
83
+ self.sliders_per_row_layout = sliders_per_row_layout
54
84
  self.init_sliders(controls)
55
85
  self.size = size
56
86
  self.full_screen_flag = False
57
87
  self.pipeline = pipeline
58
88
  self.share_gradio_app = share_gradio_app
89
+ self.audio = audio
59
90
  # Define the functions that will be called when the input changes for gradio. => gr.Interface(fn=process_fn)
60
91
 
61
92
  def process_outputs_fn(out) -> tuple:
@@ -92,6 +123,9 @@ class MainWindow(InteractivePipeWindow):
92
123
  def run_fn(*args) -> tuple:
93
124
  out = process_inputs_fn(*args)
94
125
  out_tuple = process_outputs_fn(out)
126
+ if self.audio:
127
+ html_audio = audio_to_html(self.audio_content)
128
+ return *out_tuple, html_audio
95
129
  return out_tuple
96
130
  self.default_values = [self.ctrl[ctrl_key].value for ctrl_key in self.ctrl.keys()]
97
131
  self.process_inputs_fn = process_inputs_fn
@@ -116,15 +150,68 @@ class MainWindow(InteractivePipeWindow):
116
150
  # Gradio Blocks mode
117
151
  # https://www.gradio.app/guides/blocks-and-event-listeners
118
152
  with gr.Blocks() as io:
153
+ if self.audio:
154
+ outputs.append(self.audio)
155
+ with gr.Row(variant="compact"):
156
+ gr.Markdown("### " + self.name.replace("_", " "))
119
157
  with gr.Row():
120
158
  for elem in outputs:
121
159
  elem.render()
122
- with gr.Row():
123
- for elem in self.widget_list:
124
- with gr.Row():
125
- elem.render()
126
- with gr.Row():
127
- gr.Examples([self.default_values], inputs=self.widget_list)
160
+
161
+ if self.sliders_layout is None:
162
+ self.sliders_layout = "collapsible"
163
+ if self.sliders_per_row_layout is None:
164
+ self.sliders_per_row_layout = 1
165
+ assert self.sliders_layout in ["compact", "vertical", "collapsible", "smart"]
166
+ ctrl_dict_by_type = {"all": list(range(len(self.ctrl)))}
167
+ categories = ["all"]
168
+ if self.sliders_layout == "compact":
169
+ selected_mode = gr.Row()
170
+ elif self.sliders_layout == "vertical":
171
+ # Use Column to stack elements vertically
172
+ selected_mode = gr.Column()
173
+ elif self.sliders_layout == "collapsible":
174
+ selected_mode = gr.Accordion("Parameters", open=True)
175
+ elif self.sliders_layout == "smart":
176
+ # Group sliders by type
177
+ ctrl_dict_by_type = {}
178
+ for ctrl_index, ctrl_key in enumerate(self.ctrl.keys()):
179
+ ctrl_type = self.ctrl[ctrl_key]._type
180
+ ctrl_type = str(ctrl_type).split("<class '")[1].replace("'>", "")
181
+ if ctrl_type == "int":
182
+ ctrl_type = "float"
183
+ if ctrl_type not in ctrl_dict_by_type:
184
+ ctrl_dict_by_type[ctrl_type] = []
185
+ ctrl_dict_by_type[ctrl_type].append(ctrl_index)
186
+ categories = ["str", "bool", "float"]
187
+ selected_mode = gr.Column()
188
+ else:
189
+ raise NotImplementedError(f"Sliders layout {self.sliders_layout} not supported")
190
+ for ctrl_type in categories:
191
+ ctrl_indices = ctrl_dict_by_type.get(ctrl_type, [])
192
+ if len(ctrl_indices) == 0:
193
+ continue
194
+ if self.sliders_per_row_layout == 1:
195
+ with selected_mode:
196
+ for idx in ctrl_indices:
197
+ elem = self.widget_list[idx]
198
+ elem.render()
199
+ else:
200
+ with selected_mode:
201
+ for split_num in range(math.ceil(len(ctrl_indices)/self.sliders_per_row_layout)):
202
+ with gr.Row():
203
+ start = split_num*self.sliders_per_row_layout
204
+ end = min((split_num+1)*self.sliders_per_row_layout, len(ctrl_indices))
205
+ for idx in ctrl_indices[start:end]:
206
+ elem = self.widget_list[idx]
207
+ elem.render()
208
+
209
+ with gr.Accordion("Reset to default values", open=False):
210
+ gr.Examples([self.default_values], inputs=self.widget_list, label="Presets")
211
+
212
+ if self.markdown_description is not None:
213
+ with gr.Row():
214
+ gr.Markdown(self.markdown_description)
128
215
  io.load(fn=self.run_fn, inputs=self.widget_list, outputs=outputs)
129
216
  for idx in range(len(self.widget_list)):
130
217
  self.widget_list[idx].change(
@@ -167,7 +254,10 @@ class MainWindow(InteractivePipeWindow):
167
254
 
168
255
  def add_image_placeholder(self, row, col):
169
256
  ax_placeholder = None
170
- text_label = None
257
+ text_label = self.get_current_style(row, col).get("title", "")
171
258
  image_label = None
172
259
  self.image_canvas[row][col] = {
173
260
  "image": image_label, "title": text_label, "ax_placeholder": ax_placeholder}
261
+
262
+ def update_image(self, content, row, col):
263
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: interactive_pipe
3
- Version: 0.7.0
3
+ Version: 0.7.1
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
@@ -95,7 +95,7 @@ pip install -e ".[full]"
95
95
 
96
96
  ## :scroll: Features
97
97
 
98
- **Version 0.7.0**
98
+ **Version 0.7.1**
99
99
  - Modular multi-image processing filters
100
100
  - Declarative: Easily make graphical user interface without having to learn anything about pyQt or matplotlib
101
101
  - Support in jupyter notebooks
@@ -15,6 +15,7 @@ src/interactive_pipe/core/graph.py
15
15
  src/interactive_pipe/core/pipeline.py
16
16
  src/interactive_pipe/core/signature.py
17
17
  src/interactive_pipe/data_objects/__init__.py
18
+ src/interactive_pipe/data_objects/audio.py
18
19
  src/interactive_pipe/data_objects/curves.py
19
20
  src/interactive_pipe/data_objects/data.py
20
21
  src/interactive_pipe/data_objects/image.py