molde 0.1.1__py3-none-any.whl → 0.1.2__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 (55) hide show
  1. molde/__init__.py +6 -5
  2. molde/__main__.py +74 -63
  3. molde/actors/__init__.py +5 -4
  4. molde/actors/common_symbols_actor.py +148 -0
  5. molde/actors/ghost_actor.py +12 -12
  6. molde/actors/lines_actor.py +31 -31
  7. molde/actors/round_points_actor.py +7 -7
  8. molde/actors/square_points_actor.py +32 -32
  9. molde/colors/__init__.py +1 -1
  10. molde/colors/color.py +120 -120
  11. molde/colors/color_names.py +124 -124
  12. molde/interactor_styles/__init__.py +2 -2
  13. molde/interactor_styles/arcball_camera_style.py +288 -272
  14. molde/interactor_styles/box_selection_style.py +70 -70
  15. molde/main_window.ui +864 -0
  16. molde/pickers/__init__.py +2 -2
  17. molde/pickers/cell_area_picker.py +61 -61
  18. molde/pickers/cell_property_area_picker.py +84 -84
  19. molde/poly_data/__init__.py +19 -2
  20. molde/poly_data/arrows.py +54 -0
  21. molde/poly_data/complex_shapes.py +26 -0
  22. molde/poly_data/lines_data.py +23 -23
  23. molde/poly_data/simple_shapes.py +22 -0
  24. molde/poly_data/vertices_data.py +24 -24
  25. molde/render_widgets/__init__.py +2 -2
  26. molde/render_widgets/animated_render_widget.py +164 -164
  27. molde/render_widgets/common_render_widget.py +433 -433
  28. molde/stylesheets/__init__.py +119 -119
  29. molde/stylesheets/common.qss +16 -16
  30. molde/stylesheets/create_color_page.py +61 -61
  31. molde/stylesheets/mainwindow.ui +611 -611
  32. molde/stylesheets/qcheckbox.qss +18 -18
  33. molde/stylesheets/qinputs.qss +78 -78
  34. molde/stylesheets/qlayouts.qss +22 -22
  35. molde/stylesheets/qmenubar.qss +12 -12
  36. molde/stylesheets/qprogressbar.qss +11 -11
  37. molde/stylesheets/qpushbutton.qss +90 -90
  38. molde/stylesheets/qradiobutton.qss +30 -30
  39. molde/stylesheets/qscrollbar.qss +29 -29
  40. molde/stylesheets/qslider.qss +61 -61
  41. molde/stylesheets/qtablewidget.qss +27 -27
  42. molde/stylesheets/qtabwidget.qss +29 -29
  43. molde/stylesheets/qtoolbar.qss +62 -62
  44. molde/stylesheets/qtoolbuttons.qss +14 -14
  45. molde/stylesheets/qtreewidget.qss +25 -25
  46. molde/ui_files/messages/new_loading_window.ui +73 -0
  47. molde/utils/__init__.py +8 -8
  48. molde/utils/format_sequences.py +44 -44
  49. molde/utils/poly_data_utils.py +24 -24
  50. molde/utils/tree_info.py +52 -52
  51. molde/windows/loading_window.py +189 -0
  52. {molde-0.1.1.dist-info → molde-0.1.2.dist-info}/METADATA +4 -2
  53. molde-0.1.2.dist-info/RECORD +69 -0
  54. {molde-0.1.1.dist-info → molde-0.1.2.dist-info}/WHEEL +1 -1
  55. molde-0.1.1.dist-info/RECORD +0 -62
@@ -1,433 +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()
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()