# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### # bl_info = { "name": "Screencast Keys", "author": "Paulo Gomes, Bart Crouch, John E. Herrenyo, Gaia Clary, Pablo Vazquez", "version": (1, 7), "blender": (2, 66, 0), "location": "3D View > Properties Panel > Screencast Keys", "warning": "", "description": "Display keys pressed in the 3D View, " "useful for screencasts.", "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/" "Py/Scripts/3D_interaction/Screencast_Key_Status_Tool", "tracker_url": "http://projects.blender.org/tracker/index.php?" "func=detail&aid=21612", "category": "3D View"} import bgl import blf import bpy import time import datetime MOUSE_RATIO = 0.535 def getDisplayLocation(context): scene = context.scene mouse_size = scene.screencast_keys_mouse_size pos_x = int( (context.region.width - mouse_size * MOUSE_RATIO) * \ scene.screencast_keys_pos_x / 100) pos_y = int( (context.region.height - mouse_size) * scene.screencast_keys_pos_y / 100) return(pos_x, pos_y) def getBoundingBox(current_width, current_height, new_text): w,h = blf.dimensions(0,new_text) if w > current_width: current_width = w current_height += h return(current_width, current_height) def draw_callback_px_text(self, context): wm = context.window_manager sc = context.scene if not wm.screencast_keys_keys: return font_size = sc.screencast_keys_font_size mouse_size = sc.screencast_keys_mouse_size box_draw = sc.screencast_keys_box_draw pos_x, pos_y = getDisplayLocation(context) label_time_max = sc.screencast_keys_fade_time # draw text in the 3D View blf.size(0, sc.screencast_keys_font_size, 72) blf.enable(0, blf.SHADOW) blf.shadow_offset(0, 1, -1) blf.shadow(0, 5, 0.0, 0.0, 0.0, 0.8) font_color_r, font_color_g, font_color_b, font_color_alpha = sc.screencast_keys_text_color final = 0 row_count = len(self.key) keypos_x = pos_x if sc.screencast_keys_mouse_position == 'left': keypos_x += mouse_size * MOUSE_RATIO * 1.7 if sc.screencast_keys_mouse != 'icon': keypos_x -= mouse_size * MOUSE_RATIO if sc.screencast_keys_mouse_position == 'right' and sc.screencast_keys_mouse != 'icon': keypos_x = pos_x shift = 0 # we want to make sure we can shift vertically the text if the mouse is big, # but don't care if aligned to right if mouse_size > font_size*row_count and not sc.screencast_keys_mouse_position == 'right': shift = (mouse_size - font_size*row_count) / 2 text_width, text_height = 0,0 row_count = 0 alpha = 1.0 for i in range(len(self.key)): label_time = time.time() - self.time[i] if label_time < label_time_max: # only display key-presses of last 2 seconds if label_time > (label_time_max / 1.2): blf.blur(0, 1) if label_time > (label_time_max / 1.1): blf.blur(0, 3) keypos_y = pos_y + shift + font_size*(i+0.1) blf.position(0, keypos_x, keypos_y , 0) alpha = min(1.0, max(0.0, label_time_max * (label_time_max - label_time))) bgl.glColor4f(font_color_r, font_color_g, font_color_b, font_color_alpha * alpha) blf.draw(0, self.key[i]) text_width, text_height = getBoundingBox(text_width, text_height, self.key[i]) row_count += 1 final = i + 1 else: break # remove blurriness # disable shadows so they don't appear all over blender blf.blur(0,0) blf.disable(0, blf.SHADOW) # get rid of status texts that aren't displayed anymore self.key = self.key[:final] self.time = self.time[:final] # draw graphical representation of the mouse if sc.screencast_keys_mouse == 'icon': for shape in ["mouse", "left_button", "middle_button", "right_button"]: draw_mouse(context, shape, "outline", font_color_alpha * 0.4) final = 0 for i in range(len(self.mouse)): click_time = time.time() - self.mouse_time[i] if click_time < 2: shape = map_mouse_event(self.mouse[i]) if shape: alpha = min(1.0, max(0.0, 2 * (2 - click_time))) draw_mouse(context, shape, "filled", alpha) final = i + 1 else: break # get rid of mouse clicks that aren't displayed anymore self.mouse = self.mouse[:final] self.mouse_time = self.mouse_time[:final] def draw_callback_px_box(self, context): wm = context.window_manager sc = context.scene if not wm.screencast_keys_keys: return font_size = sc.screencast_keys_font_size mouse_size = sc.screencast_keys_mouse_size if sc.screencast_keys_mouse_position == 'right': mouse_size = 25 box_draw = sc.screencast_keys_box_draw pos_x, pos_y = getDisplayLocation(context) # get text-width/height to resize the box blf.size(0, sc.screencast_keys_font_size, 72) box_width, box_height = sc.screencast_keys_box_width,0 final = 0 row_count = 0 box_hide = sc.screencast_keys_box_hide label_time_max = sc.screencast_keys_fade_time for i in range(len(self.key)): label_time = time.time() - self.time[i] if label_time < label_time_max: # only display key-presses of last 4 seconds box_width, box_height = getBoundingBox(box_width, box_height, self.key[i]) row_count += 1 final = i + 1 box_hide = False else: break # Got the size right, now draw box using proper colors box_color_r, box_color_g, box_color_b, box_color_alpha = sc.screencast_keys_box_color if box_draw and not box_hide: padding_x = 16 padding_y = 12 x0 = max(0, pos_x - padding_x) y0 = max(0, pos_y - padding_y) x1 = pos_x + box_width + mouse_size * MOUSE_RATIO * 1.3 + padding_x y1 = pos_y + max(mouse_size, font_size * row_count) + padding_y positions = [[x0, y0], [x0, y1], [x1, y1], [x1, y0]] settings = [[bgl.GL_QUADS, min(0.0, box_color_alpha)], [bgl.GL_LINE_LOOP, min(0.0, box_color_alpha)]] for mode, box_alpha in settings: bgl.glEnable(bgl.GL_BLEND) bgl.glBegin(mode) bgl.glColor4f(box_color_r, box_color_g, box_color_b, box_color_alpha) for v1, v2 in positions: bgl.glVertex2f(v1, v2) bgl.glEnd() if sc.screencast_keys_show_operator: draw_last_operator(context, pos_x, pos_y) if sc.screencast_keys_timer_show: draw_timer(context, pos_x, pos_y) # get rid of status texts that aren't displayed anymore self.key = self.key[:final] self.time = self.time[:final] def draw_callback_px(self, context): draw_callback_px_text(self, context) draw_callback_px_box(self, context) def draw_last_operator(context, pos_x, pos_y): wm = context.window_manager sc = context.scene font_color_r, font_color_g, font_color_b, font_color_alpha = sc.screencast_keys_text_color pos_x, pos_y = getDisplayLocation(context) if wm.operators: last_operator = wm.operators[-1].bl_label blf.enable(0, blf.SHADOW) blf.shadow_offset(0, 1, -1) blf.shadow(0, 5, 0.0, 0.0, 0.0, 0.8) blf.size(0, sc.screencast_keys_font_size, 36) blf.position(0, pos_x - 14, pos_y - 30, 0) bgl.glColor4f(font_color_r, font_color_g, font_color_b, font_color_alpha * 0.8) blf.draw(0, "Last: %s" % (last_operator)) blf.disable(0, blf.SHADOW) def draw_timer(context, pos_x, pos_y): sc = context.scene #calculate overall time overall_time = datetime.timedelta(seconds=int(time.time() - ScreencastKeysStatus.overall_time[0])) timer_color_r, timer_color_g, timer_color_b, timer_color_alpha = sc.screencast_keys_timer_color pos_x = context.region.width - (sc.screencast_keys_timer_size * 12) + 12 pos_y = 10 #draw time blf.size(0, sc.screencast_keys_timer_size, 72) blf.position(0, pos_x, pos_y, 0) bgl.glColor4f(timer_color_r, timer_color_g, timer_color_b, timer_color_alpha) blf.draw(0, "Elapsed Time: %s" % (overall_time)) def draw_mouse(context, shape, style, alpha): # shape and position sc = context.scene mouse_size = sc.screencast_keys_mouse_size font_size = sc.screencast_keys_font_size box_draw = sc.screencast_keys_box_draw pos_x, pos_y = getDisplayLocation(context) if sc.screencast_keys_mouse_position == 'left': offset_x = pos_x if sc.screencast_keys_mouse_position == 'right': offset_x = context.region.width - pos_x - (mouse_size * MOUSE_RATIO) offset_y = pos_y if font_size > mouse_size: offset_y += (font_size - mouse_size) / 2 shape_data = get_shape_data(shape) bgl.glTranslatef(offset_x, offset_y, 0) # color r, g, b, a = sc.screencast_keys_text_color bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(r, g, b, alpha) # inner shape for filled style if style == "filled": inner_shape = [] for i in shape_data: inner_shape.append(i[0]) # outer shape for i in shape_data: shape_segment = i shape_segment[0] = [mouse_size * k for k in shape_segment[0]] shape_segment[1] = [mouse_size * k for k in shape_segment[1]] shape_segment[2] = [mouse_size * k for k in shape_segment[2]] shape_segment[3] = [mouse_size * k for k in shape_segment[3]] # create the buffer shape_buffer = bgl.Buffer(bgl.GL_FLOAT, [4, 3], shape_segment) # create the map and draw the triangle fan bgl.glMap1f(bgl.GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, shape_buffer) bgl.glEnable(bgl.GL_MAP1_VERTEX_3) if style == "outline": bgl.glBegin(bgl.GL_LINE_STRIP) else: # style == "filled" bgl.glBegin(bgl.GL_TRIANGLE_FAN) for j in range(10): bgl.glEvalCoord1f(j / 10.0) x, y, z = shape_segment[3] # make sure the last vertex is indeed the last one, to avoid gaps bgl.glVertex3f(x, y, z) bgl.glEnd() bgl.glDisable(bgl.GL_MAP1_VERTEX_3) # draw interior if style == "filled": bgl.glBegin(bgl.GL_TRIANGLE_FAN) for i in inner_shape: j = [mouse_size * k for k in i] x, y, z = j bgl.glVertex3f(x, y, z) bgl.glEnd() bgl.glTranslatef(-offset_x, -offset_y, 0) # hardcoded data to draw the graphical represenation of the mouse def get_shape_data(shape): data = [] if shape == "mouse": data = [[[0.404, 0.032, 0.0], [0.096, 0.002, 0.0], [0.059, 0.126, 0.0], [0.04, 0.213, 0.0]], [[0.04, 0.213, 0.0], [-0.015, 0.465, 0.0], [-0.005, 0.564, 0.0], [0.032, 0.87, 0.0]], [[0.032, 0.87, 0.0], [0.05, 0.973, 0.0], [0.16, 1.002, 0.0], [0.264, 1.002, 0.0]], [[0.264, 1.002, 0.0], [0.369, 1.002, 0.0], [0.478, 0.973, 0.0], [0.497, 0.87, 0.0]], [[0.497, 0.87, 0.0], [0.533, 0.564, 0.0], [0.554, 0.465, 0.0], [0.499, 0.213, 0.0]], [[0.499, 0.213, 0.0], [0.490, 0.126, 0.0], [0.432, 0.002, 0.0], [0.404, 0.032, 0.0]]] elif shape == "left_button": data = [[[0.154, 0.763, 0.0], [0.126, 0.755, 0.0], [0.12, 0.754, 0.0], [0.066, 0.751, 0.0]], [[0.066, 0.751, 0.0], [0.043, 0.75, 0.0], [0.039, 0.757, 0.0], [0.039, 0.767, 0.0]], [[0.039, 0.767, 0.0], [0.047, 0.908, 0.0], [0.078, 0.943, 0.0], [0.155, 0.97, 0.0]], [[0.155, 0.97, 0.0], [0.174, 0.977, 0.0], [0.187, 0.975, 0.0], [0.191, 0.972, 0.0]], [[0.191, 0.972, 0.0], [0.203, 0.958, 0.0], [0.205, 0.949, 0.0], [0.199, 0.852, 0.0]], [[0.199, 0.852, 0.0], [0.195, 0.77, 0.0], [0.18, 0.771, 0.0], [0.154, 0.763, 0.0]]] elif shape == "middle_button": data = [[[0.301, 0.8, 0.0], [0.298, 0.768, 0.0], [0.231, 0.768, 0.0], [0.228, 0.8, 0.0]], [[0.228, 0.8, 0.0], [0.226, 0.817, 0.0], [0.225, 0.833, 0.0], [0.224, 0.85, 0.0]], [[0.224, 0.85, 0.0], [0.222, 0.873, 0.0], [0.222, 0.877, 0.0], [0.224, 0.9, 0.0]], [[0.224, 0.9, 0.0], [0.225, 0.917, 0.0], [0.226, 0.933, 0.0], [0.228, 0.95, 0.0]], [[0.228, 0.95, 0.0], [0.231, 0.982, 0.0], [0.298, 0.982, 0.0], [0.301, 0.95, 0.0]], [[0.301, 0.95, 0.0], [0.302, 0.933, 0.0], [0.303, 0.917, 0.0], [0.305, 0.9, 0.0]], [[0.305, 0.9, 0.0], [0.307, 0.877, 0.0], [0.307, 0.873, 0.0], [0.305, 0.85, 0.0]], [[0.305, 0.85, 0.0], [0.303, 0.833, 0.0], [0.302, 0.817, 0.0], [0.301, 0.8, 0.0]]] elif shape == "middle_down_button": data = [[[0.301, 0.8, 0.0], [0.298, 0.768, 0.0], [0.231, 0.768, 0.0], [0.228, 0.8, 0.0]], [[0.228, 0.8, 0.0], [0.226, 0.817, 0.0], [0.225, 0.833, 0.0], [0.224, 0.85, 0.0]], [[0.224, 0.85, 0.0], [0.264, 0.873, 0.0], [0.284, 0.873, 0.0], [0.305, 0.85, 0.0]], [[0.305, 0.85, 0.0], [0.303, 0.833, 0.0], [0.302, 0.817, 0.0], [0.301, 0.8, 0.0]]] elif shape == "middle_up_button": data = [[[0.270, 0.873, 0.0], [0.264, 0.873, 0.0], [0.222, 0.877, 0.0], [0.224, 0.9, 0.0]], [[0.224, 0.9, 0.0], [0.225, 0.917, 0.0], [0.226, 0.933, 0.0], [0.228, 0.95, 0.0]], [[0.228, 0.95, 0.0], [0.231, 0.982, 0.0], [0.298, 0.982, 0.0], [0.301, 0.95, 0.0]], [[0.301, 0.95, 0.0], [0.302, 0.933, 0.0], [0.303, 0.917, 0.0], [0.305, 0.9, 0.0]], [[0.305, 0.9, 0.0], [0.307, 0.877, 0.0], [0.284, 0.873, 0.0], [0.270, 0.873, 0.0]]] elif shape == "right_button": data = [[[0.375, 0.763, 0.0], [0.402, 0.755, 0.0], [0.408, 0.754, 0.0], [0.462, 0.751, 0.0]], [[0.462, 0.751, 0.0], [0.486, 0.75, 0.0], [0.49, 0.757, 0.0], [0.489, 0.767, 0.0]], [[0.489, 0.767, 0.0], [0.481, 0.908, 0.0], [0.451, 0.943, 0.0], [0.374, 0.97, 0.0]], [[0.374, 0.97, 0.0], [0.354, 0.977, 0.0], [0.341, 0.975, 0.0], [0.338, 0.972, 0.0]], [[0.338, 0.972, 0.0], [0.325, 0.958, 0.0], [0.324, 0.949, 0.0], [0.329, 0.852, 0.0]], [[0.329, 0.852, 0.0], [0.334, 0.77, 0.0], [0.348, 0.771, 0.0], [0.375, 0.763, 0.0]]] return(data) # return the shape that belongs to the given event def map_mouse_event(event): shape = False if event == 'LEFTMOUSE': shape = "left_button" elif event == 'MIDDLEMOUSE': shape = "middle_button" elif event == 'RIGHTMOUSE': shape = "right_button" elif event == 'WHEELDOWNMOUSE': shape = "middle_down_button" elif event == 'WHEELUPMOUSE': shape = "middle_up_button" return(shape) class ScreencastKeysStatus(bpy.types.Operator): bl_idname = "view3d.screencast_keys" bl_label = "Screencast Keys" bl_description = "Display keys pressed in the 3D View" last_activity = 'NONE' _handle = None _timer = None @staticmethod def handle_add(self, context): ScreencastKeysStatus._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL') ScreencastKeysStatus._timer = context.window_manager.event_timer_add(0.075, context.window) @staticmethod def handle_remove(context): if ScreencastKeysStatus._handle is not None: context.window_manager.event_timer_remove(ScreencastKeysStatus._timer) bpy.types.SpaceView3D.draw_handler_remove(ScreencastKeysStatus._handle, 'WINDOW') ScreencastKeysStatus._handle = None ScreencastKeysStatus._timer = None def modal(self, context, event): if context.area: context.area.tag_redraw() if event.type == 'TIMER': # no input, so no need to change the display return {'PASS_THROUGH'} scene = context.scene # keys that shouldn't show up in the 3D View mouse_keys = ['MOUSEMOVE','MIDDLEMOUSE','LEFTMOUSE', 'RIGHTMOUSE', 'WHEELDOWNMOUSE','WHEELUPMOUSE'] ignore_keys = ['LEFT_SHIFT', 'RIGHT_SHIFT', 'LEFT_ALT', 'RIGHT_ALT', 'LEFT_CTRL', 'RIGHT_CTRL', 'TIMER', 'MOUSEMOVE', 'INBETWEEN_MOUSEMOVE'] if scene.screencast_keys_mouse != 'text': ignore_keys.extend(mouse_keys) if event.value == 'PRESS' or (event.value == 'RELEASE' and \ self.last_activity == 'KEYBOARD' and event.type in mouse_keys): # add key-press to display-list sc_keys = [] if event.ctrl: sc_keys.append("Ctrl ") if event.alt: sc_keys.append("Alt ") if event.shift: sc_keys.append("Shift ") sc_amount = "" if self.key: #print("Is a key") if event.type not in ignore_keys and event.type in self.key[0]: mods = "+ ".join(sc_keys) old_mods = "+ ".join(self.key[0].split("+ ")[:-1]) if mods == old_mods: amount = self.key[0].split(" x") if len(amount) >= 2: sc_amount = " x" + str(int(amount[-1]) + 1) else: sc_amount = " x2" del self.key[0] del self.time[0] if event.type not in ignore_keys: #print("Recorded as key") sc_keys.append(event.type) self.key.insert(0, "+ ".join(sc_keys) + sc_amount) self.time.insert(0, time.time()) elif event.type in mouse_keys and \ scene.screencast_keys_mouse == 'icon': #print("Recorded as mouse press") self.mouse.insert(0, event.type) self.mouse_time.insert(0, time.time()) if event.type in mouse_keys: self.last_activity = 'MOUSE' else: self.last_activity = 'KEYBOARD' #print("Last activity set to:", self.last_activity) if not context.window_manager.screencast_keys_keys: # stop script ScreencastKeysStatus.handle_remove(context) return {'CANCELLED'} return {'PASS_THROUGH'} def cancel(self, context): if context.window_manager.screencast_keys_keys: ScreencastKeysStatus.handle_remove(context) context.window_manager.screencast_keys_keys = False def invoke(self, context, event): if context.area.type == 'VIEW_3D': if context.window_manager.screencast_keys_keys is False: # operator is called for the first time, start everything context.window_manager.screencast_keys_keys = True self.key = [] self.time = [] self.mouse = [] self.mouse_time = [] ScreencastKeysStatus.overall_time = [] ScreencastKeysStatus.handle_add(self, context) ScreencastKeysStatus.overall_time.insert(0, time.time()) context.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} else: # operator is called again, stop displaying context.window_manager.screencast_keys_keys = False self.key = [] self.time = [] self.mouse = [] self.mouse_time = [] ScreencastKeysStatus.overall_time = [] return {'CANCELLED'} else: self.report({'WARNING'}, "3D View not found, can't run Screencast Keys") return {'CANCELLED'} class ScreencastKeysTimerReset(bpy.types.Operator): """Reset Timer""" bl_idname = "view3d.screencast_keys_timer_reset" bl_label = "Reset Timer" bl_description = "Set the timer back to zero" def execute(self, context): ScreencastKeysStatus.overall_time = [time.time()] return {'FINISHED'} # properties used by the script def init_properties(): scene = bpy.types.Scene wm = bpy.types.WindowManager scene.screencast_keys_pos_x = bpy.props.IntProperty( name="Position X", description="Margin on the X axis", default=3, min=0, max=100) scene.screencast_keys_pos_y = bpy.props.IntProperty( name="Position Y", description="Margin on the Y axis", default=10, min=0, max=100) scene.screencast_keys_font_size = bpy.props.IntProperty( name="Text Size", description="Text size displayed on 3D View", default=24, min=10, max=150) scene.screencast_keys_mouse_size = bpy.props.IntProperty( name="Mouse Size", description="Mouse size displayed on 3D View", default=33, min=10, max=150) scene.screencast_keys_text_color = bpy.props.FloatVectorProperty( name="Text / Icon Color", description="Color for the text and mouse icon", default=(1.0, 1.0, 1.0, 1.0), min=0.1, max=1, subtype='COLOR', size=4) scene.screencast_keys_box_color = bpy.props.FloatVectorProperty( name="Box Color", description="Box color", default=(0.0, 0.0, 0.0, 0.3), min=0, max=1, subtype='COLOR', size=4) scene.screencast_keys_box_width = bpy.props.IntProperty( name="Box Width", description="Box default width (resizes with text if needed)", default = 0, min = 0, max = 2048, soft_max = 1024) scene.screencast_keys_mouse = bpy.props.EnumProperty( items=(("none", "No Mouse", "Don't display mouse events"), ("icon", "Icon", "Display graphical representation of "\ "the mouse"), ("text", "Text", "Display mouse events as text lines")), name="Mouse Display", description="Display mouse events", default='icon') scene.screencast_keys_mouse_position = bpy.props.EnumProperty( items=(("left", "Left", "Align to the left"), ("right", "Right", "Align to the right")), name="Icon Position", description="Align the mouse icon on the 3D View", default='left') scene.screencast_keys_box_draw = bpy.props.BoolProperty( name="Display Box", description = "Display a bounding box behind the text", default = True) scene.screencast_keys_box_hide = bpy.props.BoolProperty( name="Hide Box", description = "Hide the box when no key is pressed", default = False) scene.screencast_keys_fade_time = bpy.props.FloatProperty( name="Fade Out Time", description = "Time in seconds for keys to last on screen", default = 3.5, min = 0.5, max = 10.0, soft_max = 5.0, step = 10, subtype = 'TIME') scene.screencast_keys_show_operator = bpy.props.BoolProperty( name="Display Last Operator", description = "Display the last operator used", default = True) scene.screencast_keys_timer_show = bpy.props.BoolProperty( name="Display Timer", description = "Counter of the elapsed time in H:MM:SS since the script started", default = False) scene.screencast_keys_timer_size = bpy.props.IntProperty( name="Time Size", description="Time size displayed on 3D View", default=12, min=8, max=100) scene.screencast_keys_timer_color = bpy.props.FloatVectorProperty( name="Time Color", description="Color for the time display", default=(1.0, 1.0, 1.0, 0.3), min=0, max=1, subtype='COLOR', size=4) # Runstate initially always set to False # note: it is not stored in the Scene, but in window manager: wm.screencast_keys_keys = bpy.props.BoolProperty(default=False) # removal of properties when script is disabled def clear_properties(): props = ( "screencast_keys_keys", "screencast_keys_mouse", "screencast_keys_font_size", "screencast_keys_mouse_size", "screencast_keys_mouse_position", "screencast_keys_fade_time", "screencast_keys_pos_x", "screencast_keys_pos_y", "screencast_keys_box_draw", "screencast_keys_text_color", "screencast_keys_box_color", "screencast_keys_box_hide", "screencast_keys_box_width", "screencast_keys_show_operator", "screencast_keys_timer_show", "screencast_keys_timer_color", "screencast_keys_timer_size", ) wm = bpy.context.window_manager for p in props: if p in wm: del wm[p] # defining the panel class OBJECT_PT_keys_status(bpy.types.Panel): bl_label = "Screencast Keys" bl_space_type = "VIEW_3D" bl_region_type = "UI" def draw(self, context): sc = context.scene wm = context.window_manager layout = self.layout if not wm.screencast_keys_keys: layout.operator("view3d.screencast_keys", text="Start Display", icon = "PLAY") else: layout.operator("view3d.screencast_keys", text="Stop Display", icon = "PAUSE") split = layout.split() col = split.column() sub = col.column(align=True) sub.label(text="Size:") sub.prop(sc, "screencast_keys_font_size", text="Text") sub.prop(sc, "screencast_keys_mouse_size", text="Mouse") col = split.column() sub = col.column(align=True) sub.label(text="Position:") sub.prop(sc, "screencast_keys_pos_x", text="X") sub.prop(sc, "screencast_keys_pos_y", text="Y") row = layout.row(align=True) row.prop(sc, "screencast_keys_text_color") row = layout.row(align=True) row.prop(sc, "screencast_keys_fade_time") layout.separator() row = layout.row(align=True) row.prop(sc, "screencast_keys_mouse", text="Mouse") row = layout.row(align=True) row.enabled = sc.screencast_keys_mouse == 'icon' row.prop(sc, "screencast_keys_mouse_position", expand=True) layout.label(text="Display:") row = layout.row(align=True) row.prop(sc, "screencast_keys_box_draw", text="Box") row = layout.row(align=True) row.active = sc.screencast_keys_box_draw row.prop(sc, "screencast_keys_box_color", text="") row.prop(sc, "screencast_keys_box_hide", text="Hide") row = layout.row(align=True) row.active = sc.screencast_keys_box_draw row.prop(sc, "screencast_keys_box_width") row = layout.row(align=True) row.prop(sc, "screencast_keys_show_operator", text="Last Operator") split = layout.split() col = split.column() sub = col.column(align=True) sub.prop(sc, "screencast_keys_timer_show", text="Time") col = split.column() sub = col.column(align=True) sub.active = sc.screencast_keys_timer_show sub.prop(sc, "screencast_keys_timer_color", text="") row = layout.row(align=True) row.enabled = sc.screencast_keys_timer_show row.prop(sc, "screencast_keys_timer_size") row = layout.row(align=True) row.enabled = sc.screencast_keys_timer_show row.operator("view3d.screencast_keys_timer_reset", text="Reset") classes = (ScreencastKeysStatus, ScreencastKeysTimerReset, OBJECT_PT_keys_status) # store keymaps here to access after registration addon_keymaps = [] def register(): init_properties() for c in classes: bpy.utils.register_class(c) wm = bpy.context.window_manager kc = wm.keyconfigs.addon if kc: km = kc.keymaps.new(name='3D View', space_type='VIEW_3D') kmi = km.keymap_items.new('view3d.screencast_keys', 'C', 'PRESS', shift=True, alt=True) addon_keymaps.append((km, kmi)) def unregister(): # incase its enabled ScreencastKeysStatus.handle_remove(bpy.context) for c in classes: bpy.utils.unregister_class(c) # handle the keymap for km, kmi in addon_keymaps: km.keymap_items.remove(kmi) addon_keymaps.clear() clear_properties() if __name__ == "__main__": register()