Fix Print3D Toolbox: fix button alignment
[blender-addons.git] / space_view3d_screencast_keys.py
blobf568c31ab6f6d293a7cd2655ce6c9e0fd2b11beb
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8 compliant>
21 bl_info = {
22 "name": "Screencast Keys",
23 "author": "Paulo Gomes, Bart Crouch, John E. Herrenyo, Gaia Clary, Pablo Vazquez",
24 "version": (1, 7),
25 "blender": (2, 66, 0),
26 "location": "3D View > Properties Panel > Screencast Keys",
27 "warning": "",
28 "description": "Display keys pressed in the 3D View, "
29 "useful for screencasts.",
30 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/"
31 "Py/Scripts/3D_interaction/Screencast_Key_Status_Tool",
32 "tracker_url": "http://projects.blender.org/tracker/index.php?"
33 "func=detail&aid=21612",
34 "category": "3D View"}
36 import bgl
37 import blf
38 import bpy
39 import time
40 import datetime
43 MOUSE_RATIO = 0.535
46 def getDisplayLocation(context):
47 scene = context.scene
48 mouse_size = scene.screencast_keys_mouse_size
50 pos_x = int( (context.region.width - mouse_size * MOUSE_RATIO) * \
51 scene.screencast_keys_pos_x / 100)
52 pos_y = int( (context.region.height - mouse_size) *
53 scene.screencast_keys_pos_y / 100)
55 return(pos_x, pos_y)
58 def getBoundingBox(current_width, current_height, new_text):
59 w,h = blf.dimensions(0,new_text)
60 if w > current_width:
61 current_width = w
62 current_height += h
64 return(current_width, current_height)
67 def draw_callback_px_text(self, context):
68 wm = context.window_manager
69 sc = context.scene
70 if not wm.screencast_keys_keys:
71 return
73 font_size = sc.screencast_keys_font_size
74 mouse_size = sc.screencast_keys_mouse_size
75 box_draw = sc.screencast_keys_box_draw
76 pos_x, pos_y = getDisplayLocation(context)
77 label_time_max = sc.screencast_keys_fade_time
79 # draw text in the 3D View
80 blf.size(0, sc.screencast_keys_font_size, 72)
81 blf.enable(0, blf.SHADOW)
82 blf.shadow_offset(0, 1, -1)
83 blf.shadow(0, 5, 0.0, 0.0, 0.0, 0.8)
85 font_color_r, font_color_g, font_color_b, font_color_alpha = sc.screencast_keys_text_color
86 final = 0
87 row_count = len(self.key)
89 keypos_x = pos_x
91 if sc.screencast_keys_mouse_position == 'left':
92 keypos_x += mouse_size * MOUSE_RATIO * 1.7
93 if sc.screencast_keys_mouse != 'icon':
94 keypos_x -= mouse_size * MOUSE_RATIO
95 if sc.screencast_keys_mouse_position == 'right' and sc.screencast_keys_mouse != 'icon':
96 keypos_x = pos_x
98 shift = 0
100 # we want to make sure we can shift vertically the text if the mouse is big,
101 # but don't care if aligned to right
102 if mouse_size > font_size*row_count and not sc.screencast_keys_mouse_position == 'right':
103 shift = (mouse_size - font_size*row_count) / 2
105 text_width, text_height = 0,0
106 row_count = 0
107 alpha = 1.0
109 for i in range(len(self.key)):
110 label_time = time.time() - self.time[i]
111 if label_time < label_time_max: # only display key-presses of last 2 seconds
112 if label_time > (label_time_max / 1.2):
113 blf.blur(0, 1)
114 if label_time > (label_time_max / 1.1):
115 blf.blur(0, 3)
116 keypos_y = pos_y + shift + font_size*(i+0.1)
118 blf.position(0, keypos_x, keypos_y , 0)
119 alpha = min(1.0, max(0.0, label_time_max * (label_time_max - label_time)))
120 bgl.glColor4f(font_color_r, font_color_g, font_color_b, font_color_alpha * alpha)
121 blf.draw(0, self.key[i])
122 text_width, text_height = getBoundingBox(text_width, text_height,
123 self.key[i])
124 row_count += 1
125 final = i + 1
126 else:
127 break
129 # remove blurriness
131 # disable shadows so they don't appear all over blender
132 blf.blur(0,0)
133 blf.disable(0, blf.SHADOW)
135 # get rid of status texts that aren't displayed anymore
136 self.key = self.key[:final]
137 self.time = self.time[:final]
139 # draw graphical representation of the mouse
140 if sc.screencast_keys_mouse == 'icon':
141 for shape in ["mouse", "left_button", "middle_button", "right_button"]:
142 draw_mouse(context, shape, "outline", font_color_alpha * 0.4)
143 final = 0
145 for i in range(len(self.mouse)):
146 click_time = time.time() - self.mouse_time[i]
147 if click_time < 2:
148 shape = map_mouse_event(self.mouse[i])
149 if shape:
150 alpha = min(1.0, max(0.0, 2 * (2 - click_time)))
151 draw_mouse(context, shape, "filled", alpha)
152 final = i + 1
153 else:
154 break
156 # get rid of mouse clicks that aren't displayed anymore
157 self.mouse = self.mouse[:final]
158 self.mouse_time = self.mouse_time[:final]
160 def draw_callback_px_box(self, context):
161 wm = context.window_manager
162 sc = context.scene
164 if not wm.screencast_keys_keys:
165 return
167 font_size = sc.screencast_keys_font_size
168 mouse_size = sc.screencast_keys_mouse_size
170 if sc.screencast_keys_mouse_position == 'right':
171 mouse_size = 25
173 box_draw = sc.screencast_keys_box_draw
174 pos_x, pos_y = getDisplayLocation(context)
176 # get text-width/height to resize the box
177 blf.size(0, sc.screencast_keys_font_size, 72)
178 box_width, box_height = sc.screencast_keys_box_width,0
179 final = 0
180 row_count = 0
181 box_hide = sc.screencast_keys_box_hide
182 label_time_max = sc.screencast_keys_fade_time
184 for i in range(len(self.key)):
185 label_time = time.time() - self.time[i]
187 if label_time < label_time_max: # only display key-presses of last 4 seconds
188 box_width, box_height = getBoundingBox(box_width, box_height, self.key[i])
189 row_count += 1
190 final = i + 1
191 box_hide = False
192 else:
193 break
195 # Got the size right, now draw box using proper colors
196 box_color_r, box_color_g, box_color_b, box_color_alpha = sc.screencast_keys_box_color
198 if box_draw and not box_hide:
199 padding_x = 16
200 padding_y = 12
201 x0 = max(0, pos_x - padding_x)
202 y0 = max(0, pos_y - padding_y)
203 x1 = pos_x + box_width + mouse_size * MOUSE_RATIO * 1.3 + padding_x
204 y1 = pos_y + max(mouse_size, font_size * row_count) + padding_y
205 positions = [[x0, y0], [x0, y1], [x1, y1], [x1, y0]]
206 settings = [[bgl.GL_QUADS, min(0.0, box_color_alpha)], [bgl.GL_LINE_LOOP, min(0.0, box_color_alpha)]]
208 for mode, box_alpha in settings:
209 bgl.glEnable(bgl.GL_BLEND)
210 bgl.glBegin(mode)
211 bgl.glColor4f(box_color_r, box_color_g, box_color_b, box_color_alpha)
212 for v1, v2 in positions:
213 bgl.glVertex2f(v1, v2)
214 bgl.glEnd()
216 if sc.screencast_keys_show_operator:
217 draw_last_operator(context, pos_x, pos_y)
219 if sc.screencast_keys_timer_show:
220 draw_timer(context, pos_x, pos_y)
222 # get rid of status texts that aren't displayed anymore
223 self.key = self.key[:final]
224 self.time = self.time[:final]
227 def draw_callback_px(self, context):
228 draw_callback_px_text(self, context)
229 draw_callback_px_box(self, context)
232 def draw_last_operator(context, pos_x, pos_y):
234 wm = context.window_manager
235 sc = context.scene
236 font_color_r, font_color_g, font_color_b, font_color_alpha = sc.screencast_keys_text_color
237 pos_x, pos_y = getDisplayLocation(context)
239 if wm.operators:
240 last_operator = wm.operators[-1].bl_label
242 blf.enable(0, blf.SHADOW)
243 blf.shadow_offset(0, 1, -1)
244 blf.shadow(0, 5, 0.0, 0.0, 0.0, 0.8)
245 blf.size(0, sc.screencast_keys_font_size, 36)
246 blf.position(0, pos_x - 14, pos_y - 30, 0)
247 bgl.glColor4f(font_color_r, font_color_g, font_color_b, font_color_alpha * 0.8)
248 blf.draw(0, "Last: %s" % (last_operator))
249 blf.disable(0, blf.SHADOW)
251 def draw_timer(context, pos_x, pos_y):
253 sc = context.scene
254 #calculate overall time
255 overall_time = datetime.timedelta(seconds=int(time.time() - ScreencastKeysStatus.overall_time[0]))
257 timer_color_r, timer_color_g, timer_color_b, timer_color_alpha = sc.screencast_keys_timer_color
258 pos_x = context.region.width - (sc.screencast_keys_timer_size * 12) + 12
259 pos_y = 10
261 #draw time
262 blf.size(0, sc.screencast_keys_timer_size, 72)
263 blf.position(0, pos_x, pos_y, 0)
264 bgl.glColor4f(timer_color_r, timer_color_g, timer_color_b, timer_color_alpha)
265 blf.draw(0, "Elapsed Time: %s" % (overall_time))
267 def draw_mouse(context, shape, style, alpha):
268 # shape and position
269 sc = context.scene
270 mouse_size = sc.screencast_keys_mouse_size
271 font_size = sc.screencast_keys_font_size
272 box_draw = sc.screencast_keys_box_draw
274 pos_x, pos_y = getDisplayLocation(context)
276 if sc.screencast_keys_mouse_position == 'left':
277 offset_x = pos_x
278 if sc.screencast_keys_mouse_position == 'right':
279 offset_x = context.region.width - pos_x - (mouse_size * MOUSE_RATIO)
281 offset_y = pos_y
282 if font_size > mouse_size:
283 offset_y += (font_size - mouse_size) / 2
285 shape_data = get_shape_data(shape)
287 bgl.glTranslatef(offset_x, offset_y, 0)
289 # color
290 r, g, b, a = sc.screencast_keys_text_color
291 bgl.glEnable(bgl.GL_BLEND)
292 bgl.glColor4f(r, g, b, alpha)
294 # inner shape for filled style
295 if style == "filled":
296 inner_shape = []
297 for i in shape_data:
298 inner_shape.append(i[0])
300 # outer shape
301 for i in shape_data:
302 shape_segment = i
303 shape_segment[0] = [mouse_size * k for k in shape_segment[0]]
304 shape_segment[1] = [mouse_size * k for k in shape_segment[1]]
305 shape_segment[2] = [mouse_size * k for k in shape_segment[2]]
306 shape_segment[3] = [mouse_size * k for k in shape_segment[3]]
308 # create the buffer
309 shape_buffer = bgl.Buffer(bgl.GL_FLOAT, [4, 3], shape_segment)
311 # create the map and draw the triangle fan
312 bgl.glMap1f(bgl.GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, shape_buffer)
313 bgl.glEnable(bgl.GL_MAP1_VERTEX_3)
315 if style == "outline":
316 bgl.glBegin(bgl.GL_LINE_STRIP)
317 else: # style == "filled"
318 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
319 for j in range(10):
320 bgl.glEvalCoord1f(j / 10.0)
321 x, y, z = shape_segment[3]
323 # make sure the last vertex is indeed the last one, to avoid gaps
324 bgl.glVertex3f(x, y, z)
325 bgl.glEnd()
326 bgl.glDisable(bgl.GL_MAP1_VERTEX_3)
328 # draw interior
329 if style == "filled":
330 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
331 for i in inner_shape:
332 j = [mouse_size * k for k in i]
333 x, y, z = j
334 bgl.glVertex3f(x, y, z)
335 bgl.glEnd()
337 bgl.glTranslatef(-offset_x, -offset_y, 0)
339 # hardcoded data to draw the graphical represenation of the mouse
340 def get_shape_data(shape):
341 data = []
342 if shape == "mouse":
343 data = [[[0.404, 0.032, 0.0],
344 [0.096, 0.002, 0.0],
345 [0.059, 0.126, 0.0],
346 [0.04, 0.213, 0.0]],
347 [[0.04, 0.213, 0.0],
348 [-0.015, 0.465, 0.0],
349 [-0.005, 0.564, 0.0],
350 [0.032, 0.87, 0.0]],
351 [[0.032, 0.87, 0.0],
352 [0.05, 0.973, 0.0],
353 [0.16, 1.002, 0.0],
354 [0.264, 1.002, 0.0]],
355 [[0.264, 1.002, 0.0],
356 [0.369, 1.002, 0.0],
357 [0.478, 0.973, 0.0],
358 [0.497, 0.87, 0.0]],
359 [[0.497, 0.87, 0.0],
360 [0.533, 0.564, 0.0],
361 [0.554, 0.465, 0.0],
362 [0.499, 0.213, 0.0]],
363 [[0.499, 0.213, 0.0],
364 [0.490, 0.126, 0.0],
365 [0.432, 0.002, 0.0],
366 [0.404, 0.032, 0.0]]]
367 elif shape == "left_button":
368 data = [[[0.154, 0.763, 0.0],
369 [0.126, 0.755, 0.0],
370 [0.12, 0.754, 0.0],
371 [0.066, 0.751, 0.0]],
372 [[0.066, 0.751, 0.0],
373 [0.043, 0.75, 0.0],
374 [0.039, 0.757, 0.0],
375 [0.039, 0.767, 0.0]],
376 [[0.039, 0.767, 0.0],
377 [0.047, 0.908, 0.0],
378 [0.078, 0.943, 0.0],
379 [0.155, 0.97, 0.0]],
380 [[0.155, 0.97, 0.0],
381 [0.174, 0.977, 0.0],
382 [0.187, 0.975, 0.0],
383 [0.191, 0.972, 0.0]],
384 [[0.191, 0.972, 0.0],
385 [0.203, 0.958, 0.0],
386 [0.205, 0.949, 0.0],
387 [0.199, 0.852, 0.0]],
388 [[0.199, 0.852, 0.0],
389 [0.195, 0.77, 0.0],
390 [0.18, 0.771, 0.0],
391 [0.154, 0.763, 0.0]]]
392 elif shape == "middle_button":
393 data = [[[0.301, 0.8, 0.0],
394 [0.298, 0.768, 0.0],
395 [0.231, 0.768, 0.0],
396 [0.228, 0.8, 0.0]],
397 [[0.228, 0.8, 0.0],
398 [0.226, 0.817, 0.0],
399 [0.225, 0.833, 0.0],
400 [0.224, 0.85, 0.0]],
401 [[0.224, 0.85, 0.0],
402 [0.222, 0.873, 0.0],
403 [0.222, 0.877, 0.0],
404 [0.224, 0.9, 0.0]],
405 [[0.224, 0.9, 0.0],
406 [0.225, 0.917, 0.0],
407 [0.226, 0.933, 0.0],
408 [0.228, 0.95, 0.0]],
409 [[0.228, 0.95, 0.0],
410 [0.231, 0.982, 0.0],
411 [0.298, 0.982, 0.0],
412 [0.301, 0.95, 0.0]],
413 [[0.301, 0.95, 0.0],
414 [0.302, 0.933, 0.0],
415 [0.303, 0.917, 0.0],
416 [0.305, 0.9, 0.0]],
417 [[0.305, 0.9, 0.0],
418 [0.307, 0.877, 0.0],
419 [0.307, 0.873, 0.0],
420 [0.305, 0.85, 0.0]],
421 [[0.305, 0.85, 0.0],
422 [0.303, 0.833, 0.0],
423 [0.302, 0.817, 0.0],
424 [0.301, 0.8, 0.0]]]
425 elif shape == "middle_down_button":
426 data = [[[0.301, 0.8, 0.0],
427 [0.298, 0.768, 0.0],
428 [0.231, 0.768, 0.0],
429 [0.228, 0.8, 0.0]],
430 [[0.228, 0.8, 0.0],
431 [0.226, 0.817, 0.0],
432 [0.225, 0.833, 0.0],
433 [0.224, 0.85, 0.0]],
434 [[0.224, 0.85, 0.0],
435 [0.264, 0.873, 0.0],
436 [0.284, 0.873, 0.0],
437 [0.305, 0.85, 0.0]],
438 [[0.305, 0.85, 0.0],
439 [0.303, 0.833, 0.0],
440 [0.302, 0.817, 0.0],
441 [0.301, 0.8, 0.0]]]
442 elif shape == "middle_up_button":
443 data = [[[0.270, 0.873, 0.0],
444 [0.264, 0.873, 0.0],
445 [0.222, 0.877, 0.0],
446 [0.224, 0.9, 0.0]],
447 [[0.224, 0.9, 0.0],
448 [0.225, 0.917, 0.0],
449 [0.226, 0.933, 0.0],
450 [0.228, 0.95, 0.0]],
451 [[0.228, 0.95, 0.0],
452 [0.231, 0.982, 0.0],
453 [0.298, 0.982, 0.0],
454 [0.301, 0.95, 0.0]],
455 [[0.301, 0.95, 0.0],
456 [0.302, 0.933, 0.0],
457 [0.303, 0.917, 0.0],
458 [0.305, 0.9, 0.0]],
459 [[0.305, 0.9, 0.0],
460 [0.307, 0.877, 0.0],
461 [0.284, 0.873, 0.0],
462 [0.270, 0.873, 0.0]]]
463 elif shape == "right_button":
464 data = [[[0.375, 0.763, 0.0],
465 [0.402, 0.755, 0.0],
466 [0.408, 0.754, 0.0],
467 [0.462, 0.751, 0.0]],
468 [[0.462, 0.751, 0.0],
469 [0.486, 0.75, 0.0],
470 [0.49, 0.757, 0.0],
471 [0.489, 0.767, 0.0]],
472 [[0.489, 0.767, 0.0],
473 [0.481, 0.908, 0.0],
474 [0.451, 0.943, 0.0],
475 [0.374, 0.97, 0.0]],
476 [[0.374, 0.97, 0.0],
477 [0.354, 0.977, 0.0],
478 [0.341, 0.975, 0.0],
479 [0.338, 0.972, 0.0]],
480 [[0.338, 0.972, 0.0],
481 [0.325, 0.958, 0.0],
482 [0.324, 0.949, 0.0],
483 [0.329, 0.852, 0.0]],
484 [[0.329, 0.852, 0.0],
485 [0.334, 0.77, 0.0],
486 [0.348, 0.771, 0.0],
487 [0.375, 0.763, 0.0]]]
489 return(data)
492 # return the shape that belongs to the given event
493 def map_mouse_event(event):
494 shape = False
496 if event == 'LEFTMOUSE':
497 shape = "left_button"
498 elif event == 'MIDDLEMOUSE':
499 shape = "middle_button"
500 elif event == 'RIGHTMOUSE':
501 shape = "right_button"
502 elif event == 'WHEELDOWNMOUSE':
503 shape = "middle_down_button"
504 elif event == 'WHEELUPMOUSE':
505 shape = "middle_up_button"
507 return(shape)
509 class ScreencastKeysStatus(bpy.types.Operator):
510 bl_idname = "view3d.screencast_keys"
511 bl_label = "Screencast Keys"
512 bl_description = "Display keys pressed in the 3D View"
513 last_activity = 'NONE'
515 _handle = None
516 _timer = None
518 @staticmethod
519 def handle_add(self, context):
520 ScreencastKeysStatus._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL')
521 ScreencastKeysStatus._timer = context.window_manager.event_timer_add(0.075, context.window)
523 @staticmethod
524 def handle_remove(context):
525 if ScreencastKeysStatus._handle is not None:
526 context.window_manager.event_timer_remove(ScreencastKeysStatus._timer)
527 bpy.types.SpaceView3D.draw_handler_remove(ScreencastKeysStatus._handle, 'WINDOW')
528 ScreencastKeysStatus._handle = None
529 ScreencastKeysStatus._timer = None
531 def modal(self, context, event):
532 if context.area:
533 context.area.tag_redraw()
535 if event.type == 'TIMER':
536 # no input, so no need to change the display
537 return {'PASS_THROUGH'}
539 scene = context.scene
540 # keys that shouldn't show up in the 3D View
541 mouse_keys = ['MOUSEMOVE','MIDDLEMOUSE','LEFTMOUSE',
542 'RIGHTMOUSE', 'WHEELDOWNMOUSE','WHEELUPMOUSE']
543 ignore_keys = ['LEFT_SHIFT', 'RIGHT_SHIFT', 'LEFT_ALT',
544 'RIGHT_ALT', 'LEFT_CTRL', 'RIGHT_CTRL', 'TIMER',
545 'MOUSEMOVE', 'INBETWEEN_MOUSEMOVE']
546 if scene.screencast_keys_mouse != 'text':
547 ignore_keys.extend(mouse_keys)
549 if event.value == 'PRESS' or (event.value == 'RELEASE' and \
550 self.last_activity == 'KEYBOARD' and event.type in mouse_keys):
551 # add key-press to display-list
552 sc_keys = []
554 if event.ctrl:
555 sc_keys.append("Ctrl ")
556 if event.alt:
557 sc_keys.append("Alt ")
558 if event.shift:
559 sc_keys.append("Shift ")
561 sc_amount = ""
563 if self.key:
564 #print("Is a key")
565 if event.type not in ignore_keys and event.type in self.key[0]:
566 mods = "+ ".join(sc_keys)
567 old_mods = "+ ".join(self.key[0].split("+ ")[:-1])
568 if mods == old_mods:
569 amount = self.key[0].split(" x")
570 if len(amount) >= 2:
571 sc_amount = " x" + str(int(amount[-1]) + 1)
572 else:
573 sc_amount = " x2"
574 del self.key[0]
575 del self.time[0]
577 if event.type not in ignore_keys:
578 #print("Recorded as key")
579 sc_keys.append(event.type)
580 self.key.insert(0, "+ ".join(sc_keys) + sc_amount)
581 self.time.insert(0, time.time())
583 elif event.type in mouse_keys and \
584 scene.screencast_keys_mouse == 'icon':
585 #print("Recorded as mouse press")
586 self.mouse.insert(0, event.type)
587 self.mouse_time.insert(0, time.time())
589 if event.type in mouse_keys:
590 self.last_activity = 'MOUSE'
591 else:
592 self.last_activity = 'KEYBOARD'
593 #print("Last activity set to:", self.last_activity)
595 if not context.window_manager.screencast_keys_keys:
596 # stop script
597 ScreencastKeysStatus.handle_remove(context)
598 return {'CANCELLED'}
600 return {'PASS_THROUGH'}
602 def cancel(self, context):
603 if context.window_manager.screencast_keys_keys:
604 ScreencastKeysStatus.handle_remove(context)
605 context.window_manager.screencast_keys_keys = False
607 def invoke(self, context, event):
608 if context.area.type == 'VIEW_3D':
609 if context.window_manager.screencast_keys_keys is False:
610 # operator is called for the first time, start everything
611 context.window_manager.screencast_keys_keys = True
612 self.key = []
613 self.time = []
614 self.mouse = []
615 self.mouse_time = []
616 ScreencastKeysStatus.overall_time = []
617 ScreencastKeysStatus.handle_add(self, context)
618 ScreencastKeysStatus.overall_time.insert(0, time.time())
619 context.window_manager.modal_handler_add(self)
620 return {'RUNNING_MODAL'}
621 else:
622 # operator is called again, stop displaying
623 context.window_manager.screencast_keys_keys = False
624 self.key = []
625 self.time = []
626 self.mouse = []
627 self.mouse_time = []
628 ScreencastKeysStatus.overall_time = []
629 return {'CANCELLED'}
630 else:
631 self.report({'WARNING'}, "3D View not found, can't run Screencast Keys")
632 return {'CANCELLED'}
634 class ScreencastKeysTimerReset(bpy.types.Operator):
635 """Reset Timer"""
636 bl_idname = "view3d.screencast_keys_timer_reset"
637 bl_label = "Reset Timer"
638 bl_description = "Set the timer back to zero"
640 def execute(self, context):
641 ScreencastKeysStatus.overall_time = [time.time()]
642 return {'FINISHED'}
645 # properties used by the script
646 def init_properties():
647 scene = bpy.types.Scene
648 wm = bpy.types.WindowManager
650 scene.screencast_keys_pos_x = bpy.props.IntProperty(
651 name="Position X",
652 description="Margin on the X axis",
653 default=3,
654 min=0,
655 max=100)
656 scene.screencast_keys_pos_y = bpy.props.IntProperty(
657 name="Position Y",
658 description="Margin on the Y axis",
659 default=10,
660 min=0,
661 max=100)
662 scene.screencast_keys_font_size = bpy.props.IntProperty(
663 name="Text Size",
664 description="Text size displayed on 3D View",
665 default=24, min=10, max=150)
666 scene.screencast_keys_mouse_size = bpy.props.IntProperty(
667 name="Mouse Size",
668 description="Mouse size displayed on 3D View",
669 default=33, min=10, max=150)
670 scene.screencast_keys_text_color = bpy.props.FloatVectorProperty(
671 name="Text / Icon Color",
672 description="Color for the text and mouse icon",
673 default=(1.0, 1.0, 1.0, 1.0),
674 min=0.1,
675 max=1,
676 subtype='COLOR',
677 size=4)
678 scene.screencast_keys_box_color = bpy.props.FloatVectorProperty(
679 name="Box Color",
680 description="Box color",
681 default=(0.0, 0.0, 0.0, 0.3),
682 min=0,
683 max=1,
684 subtype='COLOR',
685 size=4)
686 scene.screencast_keys_box_width = bpy.props.IntProperty(
687 name="Box Width",
688 description="Box default width (resizes with text if needed)",
689 default = 0,
690 min = 0,
691 max = 2048,
692 soft_max = 1024)
693 scene.screencast_keys_mouse = bpy.props.EnumProperty(
694 items=(("none", "No Mouse", "Don't display mouse events"),
695 ("icon", "Icon", "Display graphical representation of "\
696 "the mouse"),
697 ("text", "Text", "Display mouse events as text lines")),
698 name="Mouse Display",
699 description="Display mouse events",
700 default='icon')
701 scene.screencast_keys_mouse_position = bpy.props.EnumProperty(
702 items=(("left", "Left", "Align to the left"),
703 ("right", "Right", "Align to the right")),
704 name="Icon Position",
705 description="Align the mouse icon on the 3D View",
706 default='left')
707 scene.screencast_keys_box_draw = bpy.props.BoolProperty(
708 name="Display Box",
709 description = "Display a bounding box behind the text",
710 default = True)
711 scene.screencast_keys_box_hide = bpy.props.BoolProperty(
712 name="Hide Box",
713 description = "Hide the box when no key is pressed",
714 default = False)
715 scene.screencast_keys_fade_time = bpy.props.FloatProperty(
716 name="Fade Out Time",
717 description = "Time in seconds for keys to last on screen",
718 default = 3.5,
719 min = 0.5,
720 max = 10.0,
721 soft_max = 5.0,
722 step = 10,
723 subtype = 'TIME')
724 scene.screencast_keys_show_operator = bpy.props.BoolProperty(
725 name="Display Last Operator",
726 description = "Display the last operator used",
727 default = True)
728 scene.screencast_keys_timer_show = bpy.props.BoolProperty(
729 name="Display Timer",
730 description = "Counter of the elapsed time in H:MM:SS since the script started",
731 default = False)
732 scene.screencast_keys_timer_size = bpy.props.IntProperty(
733 name="Time Size",
734 description="Time size displayed on 3D View",
735 default=12, min=8, max=100)
736 scene.screencast_keys_timer_color = bpy.props.FloatVectorProperty(
737 name="Time Color",
738 description="Color for the time display",
739 default=(1.0, 1.0, 1.0, 0.3),
740 min=0,
741 max=1,
742 subtype='COLOR',
743 size=4)
745 # Runstate initially always set to False
746 # note: it is not stored in the Scene, but in window manager:
747 wm.screencast_keys_keys = bpy.props.BoolProperty(default=False)
750 # removal of properties when script is disabled
751 def clear_properties():
752 props = (
753 "screencast_keys_keys",
754 "screencast_keys_mouse",
755 "screencast_keys_font_size",
756 "screencast_keys_mouse_size",
757 "screencast_keys_mouse_position",
758 "screencast_keys_fade_time",
759 "screencast_keys_pos_x",
760 "screencast_keys_pos_y",
761 "screencast_keys_box_draw",
762 "screencast_keys_text_color",
763 "screencast_keys_box_color",
764 "screencast_keys_box_hide",
765 "screencast_keys_box_width",
766 "screencast_keys_show_operator",
767 "screencast_keys_timer_show",
768 "screencast_keys_timer_color",
769 "screencast_keys_timer_size",
772 wm = bpy.context.window_manager
773 for p in props:
774 if p in wm:
775 del wm[p]
778 # defining the panel
779 class OBJECT_PT_keys_status(bpy.types.Panel):
780 bl_label = "Screencast Keys"
781 bl_space_type = "VIEW_3D"
782 bl_region_type = "UI"
784 def draw(self, context):
785 sc = context.scene
786 wm = context.window_manager
787 layout = self.layout
789 if not wm.screencast_keys_keys:
790 layout.operator("view3d.screencast_keys", text="Start Display",
791 icon = "PLAY")
792 else:
793 layout.operator("view3d.screencast_keys", text="Stop Display",
794 icon = "PAUSE")
796 split = layout.split()
798 col = split.column()
799 sub = col.column(align=True)
800 sub.label(text="Size:")
801 sub.prop(sc, "screencast_keys_font_size", text="Text")
802 sub.prop(sc, "screencast_keys_mouse_size", text="Mouse")
804 col = split.column()
805 sub = col.column(align=True)
806 sub.label(text="Position:")
807 sub.prop(sc, "screencast_keys_pos_x", text="X")
808 sub.prop(sc, "screencast_keys_pos_y", text="Y")
810 row = layout.row(align=True)
811 row.prop(sc, "screencast_keys_text_color")
812 row = layout.row(align=True)
813 row.prop(sc, "screencast_keys_fade_time")
815 layout.separator()
817 row = layout.row(align=True)
818 row.prop(sc, "screencast_keys_mouse", text="Mouse")
819 row = layout.row(align=True)
820 row.enabled = sc.screencast_keys_mouse == 'icon'
821 row.prop(sc, "screencast_keys_mouse_position", expand=True)
823 layout.label(text="Display:")
824 row = layout.row(align=True)
825 row.prop(sc, "screencast_keys_box_draw", text="Box")
826 row = layout.row(align=True)
827 row.active = sc.screencast_keys_box_draw
828 row.prop(sc, "screencast_keys_box_color", text="")
829 row.prop(sc, "screencast_keys_box_hide", text="Hide")
830 row = layout.row(align=True)
831 row.active = sc.screencast_keys_box_draw
832 row.prop(sc, "screencast_keys_box_width")
833 row = layout.row(align=True)
834 row.prop(sc, "screencast_keys_show_operator", text="Last Operator")
836 split = layout.split()
838 col = split.column()
839 sub = col.column(align=True)
840 sub.prop(sc, "screencast_keys_timer_show", text="Time")
841 col = split.column()
842 sub = col.column(align=True)
843 sub.active = sc.screencast_keys_timer_show
844 sub.prop(sc, "screencast_keys_timer_color", text="")
846 row = layout.row(align=True)
847 row.enabled = sc.screencast_keys_timer_show
848 row.prop(sc, "screencast_keys_timer_size")
849 row = layout.row(align=True)
850 row.enabled = sc.screencast_keys_timer_show
851 row.operator("view3d.screencast_keys_timer_reset", text="Reset")
853 classes = (ScreencastKeysStatus,
854 ScreencastKeysTimerReset,
855 OBJECT_PT_keys_status)
858 # store keymaps here to access after registration
859 addon_keymaps = []
862 def register():
863 init_properties()
864 for c in classes:
865 bpy.utils.register_class(c)
867 wm = bpy.context.window_manager
868 kc = wm.keyconfigs.addon
869 if kc:
870 km = kc.keymaps.new(name='3D View', space_type='VIEW_3D')
871 kmi = km.keymap_items.new('view3d.screencast_keys', 'C', 'PRESS', shift=True, alt=True)
872 addon_keymaps.append((km, kmi))
875 def unregister():
876 # incase its enabled
877 ScreencastKeysStatus.handle_remove(bpy.context)
879 for c in classes:
880 bpy.utils.unregister_class(c)
882 # handle the keymap
883 for km, kmi in addon_keymaps:
884 km.keymap_items.remove(kmi)
885 addon_keymaps.clear()
887 clear_properties()
890 if __name__ == "__main__":
891 register()