molde 0.1.0__py3-none-any.whl

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 (62) hide show
  1. molde/__init__.py +5 -0
  2. molde/__main__.py +63 -0
  3. molde/actors/__init__.py +4 -0
  4. molde/actors/ghost_actor.py +12 -0
  5. molde/actors/lines_actor.py +31 -0
  6. molde/actors/round_points_actor.py +7 -0
  7. molde/actors/square_points_actor.py +32 -0
  8. molde/colors/__init__.py +2 -0
  9. molde/colors/color.py +120 -0
  10. molde/colors/color_names.py +125 -0
  11. molde/fonts/IBMPlexMono-Bold.ttf +0 -0
  12. molde/fonts/IBMPlexMono-Regular.ttf +0 -0
  13. molde/icons/arrow_down_dark_theme.svg +1 -0
  14. molde/icons/arrow_down_disabled_dark_theme.svg +1 -0
  15. molde/icons/arrow_down_disabled_light_theme.svg +1 -0
  16. molde/icons/arrow_down_light_theme.svg +1 -0
  17. molde/icons/arrow_left_dark_theme.svg +1 -0
  18. molde/icons/arrow_left_light_theme.svg +1 -0
  19. molde/icons/arrow_right_dark_theme.svg +1 -0
  20. molde/icons/arrow_right_light_theme.svg +1 -0
  21. molde/icons/arrow_up_dark_theme.svg +1 -0
  22. molde/icons/arrow_up_disabled_dark_theme.svg +1 -0
  23. molde/icons/arrow_up_disabled_light_theme.svg +1 -0
  24. molde/icons/arrow_up_light_theme.svg +1 -0
  25. molde/icons/check_box_image.svg +1 -0
  26. molde/interactor_styles/__init__.py +2 -0
  27. molde/interactor_styles/arcball_camera_style.py +272 -0
  28. molde/interactor_styles/box_selection_style.py +70 -0
  29. molde/pickers/__init__.py +2 -0
  30. molde/pickers/cell_area_picker.py +61 -0
  31. molde/pickers/cell_property_area_picker.py +84 -0
  32. molde/poly_data/__init__.py +2 -0
  33. molde/poly_data/lines_data.py +23 -0
  34. molde/poly_data/vertices_data.py +24 -0
  35. molde/render_widgets/__init__.py +2 -0
  36. molde/render_widgets/animated_render_widget.py +164 -0
  37. molde/render_widgets/common_render_widget.py +433 -0
  38. molde/stylesheets/__init__.py +119 -0
  39. molde/stylesheets/common.qss +16 -0
  40. molde/stylesheets/create_color_page.py +61 -0
  41. molde/stylesheets/mainwindow.ui +611 -0
  42. molde/stylesheets/qcheckbox.qss +19 -0
  43. molde/stylesheets/qinputs.qss +79 -0
  44. molde/stylesheets/qlayouts.qss +22 -0
  45. molde/stylesheets/qmenubar.qss +12 -0
  46. molde/stylesheets/qprogressbar.qss +12 -0
  47. molde/stylesheets/qpushbutton.qss +91 -0
  48. molde/stylesheets/qradiobutton.qss +31 -0
  49. molde/stylesheets/qscrollbar.qss +30 -0
  50. molde/stylesheets/qslider.qss +61 -0
  51. molde/stylesheets/qtablewidget.qss +27 -0
  52. molde/stylesheets/qtabwidget.qss +30 -0
  53. molde/stylesheets/qtoolbar.qss +62 -0
  54. molde/stylesheets/qtoolbuttons.qss +14 -0
  55. molde/stylesheets/qtreewidget.qss +25 -0
  56. molde/utils/__init__.py +8 -0
  57. molde/utils/format_sequences.py +44 -0
  58. molde/utils/poly_data_utils.py +25 -0
  59. molde/utils/tree_info.py +52 -0
  60. molde-0.1.0.dist-info/METADATA +44 -0
  61. molde-0.1.0.dist-info/RECORD +62 -0
  62. molde-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,164 @@
1
+ from threading import Lock
2
+ from time import time
3
+ import numpy as np
4
+ from pathlib import Path
5
+ from PIL import Image
6
+ import logging
7
+
8
+ from .common_render_widget import CommonRenderWidget
9
+
10
+
11
+ class AnimatedRenderWidget(CommonRenderWidget):
12
+ def __init__(self, parent=None):
13
+ super().__init__(parent)
14
+
15
+ self.playing_animation = False
16
+ self._animation_lock = Lock()
17
+ self._animation_frame = 0
18
+ self._animation_last_time = 0
19
+ self._animation_total_frames = 30
20
+ self._animation_fps = 30
21
+ self._animation_cycles = 0
22
+ self._animation_current_cycle = 0
23
+ self._animation_timer = self.render_interactor.CreateRepeatingTimer(500)
24
+ self.render_interactor.AddObserver("TimerEvent", self._animation_callback)
25
+
26
+ def start_animation(self, fps=None, frames=None, cycles=None):
27
+ if isinstance(fps, int | float):
28
+ self._animation_fps = fps
29
+
30
+ if isinstance(frames, int):
31
+ self._animation_total_frames = frames
32
+
33
+ if isinstance(cycles, int):
34
+ self._animation_cycles = cycles
35
+ self._animation_current_cycle = 0
36
+ else:
37
+ self._animation_cycles = 0
38
+ self._animation_current_cycle = 0
39
+
40
+ if self.playing_animation:
41
+ return
42
+
43
+ self.playing_animation = True
44
+
45
+ def stop_animation(self):
46
+ if not self.playing_animation:
47
+ return
48
+
49
+ if self._animation_timer is None:
50
+ return
51
+
52
+ self.playing_animation = False
53
+
54
+ def toggle_animation(self):
55
+ if self.playing_animation:
56
+ self.stop_animation()
57
+ else:
58
+ self.start_animation()
59
+
60
+ def _animation_callback(self, obj, event):
61
+ """
62
+ Common function with controls that are meaningfull to
63
+ all kinds of animations.
64
+ """
65
+
66
+ if not self.playing_animation:
67
+ return
68
+
69
+ # Wait the rendering of the last frame
70
+ # before starting a new one
71
+ if self._animation_lock.locked():
72
+ return
73
+
74
+ # Only needed because vtk CreateRepeatingTimer(n)
75
+ # does not work =/
76
+ dt = time() - self._animation_last_time
77
+ if (dt) < 1 / self._animation_fps:
78
+ return
79
+
80
+ if (self._animation_cycles != 0) and (self._animation_current_cycle >= self._animation_cycles):
81
+ self.stop_animation()
82
+ return
83
+
84
+ if self._animation_frame == 0:
85
+ self._animation_current_cycle += 1
86
+
87
+ with self._animation_lock:
88
+ self._animation_frame = (
89
+ self._animation_frame + 1
90
+ ) % self._animation_total_frames
91
+ self.update_animation(self._animation_frame)
92
+ self._animation_last_time = time()
93
+
94
+ def update_animation(self, frame: int):
95
+ raise NotImplementedError(
96
+ 'The function "update_animation" was not implemented!'
97
+ )
98
+
99
+ def save_video(self, path: str | Path, n_loops=20):
100
+ '''
101
+ Saves a video of multiple cycles of the current animation.
102
+ Supported formats are MP4, AVI, OGV, and WEBM.
103
+ '''
104
+ from moviepy.editor import ImageSequenceClip
105
+
106
+ # Stop animation to prevent conflicts
107
+ previous_state = self.playing_animation
108
+ self.stop_animation()
109
+
110
+ logging.info("Generating video frames...")
111
+ images = list()
112
+ for i in range(self._animation_total_frames):
113
+ self.update_animation(i)
114
+ screenshot = self.get_screenshot().resize([1920, 1080])
115
+ images.append(screenshot)
116
+ frames = [np.array(img) for img in images]
117
+
118
+ # recover the playing animation status
119
+ self.playing_animation = previous_state
120
+
121
+ logging.info("Saving video to file...")
122
+ clip = ImageSequenceClip(frames, self._animation_fps)
123
+ clip = clip.loop(duration = clip.duration * n_loops)
124
+ clip.write_videofile(str(path), preset="veryfast", logger=None)
125
+
126
+ def save_animation(self, path: str | Path):
127
+ '''
128
+ Saves an animated image file of a animation cycle.
129
+ Supported formats are WEBP and GIF.
130
+
131
+ We strongly recomend you to use webp, because of the reduced
132
+ file size and the superior visual quality.
133
+ Despite that, gifs are usefull sometimes because of its popularity.
134
+ '''
135
+
136
+ path = Path(path)
137
+ is_gif = path.suffix == ".gif"
138
+
139
+ # Stop animation to prevent conflicts
140
+ previous_state = self.playing_animation
141
+ self.stop_animation()
142
+
143
+ logging.info("Generating animation frames...")
144
+ images:list[Image.Image] = list()
145
+ for i in range(self._animation_total_frames):
146
+ self.update_animation(i)
147
+ screenshot = self.get_screenshot().convert("RGB").resize([1280, 720])
148
+ if is_gif:
149
+ screenshot = screenshot.quantize(method=Image.Quantize.FASTOCTREE, kmeans=2)
150
+ images.append(screenshot)
151
+
152
+ # recover the playing animation status
153
+ self.playing_animation = previous_state
154
+
155
+ logging.info("Saving animation to file...")
156
+ images[0].save(
157
+ path,
158
+ save_all=True,
159
+ append_images=images[1:],
160
+ optimize=False,
161
+ duration=self._animation_total_frames / self._animation_fps,
162
+ loop=0,
163
+ quality=90
164
+ )
@@ -0,0 +1,433 @@
1
+ from pathlib import Path
2
+ from threading import Lock
3
+ from typing import Literal
4
+
5
+ from PIL import Image
6
+ from PyQt5.QtCore import pyqtSignal
7
+ from PyQt5.QtWidgets import QFrame, QStackedLayout
8
+ from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
9
+ from vtkmodules.util.numpy_support import vtk_to_numpy
10
+ from vtkmodules.vtkCommonCore import VTK_FONT_FILE, vtkLookupTable
11
+ from vtkmodules.vtkInteractionWidgets import (
12
+ vtkLogoRepresentation,
13
+ vtkOrientationMarkerWidget,
14
+ )
15
+ from vtkmodules.vtkIOImage import vtkPNGReader
16
+ from vtkmodules.vtkRenderingAnnotation import (
17
+ vtkAxesActor,
18
+ vtkLegendScaleActor,
19
+ vtkScalarBarActor,
20
+ )
21
+ from vtkmodules.vtkRenderingCore import (
22
+ vtkLight,
23
+ vtkActor,
24
+ vtkRenderer,
25
+ vtkTextActor,
26
+ vtkInteractorStyle,
27
+ vtkTextProperty,
28
+ vtkWindowToImageFilter,
29
+ )
30
+
31
+ from molde import MOLDE_DIR
32
+ from molde.colors import Color
33
+ from molde.interactor_styles import ArcballCameraInteractorStyle
34
+
35
+
36
+ class CommonRenderWidget(QFrame):
37
+ """
38
+ This class is needed to show vtk renderers in pyqt.
39
+
40
+ A vtk widget must always have a renderer, even if it is empty.
41
+ """
42
+
43
+ left_clicked = pyqtSignal(int, int)
44
+ left_released = pyqtSignal(int, int)
45
+ right_clicked = pyqtSignal(int, int)
46
+ right_released = pyqtSignal(int, int)
47
+
48
+ def __init__(self, parent=None, *, theme="dark"):
49
+ super().__init__(parent)
50
+
51
+ self.renderer = vtkRenderer()
52
+ self.interactor_style = ArcballCameraInteractorStyle()
53
+ self.render_interactor = QVTKRenderWindowInteractor(self)
54
+
55
+ self.render_interactor.Initialize()
56
+ self.render_interactor.GetRenderWindow().AddRenderer(self.renderer)
57
+ self.render_interactor.SetInteractorStyle(self.interactor_style)
58
+
59
+ self.renderer.ResetCamera()
60
+
61
+ self.render_interactor.AddObserver(
62
+ "LeftButtonPressEvent",
63
+ self.left_click_press_event
64
+ )
65
+ self.render_interactor.AddObserver(
66
+ "LeftButtonReleaseEvent",
67
+ self.left_click_release_event
68
+ )
69
+ self.render_interactor.AddObserver(
70
+ "RightButtonPressEvent",
71
+ self.right_click_press_event
72
+ )
73
+ self.render_interactor.AddObserver(
74
+ "RightButtonReleaseEvent",
75
+ self.right_click_release_event
76
+ )
77
+
78
+ layout = QStackedLayout()
79
+ layout.addWidget(self.render_interactor)
80
+ self.setLayout(layout)
81
+
82
+ self._current_theme = theme
83
+ self._widget_actors = list()
84
+ self.update_lock = Lock()
85
+
86
+ self.create_info_text()
87
+ self.set_theme(self._current_theme)
88
+
89
+ def update_plot(self):
90
+ raise NotImplementedError("The function update_plot was not implemented")
91
+
92
+ def add_actors(self, *actors: vtkActor):
93
+ for actor in actors:
94
+ self.renderer.AddActor(actor)
95
+ if actor not in self._widget_actors:
96
+ self._widget_actors.append(actor)
97
+
98
+ def remove_actors(self, *actors: vtkActor):
99
+ for actor in actors:
100
+ self.renderer.RemoveActor(actor)
101
+ try:
102
+ self._widget_actors.remove(actor)
103
+ except ValueError:
104
+ continue
105
+
106
+ def remove_all_actors(self):
107
+ self.remove_actors(*self.get_widget_actors())
108
+
109
+ def remove_all_props(self):
110
+ self.renderer.RemoveAllViewProps()
111
+
112
+ def get_widget_actors(self):
113
+ return [i for i in self._widget_actors]
114
+
115
+ def set_interactor_style(self, interactor_style: vtkInteractorStyle):
116
+ self.interactor_style = interactor_style
117
+ self.render_interactor.SetInteractorStyle(interactor_style)
118
+
119
+ def get_interactor_style(self) -> vtkInteractorStyle:
120
+ return self.render_interactor.GetInteractorStyle()
121
+
122
+ def update(self):
123
+ if self.update_lock.locked():
124
+ return
125
+
126
+ ren_win = self.render_interactor.GetRenderWindow()
127
+ if ren_win is not None:
128
+ self.renderer.ResetCameraClippingRange()
129
+ ren_win.Render()
130
+
131
+ def left_click_press_event(self, obj, event):
132
+ x, y, *_ = self.render_interactor.GetEventPosition()
133
+ self.left_clicked.emit(x, y)
134
+
135
+ def left_click_release_event(self, obj, event):
136
+ x, y, *_ = self.render_interactor.GetEventPosition()
137
+ self.left_released.emit(x, y)
138
+
139
+ def right_click_press_event(self, obj, event):
140
+ x, y, *_ = self.render_interactor.GetEventPosition()
141
+ self.right_clicked.emit(x, y)
142
+
143
+ def right_click_release_event(self, obj, event):
144
+ x, y, *_ = self.render_interactor.GetEventPosition()
145
+ self.right_released.emit(x, y)
146
+
147
+ def get_screenshot(self) -> Image.Image:
148
+ image_filter = vtkWindowToImageFilter()
149
+ image_filter.SetInput(self.render_interactor.GetRenderWindow())
150
+ image_filter.Update()
151
+
152
+ vtk_image = image_filter.GetOutput()
153
+ width, height, _ = vtk_image.GetDimensions()
154
+ vtk_array = vtk_image.GetPointData().GetScalars()
155
+ components = vtk_array.GetNumberOfComponents()
156
+
157
+ array = vtk_to_numpy(vtk_array).reshape(height, width, components)
158
+ image = Image.fromarray(array).transpose(Image.FLIP_TOP_BOTTOM)
159
+ return image
160
+
161
+ def get_thumbnail(self):
162
+ image = self.get_screenshot()
163
+ return image.thumbnail((512, 512))
164
+
165
+ def save_image(self, path: str | Path):
166
+ '''
167
+ Saves the render as an image.
168
+ Supported formats are JPEG, JPG, PNG, BMP, ICO, TIFF, PPM and others.
169
+ '''
170
+ image = self.get_screenshot()
171
+ with open(path, "w") as file:
172
+ image.save(file)
173
+
174
+ def create_axes(self):
175
+ axes_actor = vtkAxesActor()
176
+
177
+ axes_actor.SetXAxisLabelText(" X")
178
+ axes_actor.SetYAxisLabelText(" Y")
179
+ axes_actor.SetZAxisLabelText(" Z")
180
+
181
+ axes_actor.GetXAxisShaftProperty().LightingOff()
182
+ axes_actor.GetYAxisShaftProperty().LightingOff()
183
+ axes_actor.GetZAxisShaftProperty().LightingOff()
184
+ axes_actor.GetXAxisTipProperty().LightingOff()
185
+ axes_actor.GetYAxisTipProperty().LightingOff()
186
+ axes_actor.GetZAxisTipProperty().LightingOff()
187
+
188
+ x_property = axes_actor.GetXAxisCaptionActor2D().GetCaptionTextProperty()
189
+ y_property = axes_actor.GetYAxisCaptionActor2D().GetCaptionTextProperty()
190
+ z_property = axes_actor.GetZAxisCaptionActor2D().GetCaptionTextProperty()
191
+
192
+ for text_property in [x_property, y_property, z_property]:
193
+ text_property: vtkTextProperty
194
+ text_property.ItalicOff()
195
+ text_property.BoldOn()
196
+
197
+ self.axes = vtkOrientationMarkerWidget()
198
+ self.axes.SetViewport(0, 0, 0.18, 0.18)
199
+ self.axes.SetOrientationMarker(axes_actor)
200
+ self.axes.SetInteractor(self.render_interactor)
201
+ self.axes.EnabledOn()
202
+ self.axes.InteractiveOff()
203
+
204
+ def create_scale_bar(self):
205
+ self.scale_bar_actor = vtkLegendScaleActor()
206
+ self.scale_bar_actor.AllAxesOff()
207
+ self.renderer.AddActor(self.scale_bar_actor)
208
+
209
+ font_file = MOLDE_DIR / "fonts/IBMPlexMono-Regular.ttf"
210
+
211
+ title_property: vtkTextProperty
212
+ title_property = self.scale_bar_actor.GetLegendTitleProperty()
213
+ title_property.SetFontSize(16)
214
+ title_property.ShadowOff()
215
+ title_property.ItalicOff()
216
+ title_property.BoldOn()
217
+ title_property.SetLineOffset(-55)
218
+ title_property.SetVerticalJustificationToTop()
219
+ title_property.SetFontFamily(VTK_FONT_FILE)
220
+ title_property.SetFontFile(font_file)
221
+
222
+ label_property: vtkTextProperty
223
+ label_property = self.scale_bar_actor.GetLegendLabelProperty()
224
+ label_property.SetFontSize(16)
225
+ label_property.SetColor((0.8, 0.8, 0.8))
226
+ label_property.ShadowOff()
227
+ label_property.ItalicOff()
228
+ label_property.BoldOff()
229
+ label_property.SetLineOffset(-35)
230
+ label_property.SetFontFamily(VTK_FONT_FILE)
231
+ label_property.SetFontFile(font_file)
232
+
233
+ # set the text color based on current theme
234
+ self.set_theme(self._current_theme)
235
+
236
+ def create_color_bar(self, lookup_table=None):
237
+ if lookup_table is None:
238
+ lookup_table = vtkLookupTable()
239
+ lookup_table.Build()
240
+
241
+ font_file = MOLDE_DIR / "fonts/IBMPlexMono-Regular.ttf"
242
+
243
+ self.colorbar_actor = vtkScalarBarActor()
244
+ self.colorbar_actor.SetLabelFormat("%1.0e ")
245
+ self.colorbar_actor.SetLookupTable(lookup_table)
246
+ self.colorbar_actor.SetWidth(0.02)
247
+ self.colorbar_actor.SetPosition(0.94, 0.17)
248
+ self.colorbar_actor.SetHeight(0.7)
249
+ self.colorbar_actor.SetMaximumNumberOfColors(400)
250
+ self.colorbar_actor.SetVerticalTitleSeparation(20)
251
+ self.colorbar_actor.UnconstrainedFontSizeOn()
252
+ self.colorbar_actor.SetTextPositionToPrecedeScalarBar()
253
+ self.renderer.AddActor(self.colorbar_actor)
254
+
255
+ colorbar_title: vtkTextProperty = self.colorbar_actor.GetTitleTextProperty()
256
+ colorbar_title.ShadowOff()
257
+ colorbar_title.ItalicOff()
258
+ colorbar_title.BoldOn()
259
+ colorbar_title.SetFontSize(16)
260
+ colorbar_title.SetJustificationToLeft()
261
+ colorbar_title.SetFontFamily(VTK_FONT_FILE)
262
+ colorbar_title.SetFontFile(font_file)
263
+
264
+ colorbar_label:vtkTextProperty = self.colorbar_actor.GetLabelTextProperty()
265
+ colorbar_label.ShadowOff()
266
+ colorbar_label.ItalicOff()
267
+ colorbar_label.BoldOn()
268
+ colorbar_label.SetFontSize(16)
269
+ colorbar_label.SetJustificationToLeft()
270
+ colorbar_label.SetFontFamily(VTK_FONT_FILE)
271
+ colorbar_label.SetFontFile(font_file)
272
+
273
+ # set the text color based on current theme
274
+ self.set_theme(self._current_theme)
275
+
276
+ def create_info_text(self):
277
+ font_file = MOLDE_DIR / "fonts/IBMPlexMono-Bold.ttf"
278
+
279
+ self.text_actor = vtkTextActor()
280
+ self.renderer.AddActor2D(self.text_actor)
281
+
282
+ info_text_property = self.text_actor.GetTextProperty()
283
+ info_text_property.SetFontSize(14)
284
+ info_text_property.SetVerticalJustificationToTop()
285
+ info_text_property.SetColor((0.2, 0.2, 0.2))
286
+ info_text_property.SetLineSpacing(1.3)
287
+ info_text_property.SetFontFamilyToTimes()
288
+ info_text_property.SetFontFamily(VTK_FONT_FILE)
289
+ info_text_property.SetFontFile(font_file)
290
+
291
+ coord = self.text_actor.GetPositionCoordinate()
292
+ coord.SetCoordinateSystemToNormalizedViewport()
293
+ coord.SetValue(0.01, 0.98)
294
+
295
+ # set the text color based on current theme
296
+ self.set_theme(self._current_theme)
297
+
298
+ def create_logo(self, path: str | Path) -> vtkLogoRepresentation:
299
+ path = Path(path)
300
+
301
+ image_reader = vtkPNGReader()
302
+ image_reader.SetFileName(path)
303
+ image_reader.Update()
304
+
305
+ logo = vtkLogoRepresentation()
306
+ logo.SetImage(image_reader.GetOutput())
307
+ logo.ProportionalResizeOn()
308
+ logo.GetImageProperty().SetOpacity(0.9)
309
+ logo.GetImageProperty().SetDisplayLocationToBackground()
310
+
311
+ self.renderer.AddViewProp(logo)
312
+ logo.SetRenderer(self.renderer)
313
+ return logo
314
+
315
+ def create_camera_light(self, offset_x=0, offset_y=0):
316
+ light = vtkLight()
317
+ light.SetLightTypeToCameraLight()
318
+ light.SetPosition(offset_x, offset_y, 1)
319
+ self.renderer.AddLight(light)
320
+
321
+ def set_colorbar_unit(self, text):
322
+ if not hasattr(self, "colorbar_actor"):
323
+ return
324
+ self.colorbar_actor.SetTitle(text)
325
+
326
+ def set_info_text(self, text):
327
+ self.text_actor.SetInput(text)
328
+
329
+ def set_theme(self, theme: Literal["dark", "light", "custom"], **kwargs):
330
+ self._current_theme = theme
331
+
332
+ if theme == "dark":
333
+ bkg_1 = Color("#0b0f17")
334
+ bkg_2 = Color("#3e424d")
335
+ font_color = Color("#CCCCCC")
336
+
337
+ elif theme == "light":
338
+ bkg_1 = Color("#8092A6")
339
+ bkg_2 = Color("#EEF2F3")
340
+ font_color = Color("#111111")
341
+
342
+ elif theme == "custom":
343
+ bkg_1 = kwargs.get("bkg_1")
344
+ bkg_2 = kwargs.get("bkg_2")
345
+ font_color = kwargs.get("font_color")
346
+
347
+ if bkg_1 is None:
348
+ raise ValueError('Missing value "bkg_1"')
349
+ if bkg_2 is None:
350
+ raise ValueError('Missing value "bkg_2"')
351
+ if font_color is None:
352
+ raise ValueError('Missing value "font_color"')
353
+
354
+ else:
355
+ return
356
+
357
+ self.renderer.GradientBackgroundOn()
358
+ self.renderer.SetBackground(bkg_1.to_rgb_f())
359
+ self.renderer.SetBackground2(bkg_2.to_rgb_f())
360
+
361
+ if hasattr(self, "text_actor"):
362
+ self.text_actor.GetTextProperty().SetColor(font_color.to_rgb_f())
363
+
364
+ if hasattr(self, "colorbar_actor"):
365
+ self.colorbar_actor.GetTitleTextProperty().SetColor(font_color.to_rgb_f())
366
+ self.colorbar_actor.GetLabelTextProperty().SetColor(font_color.to_rgb_f())
367
+
368
+ if hasattr(self, "scale_bar_actor"):
369
+ self.scale_bar_actor.GetLegendTitleProperty().SetColor(font_color.to_rgb_f())
370
+ self.scale_bar_actor.GetLegendLabelProperty().SetColor(font_color.to_rgb_f())
371
+
372
+ #
373
+ def set_custom_view(self, position, view_up):
374
+ self.renderer.GetActiveCamera().SetPosition(position)
375
+ self.renderer.GetActiveCamera().SetViewUp(view_up)
376
+ self.renderer.GetActiveCamera().SetParallelProjection(True)
377
+ self.renderer.ResetCamera(*self.renderer.ComputeVisiblePropBounds())
378
+ self.update()
379
+
380
+ def set_top_view(self):
381
+ x, y, z = self.renderer.GetActiveCamera().GetFocalPoint()
382
+ position = (x, y + 1, z)
383
+ view_up = (0, 0, -1)
384
+ self.set_custom_view(position, view_up)
385
+
386
+ def set_bottom_view(self):
387
+ x, y, z = self.renderer.GetActiveCamera().GetFocalPoint()
388
+ position = (x, y - 1, z)
389
+ view_up = (0, 0, 1)
390
+ self.set_custom_view(position, view_up)
391
+
392
+ def set_left_view(self):
393
+ x, y, z = self.renderer.GetActiveCamera().GetFocalPoint()
394
+ position = (x - 1, y, z)
395
+ view_up = (0, 1, 0)
396
+ self.set_custom_view(position, view_up)
397
+
398
+ def set_right_view(self):
399
+ x, y, z = self.renderer.GetActiveCamera().GetFocalPoint()
400
+ position = (x + 1, y, z)
401
+ view_up = (0, 1, 0)
402
+ self.set_custom_view(position, view_up)
403
+
404
+ def set_front_view(self):
405
+ x, y, z = self.renderer.GetActiveCamera().GetFocalPoint()
406
+ position = (x, y, z + 1)
407
+ view_up = (0, 1, 0)
408
+ self.set_custom_view(position, view_up)
409
+
410
+ def set_back_view(self):
411
+ x, y, z = self.renderer.GetActiveCamera().GetFocalPoint()
412
+ position = (x, y, z - 1)
413
+ view_up = (0, 1, 0)
414
+ self.set_custom_view(position, view_up)
415
+
416
+ def set_isometric_view(self):
417
+ x, y, z = self.renderer.GetActiveCamera().GetFocalPoint()
418
+ position = (x + 1, y + 1, z + 1)
419
+ view_up = (0, 1, 0)
420
+ self.set_custom_view(position, view_up)
421
+
422
+ def copy_camera_from(self, other):
423
+ if isinstance(other, CommonRenderWidget):
424
+ other_camera = other.renderer.GetActiveCamera()
425
+ elif isinstance(other, vtkRenderer):
426
+ other_camera = other.GetActiveCamera()
427
+ else:
428
+ return
429
+
430
+ self.renderer.GetActiveCamera().DeepCopy(other_camera)
431
+ self.renderer.ResetCameraClippingRange()
432
+ self.renderer.GetActiveCamera().Modified()
433
+ self.update()