From 70d19e544ff4770573721a134f7750960960cb64 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 22 Jan 2015 21:48:33 -0600 Subject: [PATCH 1/7] Fix key modifier handling Fix key modifier handling Remove commented out code Ignore pause/break and caps lock --- lib/matplotlib/backends/web_backend/mpl.js | 31 +++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/backends/web_backend/mpl.js b/lib/matplotlib/backends/web_backend/mpl.js index cebc8d12b76d..d52be66e6ea6 100644 --- a/lib/matplotlib/backends/web_backend/mpl.js +++ b/lib/matplotlib/backends/web_backend/mpl.js @@ -120,7 +120,7 @@ mpl.figure.prototype._init_canvas = function() { } canvas_div.keydown('key_press', canvas_keyboard_event); - canvas_div.keydown('key_release', canvas_keyboard_event); + canvas_div.keyup('key_release', canvas_keyboard_event); this.canvas_div = canvas_div this._canvas_extra_style(canvas_div) this.root.append(canvas_div); @@ -459,20 +459,33 @@ mpl.figure.prototype.mouse_event = function(event, name) { } mpl.figure.prototype.key_event = function(event, name) { - /* Don't fire events just when a modifier is changed. Modifiers are - sent along with other keys. */ - if (event.keyCode >= 16 && event.keyCode <= 20) { - return; - } + // Ignore pause/break and caps lock + if (event.keyCode === 19 || event.keyCode === 20) + return; var value = ''; - if (event.ctrlKey) { + if (event.ctrlKey && !(event.keyCode == 17)) { value += "ctrl+"; } - if (event.altKey) { + if (event.altKey && !(event.keyCode == 18)) { value += "alt+"; } - value += String.fromCharCode(event.keyCode).toLowerCase(); + if (event.shiftKey && !(event.keyCode == 16)) { + value += "shift+"; + } + + if (event.keyCode == 17) { + value += "control"; + } + else if (event.keyCode == 18) { + value += "alt"; + } + else if (event.keyCode == 16) { + value += "shift"; + } + else { + value += String.fromCharCode(event.keyCode).toLowerCase(); + } this.send_message(name, {key: value}); return false; From 248bfefc1c50b4c81bb66303ab843cd8b3aea3bc Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 23 Jan 2015 08:45:10 -0600 Subject: [PATCH 2/7] Prevent repeat key events Prevent repeat key events Add missing brace Use cross-platform event.which and handle shift casing Handle other special keys Add support for firefox Handle modified number keys (like #), pause, and capslock Cleaner handling of shift Style update Style update Handle all key events in chrome Better handling of shift modifier in firefox --- .../backends/backend_webagg_core.py | 53 +++++++++++++++++- lib/matplotlib/backends/web_backend/mpl.js | 54 +++++++++++++------ 2 files changed, 91 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index 32bb61258a3d..c66a87532834 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -18,6 +18,7 @@ import io import json import os +import re import time import warnings @@ -47,6 +48,55 @@ def new_figure_manager_given_figure(num, figure): return manager +_KEY_LUT = {'shift+À': '~', + 'À': '`', + '\x1b': 'esc', + '\x08': 'backspace', + 'shift+Ü': '|', + 'Ü': '\\', + 'shift+Ý': '}', + 'Ý': ']', + 'shift+Û': '{', + 'Û': '[', + 'shift+º': ':', + 'º': ';', + 'shift+Þ': '"', + 'Þ': "'", + 'shift+¼': '<', + '¼': ',', + 'shift+¾': '>', + '¾': '.', + 'shift+¿': '?', + '¿': '/', + 'shift+»': '+', + '»': '=', + 'shift+½': '_', + '½': '-', + '\x7f': 'del', + '\t': 'tab'} + + +def _get_key(key): + """Handle key codes with unicode characters""" + ind = key.index('u+') + char = chr(int(key[ind + 2:], 16)) + if re.match('\d', char): + if 'shift+' in key: + num = int(key[-1]) + key = key.replace('shift+', '') + char = ')!@#$%^&*('[num] + elif 'shift+' in key: + if 'shift+' + char in _KEY_LUT: + key = key.replace('shift+', '') + char = _KEY_LUT['shift+' + char] + else: + char = _KEY_LUT[char] + else: + char = _KEY_LUT.get(char, char) + key = key[:key.index('u+')] + char + return key + + class FigureCanvasWebAggCore(backend_agg.FigureCanvasAgg): supports_blit = False @@ -222,7 +272,8 @@ def handle_event(self, event): self.scroll_event(x, y, event['step']) elif e_type in ('key_press', 'key_release'): key = event['key'] - + if 'u+' in key: + key = _get_key(key) if e_type == 'key_press': self.key_press_event(key) elif e_type == 'key_release': diff --git a/lib/matplotlib/backends/web_backend/mpl.js b/lib/matplotlib/backends/web_backend/mpl.js index d52be66e6ea6..bf757eec6acb 100644 --- a/lib/matplotlib/backends/web_backend/mpl.js +++ b/lib/matplotlib/backends/web_backend/mpl.js @@ -460,31 +460,55 @@ mpl.figure.prototype.mouse_event = function(event, name) { mpl.figure.prototype.key_event = function(event, name) { - // Ignore pause/break and caps lock - if (event.keyCode === 19 || event.keyCode === 20) - return; + // Prevent repeat events + if (name == 'key_press') + { + if (event.which === this._key) + return; + else + this._key = event.which; + } + if (name == 'key_release') + this._key = null; + var value = ''; - if (event.ctrlKey && !(event.keyCode == 17)) { + if (event.ctrlKey && !(event.which == 17)) value += "ctrl+"; - } - if (event.altKey && !(event.keyCode == 18)) { + if (event.altKey && !(event.which == 18)) value += "alt+"; - } - if (event.shiftKey && !(event.keyCode == 16)) { - value += "shift+"; - } - if (event.keyCode == 17) { + if (event.which == 17) { value += "control"; + if (event.shiftKey) + value += "+shift" } - else if (event.keyCode == 18) { + else if (event.which == 18) { value += "alt"; + if (event.shiftKey) + value += "+shift" } - else if (event.keyCode == 16) { + else if (event.which == 16) value += "shift"; - } else { - value += String.fromCharCode(event.keyCode).toLowerCase(); + if (event.which >= 65 && event.which <= 90) { + if (event.shiftKey) + value += String.fromCharCode(event.which); + else + value += String.fromCharCode(event.which).toLowerCase(); + } + else if (event.key) { + if (event.shiftKey && (event.which <= 46 || + (event.key >= 91 && event.key <= 145))) + value += "shift+" + value += event.key.toLowerCase(); + } + else if (event.originalEvent.keyIdentifier) { + if (event.shiftKey) + value += "shift+" + value += event.originalEvent.keyIdentifier.toLowerCase(); + } + else + value += String.fromCharCode(event.which); } this.send_message(name, {key: value}); From 11a30108d2e7b80aa625e22fa285e5a69f9bfe97 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 24 Jan 2015 15:19:57 -0600 Subject: [PATCH 3/7] Update UAT to display the event type and data in title Update UAT to display the event type and data in title Add source encoding to file --- lib/matplotlib/backends/backend_webagg_core.py | 1 + .../backends/web_backend/nbagg_uat.ipynb | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index c66a87532834..499f1c239861 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Displays Agg images in the browser, with interactivity """ diff --git a/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb b/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb index 9e795319bc81..a8525ea28ed5 100644 --- a/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb +++ b/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb @@ -1,7 +1,7 @@ { "metadata": { "name": "", - "signature": "sha256:7f7ec6a6e2a63837a45a88a501ba3c5b1eb88e744925456a9bfeb0d6faa896a5" + "signature": "sha256:a1ac68aba163c75eab3d1fc91aa4d9a8ca66b09159619563827a19967d96814b" }, "nbformat": 3, "nbformat_minor": 0, @@ -417,13 +417,23 @@ "evt = []\n", "colors = iter(itertools.cycle(['r', 'g', 'b', 'k', 'c']))\n", "def on_event(event):\n", + " if event.name.startswith('key'):\n", + " fig.suptitle('%s: %s' % (event.name, event.key))\n", + " elif event.name == 'scroll_event':\n", + " fig.suptitle('%s: %s' % (event.name, event.step))\n", + " else:\n", + " fig.suptitle('%s: %s' % (event.name, event.button))\n", " evt.append(event)\n", " ln.set_color(next(colors))\n", " fig.canvas.draw()\n", " fig.canvas.draw_idle()\n", + "\n", "fig.canvas.mpl_connect('button_press_event', on_event)\n", - "fig.canvas.mpl_connect('key_press_event', on_event)\n", + "fig.canvas.mpl_connect('button_release_event', on_event)\n", "fig.canvas.mpl_connect('scroll_event', on_event)\n", + "fig.canvas.mpl_connect('key_press_event', on_event)\n", + "fig.canvas.mpl_connect('key_release_event', on_event)\n", + "\n", "plt.show()" ], "language": "python", From 5eec284a93a68d622acb3c03fba17340b0200d02 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 24 Jan 2015 19:56:19 -0600 Subject: [PATCH 4/7] Move key code handling logic to python Move key code handling logic to python Add support for space key Rename keys for consistency with Qt backend Add a link to the key codes Add comments --- .../backends/backend_webagg_core.py | 131 ++++++++++++------ lib/matplotlib/backends/web_backend/mpl.js | 41 +----- 2 files changed, 91 insertions(+), 81 deletions(-) diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index 499f1c239861..a6d360a09414 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -49,52 +49,93 @@ def new_figure_manager_given_figure(num, figure): return manager -_KEY_LUT = {'shift+À': '~', - 'À': '`', - '\x1b': 'esc', - '\x08': 'backspace', - 'shift+Ü': '|', - 'Ü': '\\', - 'shift+Ý': '}', - 'Ý': ']', - 'shift+Û': '{', - 'Û': '[', - 'shift+º': ':', - 'º': ';', - 'shift+Þ': '"', - 'Þ': "'", - 'shift+¼': '<', - '¼': ',', - 'shift+¾': '>', - '¾': '.', - 'shift+¿': '?', - '¿': '/', - 'shift+»': '+', - '»': '=', - 'shift+½': '_', - '½': '-', - '\x7f': 'del', - '\t': 'tab'} - - -def _get_key(key): - """Handle key codes with unicode characters""" - ind = key.index('u+') - char = chr(int(key[ind + 2:], 16)) - if re.match('\d', char): +# http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes +_SHIFT_LUT = {59: ':', + 61: '+', + 173: '_', + 186: ':', + 187: '+', + 188: '<', + 189: '_', + 190: '>', + 191: '?', + 192: '~', + 219: '{', + 220: '|', + 221: '}', + 222: '"'} + +_LUT = {8: 'backspace', + 9: 'tab', + 13: 'enter', + 16: 'shift', + 17: 'control', + 18: 'alt', + 19: 'pause', + 20: 'caps', + 27: 'escape', + 32: ' ', + 33: 'pageup', + 34: 'pagedown', + 35: 'end', + 36: 'home', + 37: 'left', + 38: 'up', + 39: 'right', + 40: 'down', + 45: 'insert', + 46: 'delete', + 91: 'super', + 92: 'super', + 93: 'select', + 106: '*', + 107: '+', + 109: '-', + 110: '.', + 111: '/', + 144: 'num_lock', + 145: 'scroll_lock', + 186: ':', + 187: '=', + 188: ',', + 189: '-', + 190: '.', + 191: '/', + 192: '`', + 219: '[', + 220: '\\', + 221: ']', + 222: "'"} + + +def _handle_key(key): + """Handle key codes""" + code = int(key[key.index('k') + 1:]) + value = chr(code) + # letter keys + if code >= 65 and code <= 90: if 'shift+' in key: - num = int(key[-1]) key = key.replace('shift+', '') - char = ')!@#$%^&*('[num] - elif 'shift+' in key: - if 'shift+' + char in _KEY_LUT: - key = key.replace('shift+', '') - char = _KEY_LUT['shift+' + char] else: - char = _KEY_LUT[char] - else: - char = _KEY_LUT.get(char, char) - key = key[:key.index('u+')] + char + value = value.lower() + # number keys + elif code >= 48 and code <= 57: + if 'shift+' in key: + value = ')!@#$%^&*('[int(value)] + key = key.replace('shift+', '') + # function keys + elif code >= 112 and code <= 123: + value = 'f%s' % (code - 111) + # number pad keys + elif code >= 96 and code <= 105: + value = '%s' % (code - 96) + # keys with shift alternatives + elif code in _SHIFT_LUT and 'shift+' in key: + key = key.replace('shift+', '') + value = _SHIFT_LUT[code] + elif code in _LUT: + value = _LUT[code] + key = key[:key.index('k')] + value return key @@ -272,9 +313,7 @@ def handle_event(self, event): elif e_type == 'scroll': self.scroll_event(x, y, event['step']) elif e_type in ('key_press', 'key_release'): - key = event['key'] - if 'u+' in key: - key = _get_key(key) + key = _handle_key(event['key']) if e_type == 'key_press': self.key_press_event(key) elif e_type == 'key_release': diff --git a/lib/matplotlib/backends/web_backend/mpl.js b/lib/matplotlib/backends/web_backend/mpl.js index bf757eec6acb..ee550aa7eba0 100644 --- a/lib/matplotlib/backends/web_backend/mpl.js +++ b/lib/matplotlib/backends/web_backend/mpl.js @@ -472,44 +472,15 @@ mpl.figure.prototype.key_event = function(event, name) { this._key = null; var value = ''; - if (event.ctrlKey && !(event.which == 17)) + if (event.ctrlKey && event.which != 17) value += "ctrl+"; - if (event.altKey && !(event.which == 18)) + if (event.altKey && event.which != 18) value += "alt+"; + if (event.shiftKey && event.which != 16) + value += "shift+"; - if (event.which == 17) { - value += "control"; - if (event.shiftKey) - value += "+shift" - } - else if (event.which == 18) { - value += "alt"; - if (event.shiftKey) - value += "+shift" - } - else if (event.which == 16) - value += "shift"; - else { - if (event.which >= 65 && event.which <= 90) { - if (event.shiftKey) - value += String.fromCharCode(event.which); - else - value += String.fromCharCode(event.which).toLowerCase(); - } - else if (event.key) { - if (event.shiftKey && (event.which <= 46 || - (event.key >= 91 && event.key <= 145))) - value += "shift+" - value += event.key.toLowerCase(); - } - else if (event.originalEvent.keyIdentifier) { - if (event.shiftKey) - value += "shift+" - value += event.originalEvent.keyIdentifier.toLowerCase(); - } - else - value += String.fromCharCode(event.which); - } + value += 'k'; + value += event.which.toString(); this.send_message(name, {key: value}); return false; From cef78309107bf88f91c15609f1e423f80d8cd011 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sun, 25 Jan 2015 16:51:44 -0600 Subject: [PATCH 5/7] Handle shift+enter behavior in nbagg Partial implementation of shift+enter in nbagg Use the J key command to move to next cell Remove errant change to uat Add a comment and handle IPython 2/3 Move the shift+enter handling to nbagg_mpl Add explicit mode switch for shift+enter Clean up handling of keyboard manager variable Remove extraneous changes Remove unused import Remove newline change --- lib/matplotlib/backends/backend_webagg_core.py | 2 -- lib/matplotlib/backends/web_backend/mpl.js | 6 ++++++ lib/matplotlib/backends/web_backend/nbagg_mpl.js | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index a6d360a09414..531fce3ada7c 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Displays Agg images in the browser, with interactivity """ @@ -19,7 +18,6 @@ import io import json import os -import re import time import warnings diff --git a/lib/matplotlib/backends/web_backend/mpl.js b/lib/matplotlib/backends/web_backend/mpl.js index ee550aa7eba0..e8711d888f83 100644 --- a/lib/matplotlib/backends/web_backend/mpl.js +++ b/lib/matplotlib/backends/web_backend/mpl.js @@ -458,6 +458,10 @@ mpl.figure.prototype.mouse_event = function(event, name) { return false; } +mpl.figure.prototype._key_event_extra = function(event, name) { + +} + mpl.figure.prototype.key_event = function(event, name) { // Prevent repeat events @@ -482,6 +486,8 @@ mpl.figure.prototype.key_event = function(event, name) { value += 'k'; value += event.which.toString(); + this._key_event_extra(event, name); + this.send_message(name, {key: value}); return false; } diff --git a/lib/matplotlib/backends/web_backend/nbagg_mpl.js b/lib/matplotlib/backends/web_backend/nbagg_mpl.js index fa333ddc974f..f42363698a97 100644 --- a/lib/matplotlib/backends/web_backend/nbagg_mpl.js +++ b/lib/matplotlib/backends/web_backend/nbagg_mpl.js @@ -149,6 +149,22 @@ mpl.figure.prototype._canvas_extra_style = function(el){ } +mpl.figure.prototype._key_event_extra = function(event, name) { + var manager = IPython.notebook.keyboard_manager; + if (!manager) + IPython.keyboard_manager; + + // Check for shift+enter + if (event.shiftKey && event.which == 13) { + this.canvas_div.blur(); + event.shiftKey = false; + // Send a "J" for go to next cell + event.which = 74; + event.keyCode = 74; + manager.command_mode(); + manager.handle_keydown(event); + } +} mpl.figure.prototype.handle_save = function(fig, msg) { fig.ondownload(fig, null); From 31b3f8ed08f021fb55f3c2ec892a8d61e91c6fe1 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 26 Jan 2015 07:34:34 -0600 Subject: [PATCH 6/7] Add a 'docstring' to _key_event_extra --- lib/matplotlib/backends/web_backend/mpl.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/backends/web_backend/mpl.js b/lib/matplotlib/backends/web_backend/mpl.js index e8711d888f83..20a83a98be94 100644 --- a/lib/matplotlib/backends/web_backend/mpl.js +++ b/lib/matplotlib/backends/web_backend/mpl.js @@ -458,14 +458,14 @@ mpl.figure.prototype.mouse_event = function(event, name) { return false; } -mpl.figure.prototype._key_event_extra = function(event, name) { - +mpl.figure.prototype._key_event_extra = function(event, name) { + // Handle any extra behaviour associated with a key event } mpl.figure.prototype.key_event = function(event, name) { // Prevent repeat events - if (name == 'key_press') + if (name == 'key_press') { if (event.which === this._key) return; From 33bffe7c03354d124082f722b659fee9646ed0bf Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 26 Jan 2015 21:29:19 -0500 Subject: [PATCH 7/7] BUG : missing manager assignment --- lib/matplotlib/backends/web_backend/nbagg_mpl.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/web_backend/nbagg_mpl.js b/lib/matplotlib/backends/web_backend/nbagg_mpl.js index f42363698a97..cdc312b2ba46 100644 --- a/lib/matplotlib/backends/web_backend/nbagg_mpl.js +++ b/lib/matplotlib/backends/web_backend/nbagg_mpl.js @@ -149,10 +149,10 @@ mpl.figure.prototype._canvas_extra_style = function(el){ } -mpl.figure.prototype._key_event_extra = function(event, name) { +mpl.figure.prototype._key_event_extra = function(event, name) { var manager = IPython.notebook.keyboard_manager; if (!manager) - IPython.keyboard_manager; + manager = IPython.keyboard_manager; // Check for shift+enter if (event.shiftKey && event.which == 13) {