Coverage for C:\leo.repo\leo-editor\leo\plugins\qt_events.py : 19%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2#@+leo-ver=5-thin
3#@+node:ekr.20140907103315.18766: * @file ../plugins/qt_events.py
4#@@first
5"""Leo's Qt event handling code."""
6#@+<< about internal bindings >>
7#@+node:ekr.20110605121601.18538: ** << about internal bindings >>
8#@@language rest
9#@+at
10# Here are the rules for translating key bindings (in leoSettings.leo) into keys
11# for k.bindingsDict:
12#
13# 1. The case of plain letters is significant: a is not A.
14#
15# 2. The Shift- prefix can be applied *only* to letters. Leo will ignore (with a
16# warning) the shift prefix applied to any other binding, e.g., Ctrl-Shift-(
17#
18# 3. The case of letters prefixed by Ctrl-, Alt-, Key- or Shift- is *not*
19# significant. Thus, the Shift- prefix is required if you want an upper-case
20# letter (with the exception of 'bare' uppercase letters.)
21#
22# The following table illustrates these rules. In each row, the first entry is the
23# key (for k.bindingsDict) and the other entries are equivalents that the user may
24# specify in leoSettings.leo:
25#
26# a, Key-a, Key-A
27# A, Shift-A
28# Alt-a, Alt-A
29# Alt-A, Alt-Shift-a, Alt-Shift-A
30# Ctrl-a, Ctrl-A
31# Ctrl-A, Ctrl-Shift-a, Ctrl-Shift-A
32# , Key-!,Key-exclam,exclam
33#
34# This table is consistent with how Leo already works (because it is consistent
35# with Tk's key-event specifiers). It is also, I think, the least confusing set of
36# rules.
37#@-<< about internal bindings >>
38from typing import Any, List
39from leo.core import leoGlobals as g
40from leo.core import leoGui
41from leo.core.leoQt import QtCore, QtGui, QtWidgets
42from leo.core.leoQt import Key, KeyboardModifier, Type
43#@+others
44#@+node:ekr.20210512101604.1: ** class LossageData
45class LossageData:
47 def __init__(self, actual_ch, binding, ch, keynum, mods, mods2, mods3, text, toString):
49 self.actual_ch = actual_ch
50 self.binding = binding
51 self.ch = ch
52 self.keynum = keynum
53 self.mods = mods
54 self.mods2 = mods2
55 self.mods3 = mods3
56 self.stroke = None # Set later.
57 self.text = text
58 self.toString = toString
60 def __repr__(self):
61 return (
62 f"keynum: {self.keynum:>7x} "
63 f"binding: {self.binding}"
64 )
65 # f"ch: {self.ch:>7s} "
66 # f"= {self.actual_ch!r}"
67 # f"mods: {self.mods}, {self.mods2}, {self.mods3}\n"
68 # f"stroke: {self.stroke!r}\n"
69 # f"text: {self.text!r}\n"
70 # f"toString: {self.toString!r}\n"
72 __str__ = __repr__
73#@+node:ekr.20141028061518.17: ** class LeoQtEventFilter
74class LeoQtEventFilter(QtCore.QObject): # type:ignore
75 #@+others
76 #@+node:ekr.20110605121601.18539: *3* filter.ctor
77 def __init__(self, c, w, tag=''):
78 """Ctor for LeoQtEventFilter class."""
79 super().__init__()
80 self.c = c
81 self.w = w # A leoQtX object, *not* a Qt object.
82 self.tag = tag
83 # Debugging.
84 self.keyIsActive = False
85 # Pretend there is a binding for these characters.
86 close_flashers = c.config.getString('close-flash-brackets') or ''
87 open_flashers = c.config.getString('open-flash-brackets') or ''
88 self.flashers = open_flashers + close_flashers
89 # #1563: Support alternate keyboards.
90 self.keyboard_kind = c.config.getString('keyboard-kind') or 'default-keyboard'
91 # Support for ctagscompleter.py plugin.
92 self.ctagscompleter_active = False
93 self.ctagscompleter_onKey = None
94 #@+node:ekr.20110605121601.18540: *3* filter.eventFilter & helpers
95 def eventFilter(self, obj, event):
96 """Return False if Qt should handle the event."""
97 c, k = self.c, self.c.k
98 #
99 # Handle non-key events first.
100 if not g.app:
101 return False # For unit tests, but g.unitTesting may be False!
102 if not self.c.p:
103 return False # Startup.
104 #
105 # Trace events.
106 if 'events' in g.app.debug:
107 if isinstance(event, QtGui.QKeyEvent):
108 self.traceKeys(obj, event)
109 else:
110 self.traceEvent(obj, event)
111 self.traceWidget(event)
112 #
113 # Let Qt handle the non-key events.
114 if self.doNonKeyEvent(event, obj):
115 return False
116 #
117 # Ignore incomplete key events.
118 if self.shouldIgnoreKeyEvent(event, obj):
119 return False
120 #
121 # Generate a g.KeyStroke for k.masterKeyHandler.
122 try:
123 binding, ch, lossage = self.toBinding(event)
124 if not binding:
125 return False # Not the correct event type.
126 #
127 # Pass the KeyStroke to masterKeyHandler.
128 key_event = self.createKeyEvent(event, c, self.w, ch, binding)
129 #
130 # #1933: Update the g.app.lossage
131 if len(g.app.lossage) > 99:
132 g.app.lossage.pop()
133 lossage.stroke = key_event.stroke
134 g.app.lossage.insert(0, lossage)
135 #
136 # Call masterKeyHandler!
137 k.masterKeyHandler(key_event)
138 c.outerUpdate()
139 except Exception:
140 g.es_exception()
141 return True
142 # Whatever happens, suppress all other Qt key handling.
143 #@+node:ekr.20110605195119.16937: *4* filter.createKeyEvent
144 def createKeyEvent(self, event, c, w, ch, binding):
146 return leoGui.LeoKeyEvent(
147 c=self.c,
148 char=ch,
149 # char = None doesn't work at present.
150 # But really, the binding should suffice.
151 event=event,
152 binding=binding,
153 w=w,
154 x=getattr(event, 'x', None) or 0,
155 y=getattr(event, 'y', None) or 0,
156 x_root=getattr(event, 'x_root', None) or 0,
157 y_root=getattr(event, 'y_root', None) or 0,
158 )
159 #@+node:ekr.20180413180751.2: *4* filter.doNonKeyEvent
160 def doNonKeyEvent(self, event, obj):
161 """Handle all non-key event. """
162 c = self.c
163 eventType = event.type()
164 if eventType == Type.WindowActivate:
165 g.app.gui.onActivateEvent(event, c, obj, self.tag)
166 elif eventType == Type.WindowDeactivate:
167 g.app.gui.onDeactivateEvent(event, c, obj, self.tag)
168 elif eventType == Type.FocusIn:
169 if self.tag == 'body':
170 c.frame.body.onFocusIn(obj)
171 if c.frame and c.frame.top and obj is c.frame.top.lineEdit:
172 if c.k.getStateKind() == 'getArg':
173 c.frame.top.lineEdit.restore_selection()
174 elif eventType == Type.FocusOut and self.tag == 'body':
175 c.frame.body.onFocusOut(obj)
176 return eventType not in (Type.ShortcutOverride, Type.KeyPress, Type.KeyRelease)
177 # Return True unless we have a key event.
178 #@+node:ekr.20180413180751.3: *4* filter.shouldIgnoreKeyEvent
179 def shouldIgnoreKeyEvent(self, event, obj):
180 """
181 Return True if we should ignore the key event.
183 Alas, QLineEdit *only* generates ev.KeyRelease on Windows, Ubuntu,
184 so the following hack is required.
185 """
186 c = self.c
187 t = event.type()
188 isEditWidget = (obj == c.frame.tree.edit_widget(c.p))
189 if isEditWidget:
190 return t != Type.KeyRelease
191 # QLineEdit: ignore all key events except keyRelease events.
192 if t == Type.KeyPress:
193 return False # Never ignore KeyPress events.
194 # This doesn't work. Two shortcut-override events are generated!
195 # if t == ev.ShortcutOverride and event.text():
196 # return False # Don't ignore shortcut overrides with a real value.
197 return True # Ignore everything else.
198 #@+node:ekr.20110605121601.18543: *4* filter.toBinding & helpers
199 def toBinding(self, event):
200 """
201 Return (binding, actual_ch):
203 binding: A user binding, to create g.KeyStroke.
204 Spelling no longer fragile.
205 actual_ch: The insertable key, or ''.
206 """
207 mods = self.qtMods(event)
208 keynum, text, toString, ch = self.qtKey(event)
209 actual_ch = text or toString
210 #
211 # Never allow empty chars, or chars in g.app.gui.ignoreChars
212 if toString in g.app.gui.ignoreChars:
213 return None, None, None
214 ch = ch or toString or ''
215 if not ch:
216 return None, None, None
217 #
218 # Check for AltGr and Alt+Ctrl keys *before* creating a binding.
219 actual_ch, ch, mods2 = self.doMacTweaks(actual_ch, ch, mods)
220 mods3 = self.doAltTweaks(actual_ch, keynum, mods2, toString)
221 #
222 # Use *ch* in the binding.
223 # Clearer w/o f-strings.
224 binding = '%s%s' % (''.join([f"{z}+" for z in mods3]), ch)
225 #
226 # Return the tweaked *actual* char.
227 binding, actual_ch = self.doLateTweaks(binding, actual_ch)
228 #
229 # #1933: Create lossage data.
230 lossage = LossageData(
231 actual_ch, binding, ch, keynum, mods, mods2, mods3, text, toString)
232 return binding, actual_ch, lossage
233 #@+node:ekr.20180419154543.1: *5* filter.doAltTweaks
234 def doAltTweaks(self, actual_ch, keynum, mods, toString):
235 """Turn AltGr and some Alt-Ctrl keys into plain keys."""
237 def removeAltCtrl(mods):
238 for mod in ('Alt', 'Control'):
239 if mod in mods:
240 mods.remove(mod)
241 return mods
243 #
244 # Remove Alt, Ctrl for AltGr keys.
245 # See https://en.wikipedia.org/wiki/AltGr_key
247 if keynum == Key.Key_AltGr:
248 return removeAltCtrl(mods)
249 #
250 # Never alter complex characters.
251 if len(actual_ch) != 1:
252 return mods
253 #
254 # #1563: A hack for German and Spanish keyboards:
255 # Remove *plain* Shift modifier for colon and semicolon.
256 # https://en.m.wikipedia.org/wiki/German_keyboard_layout
257 kind = self.keyboard_kind.lower()
258 if (kind in ('german', 'spanish')
259 and actual_ch in ":;"
260 and 'Shift' in mods
261 and 'Alt' not in mods and 'Control' not in mods
262 ):
263 mods.remove('Shift')
264 elif kind == 'us-international':
265 pass # To do.
266 #
267 # Handle Alt-Ctrl modifiers for chars whose that are not ascii.
268 # Testing: Alt-Ctrl-E is '€'.
269 if ord(actual_ch) > 127 and 'Alt' in mods and 'Control' in mods:
270 return removeAltCtrl(mods)
271 return mods
272 #@+node:ekr.20180417161548.1: *5* filter.doLateTweaks
273 def doLateTweaks(self, binding, ch):
274 """Make final tweaks. g.KeyStroke does other tweaks later."""
275 #
276 # These are needed because ch is separate from binding.
277 if ch == '\r':
278 ch = '\n'
279 if binding == 'Escape':
280 ch = 'Escape'
281 #
282 # Adjust the case of the binding string (for the minibuffer).
283 if len(ch) == 1 and len(binding) == 1 and ch.isalpha() and binding.isalpha():
284 if ch != binding:
285 binding = ch
286 return binding, ch
287 #@+node:ekr.20180419160958.1: *5* filter.doMacTweaks
288 def doMacTweaks(self, actual_ch, ch, mods):
289 """Replace MacOS Alt characters."""
290 if not g.isMac:
291 return actual_ch, ch, mods
292 if ch == 'Backspace':
293 # On the Mac, the reported char can be DEL (7F)
294 return '\b', ch, mods
295 if len(mods) == 1 and mods[0] == 'Alt':
296 # Patch provided by resi147.
297 # See the thread: special characters in MacOSX, like '@'.
298 mac_d = {
299 '/': '\\',
300 '5': '[',
301 '6': ']',
302 '7': '|',
303 '8': '{',
304 '9': '}',
305 'e': '€',
306 'l': '@',
307 }
308 if ch.lower() in mac_d:
309 # Ignore the case.
310 actual_ch = ch = g.checkUnicode(mac_d.get(ch.lower()))
311 mods = []
312 return actual_ch, ch, mods
313 #@+node:ekr.20110605121601.18544: *5* filter.qtKey
314 def qtKey(self, event):
315 """
316 Return the components of a Qt key event.
318 Modifiers are handled separately.
320 Return (keynum, text, toString, ch).
322 keynum: event.key()
323 ch: chr(keynum) or '' if there is an exception.
324 toString:
325 For special keys: made-up spelling that become part of the setting.
326 For all others: QtGui.QKeySequence(keynum).toString()
327 text: event.text()
328 """
329 text, toString, ch = '', '', '' # Defaults.
330 #
331 # Leo 6.4: Test keynum's directly.
332 # The values are the same in Qt4, Qt5, Qt6.
333 keynum = event.key()
334 if keynum in (
335 0x01000020, # Key_Shift
336 0x01000021, # Key_Control
337 0x01000022, # Key_Meta
338 0x01000023, # Key_Alt
339 0x01001103, # Key_AltGr
340 0x01000024, # Key_CapsLock
341 ):
342 # Disallow bare modifiers.
343 return keynum, text, toString, ch
344 #
345 # Compute toString and ch.
346 text = event.text() # This is the unicode character!
347 toString = QtGui.QKeySequence(keynum).toString()
348 #
349 # #1244461: Numpad 'Enter' key does not work in minibuffer
350 if toString == 'Enter':
351 toString = 'Return'
352 if toString == 'Esc':
353 toString = 'Escape'
354 try:
355 ch = chr(keynum)
356 except ValueError:
357 pass
358 return keynum, text, toString, ch
359 #@+node:ekr.20120204061120.10084: *5* filter.qtMods
360 def qtMods(self, event):
361 """Return the text version of the modifiers of the key event."""
362 modifiers = event.modifiers()
363 mod_table = (
364 (KeyboardModifier.AltModifier, 'Alt'),
365 (KeyboardModifier.ControlModifier, 'Control'),
366 (KeyboardModifier.MetaModifier, 'Meta'),
367 (KeyboardModifier.ShiftModifier, 'Shift'),
368 (KeyboardModifier.KeypadModifier, 'KeyPad'),
369 # #1448: Replacing this by 'Key' would make separate keypad bindings impossible.
370 )
371 # pylint: disable=superfluous-parens.
372 mods = [b for a, b in mod_table if (modifiers & a)]
373 return mods
374 #@+node:ekr.20140907103315.18767: *3* filter.Tracing
375 #@+node:ekr.20190922075339.1: *4* filter.traceKeys
376 def traceKeys(self, obj, event):
377 if g.unitTesting:
378 return
379 e = QtCore.QEvent
380 key_events = {
381 e.KeyPress: 'key-press', # 6
382 e.KeyRelease: 'key-release', # 7
383 e.Shortcut: 'shortcut', # 117
384 e.ShortcutOverride: 'shortcut-override', # 51
385 }
386 kind = key_events.get(event.type())
387 if kind:
388 mods = ','.join(self.qtMods(event))
389 g.trace(f"{kind:>20}: {mods:>7} {event.text()!r}")
390 #@+node:ekr.20110605121601.18548: *4* filter.traceEvent
391 def traceEvent(self, obj, event):
392 if g.unitTesting:
393 return
394 # http://qt-project.org/doc/qt-4.8/qevent.html#properties
395 exclude_names = ('tree', 'log', 'body', 'minibuffer')
396 traceActivate = True
397 traceFocus = False
398 traceHide = False
399 traceHover = False
400 traceKey = False
401 traceLayout = False
402 traceMouse = False
403 tracePaint = False
404 traceUpdate = False
405 c, e = self.c, QtCore.QEvent
406 eventType = event.type()
407 # http://doc.qt.io/qt-5/qevent.html
408 show: List[Any] = []
409 ignore = [
410 e.MetaCall, # 43
411 e.Timer, # 1
412 e.ToolTip, # 110
413 ]
414 activate_events = (
415 (e.Close, 'close'), # 19
416 (e.WindowActivate, 'window-activate'), # 24
417 (e.WindowBlocked, 'window-blocked'), # 103
418 (e.WindowUnblocked, 'window-unblocked'), # 104
419 (e.WindowDeactivate, 'window-deactivate'), # 25
420 )
421 focus_events = [
422 (e.Enter, 'enter'), # 10
423 (e.Leave, 'leave'), # 11
424 (e.FocusIn, 'focus-in'), # 8
425 (e.FocusOut, 'focus-out'), # 9
426 (e.ShowToParent, 'show-to-parent'), # 26
427 ]
428 if hasattr(e, 'FocusAboutToChange'):
429 # pylint: disable=no-member
430 focus_events.extend([
431 (e.FocusAboutToChange, 'focus-about-to-change'), # 23
432 ])
433 hide_events = (
434 (e.Hide, 'hide'), # 18
435 (e.HideToParent, 'hide-to-parent'), # 27
436 # (e.LeaveEditFocus,'leave-edit-focus'), # 151
437 (e.Show, 'show'), # 17
438 )
439 hover_events = (
440 (e.HoverEnter, 'hover-enter'), # 127
441 (e.HoverLeave, 'hover-leave'), # 128
442 (e.HoverMove, 'hover-move'), # 129
443 )
444 key_events = [
445 (e.KeyPress, 'key-press'), # 6
446 (e.KeyRelease, 'key-release'), # 7
447 (e.Shortcut, 'shortcut'), # 117
448 (e.ShortcutOverride, 'shortcut-override'), # 51
449 ]
450 if hasattr(e, 'InputMethodQuery'):
451 # pylint: disable=no-member
452 key_events.extend([
453 (e.InputMethodQuery, 'input-method-query'), # 207
454 ])
455 layout_events = [
456 (e.ChildAdded, 'child-added'), # 68
457 (e.ChildRemoved, 'child-removed'), # 71
458 (e.DynamicPropertyChange, 'dynamic-property-change'), # 170
459 (e.FontChange, 'font-change'), # 97
460 (e.LayoutRequest, 'layout-request'), # 76
461 (e.Move, 'move'), # 13 widget's position changed.
462 (e.Resize, 'resize'), # 14
463 (e.StyleChange, 'style-change'), # 100
464 (e.ZOrderChange, 'z-order-change'), # 126
465 ]
466 if hasattr(e, 'CloseSoftwareInputPanel'):
467 layout_events.extend([
468 (e.CloseSoftwareInputPanel, 'close-sip'), # 200
469 ])
470 mouse_events = (
471 (e.MouseMove, 'mouse-move'), # 155
472 (e.MouseButtonPress, 'mouse-press'), # 2
473 (e.MouseButtonRelease, 'mouse-release'), # 3
474 (e.Wheel, 'mouse-wheel'), # 31
475 )
476 paint_events = [
477 (e.ChildPolished, 'child-polished'), # 69
478 (e.PaletteChange, 'palette-change'), # 39
479 (e.ParentChange, 'parent-change'), # 21
480 (e.Paint, 'paint'), # 12
481 (e.Polish, 'polish'), # 75
482 (e.PolishRequest, 'polish-request'), # 74
483 ]
484 if hasattr(e, 'RequestSoftwareInputPanel'):
485 paint_events.extend([
486 (e.RequestSoftwareInputPanel, 'sip'), # 199
487 ])
488 update_events = (
489 (e.UpdateLater, 'update-later'), # 78
490 (e.UpdateRequest, 'update'), # 77
491 )
492 option_table = (
493 (traceActivate, activate_events),
494 (traceFocus, focus_events),
495 (traceHide, hide_events),
496 (traceHover, hover_events),
497 (traceKey, key_events),
498 (traceLayout, layout_events),
499 (traceMouse, mouse_events),
500 (tracePaint, paint_events),
501 (traceUpdate, update_events),
502 )
503 for option, table in option_table:
504 if option:
505 show.extend(table)
506 else:
507 for n, tag in table:
508 ignore.append(n)
509 for val, kind in show:
510 if self.tag in exclude_names:
511 return
512 if eventType == val:
513 tag = (
514 obj.objectName() if hasattr(obj, 'objectName')
515 else f"id: {id(obj)}, {obj.__class__.__name__}"
516 )
517 if traceKey:
518 g.trace(
519 f"{kind:-25} {self.tag:-25} "
520 f"in-state: {repr(c.k and c.k.inState()):5} obj: {tag}")
521 return
522 if eventType not in ignore:
523 tag = (
524 obj.objectName() if hasattr(obj, 'objectName')
525 else f"id: {id(obj)}, {obj.__class__.__name__}"
526 )
527 g.trace(f"{eventType:-25} {self.tag:-25} {tag}")
528 #@+node:ekr.20131121050226.16331: *4* filter.traceWidget
529 def traceWidget(self, event):
530 """Show unexpected events in unusual widgets."""
531 verbose = False
532 # Not good for --trace-events
533 e = QtCore.QEvent
534 assert isinstance(event, QtCore.QEvent)
535 et = event.type()
536 # http://qt-project.org/doc/qt-4.8/qevent.html#properties
537 ignore_d = {
538 e.ChildAdded: 'child-added', # 68
539 e.ChildPolished: 'child-polished', # 69
540 e.ChildRemoved: 'child-removed', # 71
541 e.Close: 'close', # 19
542 e.CloseSoftwareInputPanel: 'close-software-input-panel', # 200
543 178: 'contents-rect-change', # 178
544 # e.DeferredDelete:'deferred-delete', # 52 (let's trace this)
545 e.DynamicPropertyChange: 'dynamic-property-change', # 170
546 e.FocusOut: 'focus-out', # 9 (We don't care if we are leaving an unknown widget)
547 e.FontChange: 'font-change', # 97
548 e.Hide: 'hide', # 18
549 e.HideToParent: 'hide-to-parent', # 27
550 e.HoverEnter: 'hover-enter', # 127
551 e.HoverLeave: 'hover-leave', # 128
552 e.HoverMove: 'hover-move', # 129
553 e.KeyPress: 'key-press', # 6
554 e.KeyRelease: 'key-release', # 7
555 e.LayoutRequest: 'layout-request', # 76
556 e.Leave: 'leave', # 11 (We don't care if we are leaving an unknown widget)
557 # e.LeaveEditFocus:'leave-edit-focus', # 151
558 e.MetaCall: 'meta-call', # 43
559 e.Move: 'move', # 13 widget's position changed.
560 e.MouseButtonPress: 'mouse-button-press', # 2
561 e.MouseButtonRelease: 'mouse-button-release', # 3
562 e.MouseButtonDblClick: 'mouse-button-double-click', # 4
563 e.MouseMove: 'mouse-move', # 5
564 e.MouseTrackingChange: 'mouse-tracking-change', # 105
565 e.Paint: 'paint', # 12
566 e.PaletteChange: 'palette-change', # 39
567 e.ParentChange: 'parent-change', # 21
568 e.Polish: 'polish', # 75
569 e.PolishRequest: 'polish-request', # 74
570 e.RequestSoftwareInputPanel: 'request-software-input-panel', # 199
571 e.Resize: 'resize', # 14
572 e.ShortcutOverride: 'shortcut-override', # 51
573 e.Show: 'show', # 17
574 e.ShowToParent: 'show-to-parent', # 26
575 e.StyleChange: 'style-change', # 100
576 e.StatusTip: 'status-tip', # 112
577 e.Timer: 'timer', # 1
578 e.ToolTip: 'tool-tip', # 110
579 e.WindowBlocked: 'window-blocked', # 103
580 e.WindowUnblocked: 'window-unblocked', # 104
581 e.ZOrderChange: 'z-order-change', # 126
582 }
583 focus_d = {
584 e.DeferredDelete: 'deferred-delete', # 52
585 e.Enter: 'enter', # 10
586 e.FocusIn: 'focus-in', # 8
587 e.WindowActivate: 'window-activate', # 24
588 e.WindowDeactivate: 'window-deactivate', # 25
589 }
590 line_edit_ignore_d = {
591 e.Enter: 'enter', # 10 (mouse over)
592 e.Leave: 'leave', # 11 (mouse over)
593 e.FocusOut: 'focus-out', # 9
594 e.WindowActivate: 'window-activate', # 24
595 e.WindowDeactivate: 'window-deactivate', # 25
596 }
597 none_ignore_d = {
598 e.Enter: 'enter', # 10 (mouse over)
599 e.Leave: 'leave', # 11 (mouse over)
600 e.FocusOut: 'focus-out', # 9
601 e.WindowActivate: 'window-activate', # 24
602 }
603 if et in ignore_d:
604 return
605 w = QtWidgets.QApplication.focusWidget()
606 if verbose: # Too verbose for --trace-events.
607 for d in (ignore_d, focus_d, line_edit_ignore_d, none_ignore_d):
608 t = d.get(et)
609 if t:
610 break
611 else:
612 t = et
613 g.trace(f"{t:20} {w.__class__}")
614 return
615 if w is None:
616 if et not in none_ignore_d:
617 t = focus_d.get(et) or et
618 g.trace(f"None {t}")
619 if isinstance(w, QtWidgets.QPushButton):
620 return
621 if isinstance(w, QtWidgets.QLineEdit):
622 if et not in line_edit_ignore_d:
623 t = focus_d.get(et) or et
624 if hasattr(w, 'objectName'):
625 tag = w.objectName()
626 else:
627 tag = f"id: {id(w)}, {w.__class__.__name__}"
628 g.trace(f"{t:20} {tag}")
629 return
630 t = focus_d.get(et) or et
631 if hasattr(w, 'objectName'):
632 tag = w.objectName()
633 else:
634 tag = f"id: {id(w)}, {w.__class__.__name__}"
635 g.trace(f"{t:20} {tag}")
636 #@-others
637#@-others
638#@@language python
639#@@tabwidth -4
640#@@pagewidth 70
641#@-leo