molde 0.1.12__py3-none-any.whl → 0.1.14__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.
- molde/__init__.py +12 -12
- molde/__main__.py +81 -81
- molde/actors/__init__.py +5 -5
- molde/actors/common_symbols_actor.py +148 -148
- molde/actors/ghost_actor.py +12 -12
- molde/actors/lines_actor.py +31 -31
- molde/actors/round_points_actor.py +7 -7
- molde/actors/square_points_actor.py +32 -32
- molde/colors/__init__.py +1 -1
- molde/colors/color.py +273 -150
- molde/colors/color_names.py +124 -124
- molde/interactor_styles/__init__.py +2 -2
- molde/interactor_styles/arcball_camera_style.py +288 -288
- molde/interactor_styles/box_selection_style.py +87 -87
- molde/main_window.ui +864 -864
- molde/pickers/__init__.py +2 -2
- molde/pickers/cell_area_picker.py +61 -61
- molde/pickers/cell_property_area_picker.py +84 -84
- molde/poly_data/__init__.py +13 -13
- molde/poly_data/lines_data.py +23 -23
- molde/poly_data/vertices_data.py +24 -24
- molde/render_widgets/__init__.py +2 -2
- molde/render_widgets/animated_render_widget.py +164 -164
- molde/render_widgets/common_render_widget.py +429 -429
- molde/stylesheets/__init__.py +122 -122
- molde/stylesheets/common.qss +15 -15
- molde/stylesheets/create_color_page.py +61 -61
- molde/stylesheets/mainwindow.ui +646 -646
- molde/stylesheets/qcheckbox.qss +23 -23
- molde/stylesheets/qinputs.qss +81 -81
- molde/stylesheets/qlayouts.qss +23 -23
- molde/stylesheets/qmenubar.qss +12 -12
- molde/stylesheets/qprogressbar.qss +11 -11
- molde/stylesheets/qpushbutton.qss +89 -89
- molde/stylesheets/qradiobutton.qss +30 -30
- molde/stylesheets/qscrollbar.qss +29 -29
- molde/stylesheets/qslider.qss +61 -61
- molde/stylesheets/qtablewidget.qss +27 -27
- molde/stylesheets/qtabwidget.qss +28 -28
- molde/stylesheets/qtoolbar.qss +63 -63
- molde/stylesheets/qtoolbuttons.qss +14 -14
- molde/stylesheets/qtreewidget.qss +25 -25
- molde/ui_files/messages/new_loading_window.ui +73 -73
- molde/utils/__init__.py +8 -8
- molde/utils/format_sequences.py +44 -44
- molde/utils/poly_data_utils.py +66 -66
- molde/utils/tree_info.py +52 -52
- molde/windows/loading_window.py +189 -189
- {molde-0.1.12.dist-info → molde-0.1.14.dist-info}/METADATA +1 -1
- molde-0.1.14.dist-info/RECORD +68 -0
- {molde-0.1.12.dist-info → molde-0.1.14.dist-info}/WHEEL +1 -1
- molde-0.1.12.dist-info/RECORD +0 -68
@@ -1,164 +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
|
-
duration=self._animation_total_frames / self._animation_fps,
|
161
|
-
loop=0,
|
162
|
-
optimize=True,
|
163
|
-
lossless=True,
|
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
|
+
duration=self._animation_total_frames / self._animation_fps,
|
161
|
+
loop=0,
|
162
|
+
optimize=True,
|
163
|
+
lossless=True,
|
164
|
+
)
|