Skip to content

Commit 1fa0153

Browse files
committed
Merge pull request #4029 from blink1073/web-key-modifiers
FIX : key modifier handling in Web backends
1 parent 7468be2 commit 1fa0153

File tree

4 files changed

+143
-14
lines changed

4 files changed

+143
-14
lines changed

lib/matplotlib/backends/backend_webagg_core.py

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,96 @@ def new_figure_manager_given_figure(num, figure):
4747
return manager
4848

4949

50+
# http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes
51+
_SHIFT_LUT = {59: ':',
52+
61: '+',
53+
173: '_',
54+
186: ':',
55+
187: '+',
56+
188: '<',
57+
189: '_',
58+
190: '>',
59+
191: '?',
60+
192: '~',
61+
219: '{',
62+
220: '|',
63+
221: '}',
64+
222: '"'}
65+
66+
_LUT = {8: 'backspace',
67+
9: 'tab',
68+
13: 'enter',
69+
16: 'shift',
70+
17: 'control',
71+
18: 'alt',
72+
19: 'pause',
73+
20: 'caps',
74+
27: 'escape',
75+
32: ' ',
76+
33: 'pageup',
77+
34: 'pagedown',
78+
35: 'end',
79+
36: 'home',
80+
37: 'left',
81+
38: 'up',
82+
39: 'right',
83+
40: 'down',
84+
45: 'insert',
85+
46: 'delete',
86+
91: 'super',
87+
92: 'super',
88+
93: 'select',
89+
106: '*',
90+
107: '+',
91+
109: '-',
92+
110: '.',
93+
111: '/',
94+
144: 'num_lock',
95+
145: 'scroll_lock',
96+
186: ':',
97+
187: '=',
98+
188: ',',
99+
189: '-',
100+
190: '.',
101+
191: '/',
102+
192: '`',
103+
219: '[',
104+
220: '\\',
105+
221: ']',
106+
222: "'"}
107+
108+
109+
def _handle_key(key):
110+
"""Handle key codes"""
111+
code = int(key[key.index('k') + 1:])
112+
value = chr(code)
113+
# letter keys
114+
if code >= 65 and code <= 90:
115+
if 'shift+' in key:
116+
key = key.replace('shift+', '')
117+
else:
118+
value = value.lower()
119+
# number keys
120+
elif code >= 48 and code <= 57:
121+
if 'shift+' in key:
122+
value = ')!@#$%^&*('[int(value)]
123+
key = key.replace('shift+', '')
124+
# function keys
125+
elif code >= 112 and code <= 123:
126+
value = 'f%s' % (code - 111)
127+
# number pad keys
128+
elif code >= 96 and code <= 105:
129+
value = '%s' % (code - 96)
130+
# keys with shift alternatives
131+
elif code in _SHIFT_LUT and 'shift+' in key:
132+
key = key.replace('shift+', '')
133+
value = _SHIFT_LUT[code]
134+
elif code in _LUT:
135+
value = _LUT[code]
136+
key = key[:key.index('k')] + value
137+
return key
138+
139+
50140
class FigureCanvasWebAggCore(backend_agg.FigureCanvasAgg):
51141
supports_blit = False
52142

@@ -222,8 +312,7 @@ def handle_event(self, event):
222312
elif e_type == 'scroll':
223313
self.scroll_event(x, y, event['step'])
224314
elif e_type in ('key_press', 'key_release'):
225-
key = event['key']
226-
315+
key = _handle_key(event['key'])
227316
if e_type == 'key_press':
228317
self.key_press_event(key)
229318
elif e_type == 'key_release':

lib/matplotlib/backends/web_backend/mpl.js

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ mpl.figure.prototype._init_canvas = function() {
117117
}
118118

119119
canvas_div.keydown('key_press', canvas_keyboard_event);
120-
canvas_div.keydown('key_release', canvas_keyboard_event);
120+
canvas_div.keyup('key_release', canvas_keyboard_event);
121121
this.canvas_div = canvas_div
122122
this._canvas_extra_style(canvas_div)
123123
this.root.append(canvas_div);
@@ -473,21 +473,35 @@ mpl.figure.prototype.mouse_event = function(event, name) {
473473
return false;
474474
}
475475

476+
mpl.figure.prototype._key_event_extra = function(event, name) {
477+
// Handle any extra behaviour associated with a key event
478+
}
479+
476480
mpl.figure.prototype.key_event = function(event, name) {
477-
/* Don't fire events just when a modifier is changed. Modifiers are
478-
sent along with other keys. */
479-
if (event.keyCode >= 16 && event.keyCode <= 20) {
480-
return;
481+
482+
// Prevent repeat events
483+
if (name == 'key_press')
484+
{
485+
if (event.which === this._key)
486+
return;
487+
else
488+
this._key = event.which;
481489
}
490+
if (name == 'key_release')
491+
this._key = null;
482492

483493
var value = '';
484-
if (event.ctrlKey) {
494+
if (event.ctrlKey && event.which != 17)
485495
value += "ctrl+";
486-
}
487-
if (event.altKey) {
496+
if (event.altKey && event.which != 18)
488497
value += "alt+";
489-
}
490-
value += String.fromCharCode(event.keyCode).toLowerCase();
498+
if (event.shiftKey && event.which != 16)
499+
value += "shift+";
500+
501+
value += 'k';
502+
value += event.which.toString();
503+
504+
this._key_event_extra(event, name);
491505

492506
this.send_message(name, {key: value});
493507
return false;

lib/matplotlib/backends/web_backend/nbagg_mpl.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,22 @@ mpl.figure.prototype._canvas_extra_style = function(el){
149149

150150
}
151151

152+
mpl.figure.prototype._key_event_extra = function(event, name) {
153+
var manager = IPython.notebook.keyboard_manager;
154+
if (!manager)
155+
manager = IPython.keyboard_manager;
156+
157+
// Check for shift+enter
158+
if (event.shiftKey && event.which == 13) {
159+
this.canvas_div.blur();
160+
event.shiftKey = false;
161+
// Send a "J" for go to next cell
162+
event.which = 74;
163+
event.keyCode = 74;
164+
manager.command_mode();
165+
manager.handle_keydown(event);
166+
}
167+
}
152168

153169
mpl.figure.prototype.handle_save = function(fig, msg) {
154170
fig.ondownload(fig, null);

lib/matplotlib/backends/web_backend/nbagg_uat.ipynb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"metadata": {
33
"name": "",
4-
"signature": "sha256:7f7ec6a6e2a63837a45a88a501ba3c5b1eb88e744925456a9bfeb0d6faa896a5"
4+
"signature": "sha256:a1ac68aba163c75eab3d1fc91aa4d9a8ca66b09159619563827a19967d96814b"
55
},
66
"nbformat": 3,
77
"nbformat_minor": 0,
@@ -417,13 +417,23 @@
417417
"evt = []\n",
418418
"colors = iter(itertools.cycle(['r', 'g', 'b', 'k', 'c']))\n",
419419
"def on_event(event):\n",
420+
" if event.name.startswith('key'):\n",
421+
" fig.suptitle('%s: %s' % (event.name, event.key))\n",
422+
" elif event.name == 'scroll_event':\n",
423+
" fig.suptitle('%s: %s' % (event.name, event.step))\n",
424+
" else:\n",
425+
" fig.suptitle('%s: %s' % (event.name, event.button))\n",
420426
" evt.append(event)\n",
421427
" ln.set_color(next(colors))\n",
422428
" fig.canvas.draw()\n",
423429
" fig.canvas.draw_idle()\n",
430+
"\n",
424431
"fig.canvas.mpl_connect('button_press_event', on_event)\n",
425-
"fig.canvas.mpl_connect('key_press_event', on_event)\n",
432+
"fig.canvas.mpl_connect('button_release_event', on_event)\n",
426433
"fig.canvas.mpl_connect('scroll_event', on_event)\n",
434+
"fig.canvas.mpl_connect('key_press_event', on_event)\n",
435+
"fig.canvas.mpl_connect('key_release_event', on_event)\n",
436+
"\n",
427437
"plt.show()"
428438
],
429439
"language": "python",

0 commit comments

Comments
 (0)