Removing screencast keys addons
To properly support this functionality we would need to extend our API's and there are better, dedicated tools to do this on each platform.
This commit is contained in:
parent
a4666b70d1
commit
d406b40b02
Notes:
blender-bot
2023-02-14 20:06:26 +01:00
Referenced by issue #40618, Removal of Screencast Keys Addon
|
@ -1,898 +0,0 @@
|
|||
# ##### 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 #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
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",
|
||||
"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 = 0
|
||||
text_height = 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_user = sc.screencast_keys_box_width
|
||||
box_width = 0
|
||||
box_height = 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)
|
||||
if sc.screencast_keys_mouse_position == 'left':
|
||||
box_width += mouse_size * MOUSE_RATIO * bool(row_count)
|
||||
x1 = max(box_width, box_width_user) + pos_x + mouse_size * MOUSE_RATIO + padding_x
|
||||
else:
|
||||
x1 = max(box_width, box_width_user) + pos_x + 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_box(self, context)
|
||||
draw_callback_px_text(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.284, 0.002, 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.284, 0.002, 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()
|
Loading…
Reference in New Issue