Skip to content

Commit 2f0f7b4

Browse files
authored
Merge pull request #17791 from anntzer/gtkkeys
More accurate handling of unicode/numpad input in gtk3 backends.
2 parents 039fc00 + 1d0fcb5 commit 2f0f7b4

File tree

4 files changed

+77
-62
lines changed

4 files changed

+77
-62
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
GTK key name changes
2+
~~~~~~~~~~~~~~~~~~~~
3+
4+
The handling of non-ASCII keypresses (as reported in the KeyEvent passed to
5+
``key_press_event``-handlers) in the GTK backends now correctly reports Unicode
6+
characters (e.g., €), and respects NumLock on the numpad.
7+
8+
The following key names have changed; the new names are consistent with those
9+
reported by the Qt backends:
10+
11+
- The "Break/Pause" key (keysym 0xff13) is now reported as "pause" instead of
12+
"break" (this is also consistent with the X key name).
13+
- The numpad "delete" key is now reported as "delete" instead of "dec".

lib/matplotlib/backends/backend_gtk3.py

+11-60
Original file line numberDiff line numberDiff line change
@@ -88,58 +88,6 @@ def _on_timer(self):
8888
class FigureCanvasGTK3(Gtk.DrawingArea, FigureCanvasBase):
8989
required_interactive_framework = "gtk3"
9090
_timer_cls = TimerGTK3
91-
92-
keyvald = {65507: 'control',
93-
65505: 'shift',
94-
65513: 'alt',
95-
65508: 'control',
96-
65506: 'shift',
97-
65514: 'alt',
98-
65361: 'left',
99-
65362: 'up',
100-
65363: 'right',
101-
65364: 'down',
102-
65307: 'escape',
103-
65470: 'f1',
104-
65471: 'f2',
105-
65472: 'f3',
106-
65473: 'f4',
107-
65474: 'f5',
108-
65475: 'f6',
109-
65476: 'f7',
110-
65477: 'f8',
111-
65478: 'f9',
112-
65479: 'f10',
113-
65480: 'f11',
114-
65481: 'f12',
115-
65300: 'scroll_lock',
116-
65299: 'break',
117-
65288: 'backspace',
118-
65293: 'enter',
119-
65379: 'insert',
120-
65535: 'delete',
121-
65360: 'home',
122-
65367: 'end',
123-
65365: 'pageup',
124-
65366: 'pagedown',
125-
65438: '0',
126-
65436: '1',
127-
65433: '2',
128-
65435: '3',
129-
65430: '4',
130-
65437: '5',
131-
65432: '6',
132-
65429: '7',
133-
65431: '8',
134-
65434: '9',
135-
65451: '+',
136-
65453: '-',
137-
65450: '*',
138-
65455: '/',
139-
65439: 'dec',
140-
65421: 'enter',
141-
}
142-
14391
# Setting this as a static constant prevents
14492
# this resulting expression from leaking
14593
event_mask = (Gdk.EventMask.BUTTON_PRESS_MASK
@@ -259,13 +207,17 @@ def size_allocate(self, widget, allocation):
259207
self.draw_idle()
260208

261209
def _get_key(self, event):
262-
if event.keyval in self.keyvald:
263-
key = self.keyvald[event.keyval]
264-
elif event.keyval < 256:
265-
key = chr(event.keyval)
266-
else:
267-
key = None
268-
210+
key = chr(Gdk.keyval_to_unicode(event.keyval))
211+
if not key.isprintable():
212+
key = Gdk.keyval_name(event.keyval).lower()
213+
if key.startswith("kp_"): # keypad_x (including kp_enter).
214+
key = key[3:]
215+
if key.startswith("page_"): # page_{up,down}
216+
key = key.replace("page_", "page")
217+
if key.endswith(("_l", "_r")): # alt_l, ctrl_l, shift_l.
218+
key = key[:-2]
219+
if key == "enter":
220+
key = "return"
269221
modifiers = [
270222
(Gdk.ModifierType.MOD4_MASK, 'super'),
271223
(Gdk.ModifierType.MOD1_MASK, 'alt'),
@@ -274,7 +226,6 @@ def _get_key(self, event):
274226
for key_mask, prefix in modifiers:
275227
if event.state & key_mask:
276228
key = '{0}+{1}'.format(prefix, key)
277-
278229
return key
279230

280231
def configure_event(self, widget, event):
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from matplotlib import pyplot as plt
2+
3+
import pytest
4+
5+
6+
pytest.importorskip("matplotlib.backends.backend_gtk3agg")
7+
8+
9+
@pytest.mark.backend("gtk3agg")
10+
def test_correct_key():
11+
pytest.xfail("test_widget_send_event is not triggering key_press_event")
12+
13+
from gi.repository import Gdk, Gtk
14+
fig = plt.figure()
15+
buf = []
16+
17+
def send(event):
18+
for key, mod in [
19+
(Gdk.KEY_a, Gdk.ModifierType.SHIFT_MASK),
20+
(Gdk.KEY_a, 0),
21+
(Gdk.KEY_a, Gdk.ModifierType.CONTROL_MASK),
22+
(Gdk.KEY_agrave, 0),
23+
(Gdk.KEY_Control_L, Gdk.ModifierType.MOD1_MASK),
24+
(Gdk.KEY_Alt_L, Gdk.ModifierType.CONTROL_MASK),
25+
(Gdk.KEY_agrave,
26+
Gdk.ModifierType.CONTROL_MASK
27+
| Gdk.ModifierType.MOD1_MASK
28+
| Gdk.ModifierType.MOD4_MASK),
29+
(0xfd16, 0), # KEY_3270_Play.
30+
(Gdk.KEY_BackSpace, 0),
31+
(Gdk.KEY_BackSpace, Gdk.ModifierType.CONTROL_MASK),
32+
]:
33+
# This is not actually really the right API: it depends on the
34+
# actual keymap (e.g. on Azerty, shift+agrave -> 0).
35+
Gtk.test_widget_send_key(fig.canvas, key, mod)
36+
37+
def receive(event):
38+
buf.append(event.key)
39+
if buf == [
40+
"A", "a", "ctrl+a",
41+
"\N{LATIN SMALL LETTER A WITH GRAVE}",
42+
"alt+control", "ctrl+alt",
43+
"ctrl+alt+super+\N{LATIN SMALL LETTER A WITH GRAVE}",
44+
# (No entry for KEY_3270_Play.)
45+
"backspace", "ctrl+backspace",
46+
]:
47+
plt.close(fig)
48+
49+
fig.canvas.mpl_connect("draw_event", send)
50+
fig.canvas.mpl_connect("key_press_event", receive)
51+
plt.show()

lib/matplotlib/tests/test_backend_qt.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ def CustomHandler(signum, frame):
110110
('Key_Alt', ['ControlModifier'], 'ctrl+alt'),
111111
('Key_Aacute', ['ControlModifier', 'AltModifier', 'MetaModifier'],
112112
'ctrl+alt+super+\N{LATIN SMALL LETTER A WITH ACUTE}'),
113+
('Key_Play', [], None),
113114
('Key_Backspace', [], 'backspace'),
114115
('Key_Backspace', ['ControlModifier'], 'ctrl+backspace'),
115-
('Key_Play', [], None),
116116
],
117117
ids=[
118118
'shift',
@@ -123,9 +123,9 @@ def CustomHandler(signum, frame):
123123
'alt_control',
124124
'control_alt',
125125
'modifier_order',
126+
'non_unicode_key',
126127
'backspace',
127128
'backspace_mod',
128-
'non_unicode_key',
129129
]
130130
)
131131
@pytest.mark.parametrize('backend', [

0 commit comments

Comments
 (0)