You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Timer unit: 1e-09 s
Total time: 22.4593 s
File: /home/ian/Documents/oss/mpl-extensions/mpl-playback/mpl_playback/playback.py
Function: playback_events at line 145
Line # Hits Time Per Hit % Time Line Contents
==============================================================
145 def playback_events(
146 figures,
147 events,
148 meta,
149 globals,
150 outputs,
151 fps=24,
152 from_first_event=True,
153 prog_bar=True,
154 writer: Literal["pillow", "ffmpeg", "imagemagick", "avconv", None] = "pillow",
155 **kwargs,
156 ):
157 """
158 plays back events that have been
159
160 Parameters
161 ----------
162 ....
163 outputs : list of str or None
164 The paths to outputs. If None then the events will played back but
165 no outputs will saved.
166 fps : int, default: 24
167 Frames per second of the output
168 from_first_event : bool, default: True
169 Whether playback should start from the timing of the first recorded
170 event or from when recording was initiated.
171 prog_bar : bool, default: True
172 Whether to display a progress bar. If tqdm is not
173 available then this kwarg has no effect.
174 """
175 1 669.0 669.0 0.0 accessors = {}
176 1 295.0 295.0 0.0 fake_cursors = {}
177 1 219.0 219.0 0.0 writers = []
178 1 569.0 569.0 0.0 if writer == "ffmpeg" and FFMpegWriter.isAvailable():
179 writer = FFMpegWriter
180 1 351.0 351.0 0.0 elif writer == "imagemagick" and ImageMagickWriter.isAvailable():
181 writer = ImageMagickWriter
182 1 274.0 274.0 0.0 elif writer == "avconv" and AVConvWriter is not None and AVConvWriter.isAvailable():
183 writer = AVConvWriter
184 else:
185 1 463.0 463.0 0.0 writer = PillowWriter
186
187 1 179.0 179.0 0.0 _figs = {} # the actual figure objects
188 1 200.0 200.0 0.0 transforms = {}
189 2 2353.0 1176.5 0.0 for fig, out in zip(figures, outputs):
190 1 46722.0 46722.0 0.0 _fig = extract_by_name(fig, globals)
191 1 490.0 490.0 0.0 _figs[fig] = _fig
192 # pre invert for performance (i have no idea if this owuld get cached.)
193 # but if it's inverting a matrix then it's bad news to constantly invert
194 1 66949.0 66949.0 0.0 transforms[fig] = _fig.transFigure.inverted().frozen()
195 1 303.0 303.0 0.0 accessors[fig] = _fig
196 3 915324.0 305108.0 0.0 fake_cursors[fig] = _fig.axes[-1].plot(
197 1 371.0 371.0 0.0 [0, 0],
198 1 218.0 218.0 0.0 [0, 0],
199 1 196.0 196.0 0.0 "k",
200 1 146.0 146.0 0.0 marker=6,
201 1 453.0 453.0 0.0 markersize=15,
202 # confusing that this is necessary, see note in animate below
203 1 290.0 290.0 0.0 transform=_fig.transFigure,
204 1 134.0 134.0 0.0 clip_on=False,
205 1 214.0 214.0 0.0 zorder=99999,
206 1 291.0 291.0 0.0 )[0]
207 1 273.0 273.0 0.0 if outputs is not None:
208 1 9758.0 9758.0 0.0 writers.append(writer(fps))
209 1 84690.0 84690.0 0.0 writers[-1].setup(_fig, out, len(events))
210
211 1 44071219.0 4e+07 0.2 times, mock_events = gen_mock_events(events, globals, accessors)
212 1 400.0 400.0 0.0 if from_first_event:
213 1 14454.0 14454.0 0.0 times -= times[0]
214 1 2102.0 2102.0 0.0 N_frames = int(times[-1] * fps)
215 # map from frames to events
216 1 21293.0 21293.0 0.0 event_frames = np.round(times * fps)
217
218 1 548.0 548.0 0.0 if prog_bar and _prog_bar:
219 pbar = tqdm(total=N_frames)
220
221 global animate
222 1 2999841.0 3e+06 0.0 def animate(i):
223 idx = event_frames == i
224 if np.sum(idx) != 0:
225 for event in mock_events[idx]:
226 # event = mock_events[i]
227 accessors[event._figname].canvas.callbacks.process(event.name, event)
228 if event.name == "motion_notify_event":
229 # now set the cursor invisible so multiple don't show up
230 # if there are multiple figures
231 for fc in fake_cursors.values():
232 fc.set_visible(False)
233
234 # It really seems as though this transform should be uncessary
235 # and with only a single figure it is uncesssary. But for reasons
236 # that are beyond me, when there are multiple figures this
237 # transform is crucial or else the cursor will show up in a weirdly
238 # scaled position. This is true even with setting `transform=None`
239 # on the origin `plot` call
240 f = _figs[event._figname]
241 xy = transforms[event._figname].transform([event.x, event.y])
242 fake_cursors[event._figname].set_data([xy[0]], [xy[1]])
243 fake_cursors[event._figname].set_visible(True)
244
245 # theres got to be a clever way to avoid doing these gazillion draws
246 # maybe monkeypatching the figure's draw event?
247 for f in _figs.values():
248 f.canvas.draw()
249
250 if prog_bar and _prog_bar:
251 pbar.update(1)
252 if outputs is not None:
253 for w in writers:
254 w.grab_frame()
255
256 127 119495.0 940.9 0.0 for i in range(N_frames):
257 126 2e+10 2e+08 89.8 animate(i)
258
259 1 357.0 357.0 0.0 if outputs is not None:
260 2 1492.0 746.0 0.0 for w in writers:
261 1 2232775993.0 2e+09 9.9 w.finish()
The animate function (which is 90% of execution time):
Line # Hits Time Per Hit % Time Line Contents
==============================================================
222 def animate(i):
223 126 1072116.0 8508.9 0.0 idx = event_frames == i
224 126 3103144.0 24628.1 0.0 if np.sum(idx) != 0:
225 250 1048384.0 4193.5 0.0 for event in mock_events[idx]:
226 # event = mock_events[i]
227 157 6814067897.0 4e+07 30.5 accessors[event._figname].canvas.callbacks.process(event.name, event)
228 157 166248.0 1058.9 0.0 if event.name == "motion_notify_event":
229 # now set the cursor invisible so multiple don't show up
230 # if there are multiple figures
231 310 270923.0 873.9 0.0 for fc in fake_cursors.values():
232 155 2530040.0 16322.8 0.0 fc.set_visible(False)
233
234 # It really seems as though this transform should be uncessary
235 # and with only a single figure it is uncesssary. But for reasons
236 # that are beyond me, when there are multiple figures this
237 # transform is crucial or else the cursor will show up in a weirdly
238 # scaled position. This is true even with setting `transform=None`
239 # on the origin `plot` call
240 155 154760.0 998.5 0.0 f = _figs[event._figname]
241 155 1485817.0 9585.9 0.0 xy = transforms[event._figname].transform([event.x, event.y])
242 155 3826124.0 24684.7 0.0 fake_cursors[event._figname].set_data([xy[0]], [xy[1]])
243 155 1728288.0 11150.2 0.0 fake_cursors[event._figname].set_visible(True)
244
245 # theres got to be a clever way to avoid doing these gazillion draws
246 # maybe monkeypatching the figure's draw event?
247 314 263247.0 838.4 0.0 for f in _figs.values():
248 157 7250215170.0 5e+07 32.5 f.canvas.draw()
249
250 126 109055.0 865.5 0.0 if prog_bar and _prog_bar:
251 pbar.update(1)
252 126 46160.0 366.3 0.0 if outputs is not None:
253 252 217494.0 863.1 0.0 for w in writers:
254 126 8236026249.0 7e+07 36.9 w.grab_frame()
Activity
ianhi commentedon Aug 18, 2023
lineprofiling playback:
playback_events
The animate function (which is 90% of execution time):
ianhi commentedon Aug 18, 2023
some big improvments in #20