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

266 statements  

« prev     ^ index     » next       coverage.py v6.4, created at 2022-05-24 10:21 -0500

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 >> 

38import sys 

39from typing import Any, List 

40from leo.core import leoGlobals as g 

41from leo.core import leoGui 

42from leo.core.leoQt import QtCore, QtGui, QtWidgets 

43from leo.core.leoQt import Key, KeyboardModifier, Type 

44#@+others 

45#@+node:ekr.20210512101604.1: ** class LossageData 

46class LossageData: 

47 

48 def __init__(self, actual_ch, binding, ch, keynum, mods, mods2, mods3, text, toString): 

49 

50 self.actual_ch = actual_ch 

51 self.binding = binding 

52 self.ch = ch 

53 self.keynum = keynum 

54 self.mods = mods 

55 self.mods2 = mods2 

56 self.mods3 = mods3 

57 self.stroke = None # Set later. 

58 self.text = text 

59 self.toString = toString 

60 

61 def __repr__(self): 

62 return ( 

63 f"keynum: {self.keynum:>7x} " 

64 f"binding: {self.binding}" 

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" 

71 ) 

72 

73 __str__ = __repr__ 

74#@+node:ekr.20141028061518.17: ** class LeoQtEventFilter 

75class LeoQtEventFilter(QtCore.QObject): # type:ignore 

76 #@+others 

77 #@+node:ekr.20110605121601.18539: *3* filter.ctor 

78 def __init__(self, c, w, tag=''): 

79 """Ctor for LeoQtEventFilter class.""" 

80 super().__init__() 

81 self.c = c 

82 self.w = w # A leoQtX object, *not* a Qt object. 

83 self.tag = tag 

84 # Debugging. 

85 self.keyIsActive = False 

86 # Pretend there is a binding for these characters. 

87 close_flashers = c.config.getString('close-flash-brackets') or '' 

88 open_flashers = c.config.getString('open-flash-brackets') or '' 

89 self.flashers = open_flashers + close_flashers 

90 # #1563: Support alternate keyboards. 

91 self.keyboard_kind = c.config.getString('keyboard-kind') or 'default-keyboard' 

92 # Support for ctagscompleter.py plugin. 

93 self.ctagscompleter_active = False 

94 self.ctagscompleter_onKey = None 

95 #@+node:ekr.20110605121601.18540: *3* filter.eventFilter & helpers 

96 def eventFilter(self, obj, event): 

97 """Return False if Qt should handle the event.""" 

98 c, k = self.c, self.c.k 

99 # 

100 # Handle non-key events first. 

101 if not g.app: 

102 return False # For unit tests, but g.unitTesting may be False! 

103 if not self.c.p: 

104 return False # Startup. 

105 # 

106 # Trace events. 

107 if 'events' in g.app.debug: 

108 if isinstance(event, QtGui.QKeyEvent): 

109 self.traceKeys(obj, event) 

110 else: 

111 self.traceEvent(obj, event) 

112 self.traceWidget(event) 

113 # 

114 # Let Qt handle the non-key events. 

115 if self.doNonKeyEvent(event, obj): 

116 return False 

117 # 

118 # Ignore incomplete key events. 

119 if self.shouldIgnoreKeyEvent(event, obj): 

120 return False 

121 # 

122 # Generate a g.KeyStroke for k.masterKeyHandler. 

123 try: 

124 binding, ch, lossage = self.toBinding(event) 

125 if not binding: 

126 return False # Let Qt handle the key. 

127 # 

128 # Pass the KeyStroke to masterKeyHandler. 

129 key_event = self.createKeyEvent(event, c, self.w, ch, binding) 

130 # 

131 # #1933: Update the g.app.lossage 

132 if len(g.app.lossage) > 99: 

133 g.app.lossage.pop() 

134 lossage.stroke = key_event.stroke 

135 g.app.lossage.insert(0, lossage) 

136 # 

137 # Call masterKeyHandler! 

138 k.masterKeyHandler(key_event) 

139 c.outerUpdate() 

140 except Exception: 

141 g.es_exception() 

142 return True # 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): 

145 

146 return leoGui.LeoKeyEvent( 

147 c=self.c, 

148 # char = None doesn't work at present. 

149 # But really, the binding should suffice. 

150 char=ch, 

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 True unless we have a key event. 

177 return eventType not in (Type.ShortcutOverride, Type.KeyPress, Type.KeyRelease) 

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. 

182 

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 # QLineEdit: ignore all key events except keyRelease events. 

191 return t != Type.KeyRelease 

192 if t == Type.KeyPress: 

193 # Hack Alert! 

194 # On some Linux systems (Kubuntu, Debian, the Win or SHIFT-Win keys 

195 # insert garbage symbols into editing areas. Filter out these 

196 # key events. NOTE - this is a *magic number* - who knows if 

197 # it could change in the future? 

198 if event.key() == 0x1000053 and sys.platform == 'linux': 

199 return True 

200 return False # Never ignore KeyPress events. 

201 # This doesn't work. Two shortcut-override events are generated! 

202 # if t == ev.ShortcutOverride and event.text(): 

203 # return False # Don't ignore shortcut overrides with a real value. 

204 return True # Ignore everything else. 

205 #@+node:ekr.20110605121601.18543: *4* filter.toBinding & helpers 

206 def toBinding(self, event): 

207 """ 

208 Return (binding, actual_ch): 

209 

210 binding: A user binding, to create g.KeyStroke. 

211 Spelling no longer fragile. 

212 actual_ch: The insertable key, or ''. 

213 """ 

214 mods = self.qtMods(event) 

215 keynum, text, toString, ch = self.qtKey(event) 

216 actual_ch = text or toString 

217 # 

218 # Never allow empty chars, or chars in g.app.gui.ignoreChars 

219 if toString in g.app.gui.ignoreChars: 

220 return None, None, None 

221 ch = ch or toString or '' 

222 if not ch: 

223 return None, None, None 

224 # 

225 # Check for AltGr and Alt+Ctrl keys *before* creating a binding. 

226 actual_ch, ch, mods2 = self.doMacTweaks(actual_ch, ch, mods) 

227 mods3 = self.doAltTweaks(actual_ch, keynum, mods2, toString) 

228 # 

229 # Use *ch* in the binding. 

230 # Clearer w/o f-strings. 

231 binding = '%s%s' % (''.join([f"{z}+" for z in mods3]), ch) 

232 # 

233 # Return the tweaked *actual* char. 

234 binding, actual_ch = self.doLateTweaks(binding, actual_ch) 

235 # 

236 # #1933: Create lossage data. 

237 lossage = LossageData( 

238 actual_ch, binding, ch, keynum, mods, mods2, mods3, text, toString) 

239 return binding, actual_ch, lossage 

240 #@+node:ekr.20180419154543.1: *5* filter.doAltTweaks 

241 def doAltTweaks(self, actual_ch, keynum, mods, toString): 

242 """Turn AltGr and some Alt-Ctrl keys into plain keys.""" 

243 

244 def removeAltCtrl(mods): 

245 for mod in ('Alt', 'Control'): 

246 if mod in mods: 

247 mods.remove(mod) 

248 return mods 

249 

250 # 

251 # Remove Alt, Ctrl for AltGr keys. 

252 # See https://en.wikipedia.org/wiki/AltGr_key 

253 

254 if keynum == Key.Key_AltGr: 

255 return removeAltCtrl(mods) 

256 # 

257 # Never alter complex characters. 

258 if len(actual_ch) != 1: 

259 return mods 

260 # 

261 # #1563: A hack for German and Spanish keyboards: 

262 # Remove *plain* Shift modifier for colon and semicolon. 

263 # https://en.m.wikipedia.org/wiki/German_keyboard_layout 

264 kind = self.keyboard_kind.lower() 

265 if (kind in ('german', 'spanish') 

266 and actual_ch in ":;" 

267 and 'Shift' in mods 

268 and 'Alt' not in mods and 'Control' not in mods 

269 ): 

270 mods.remove('Shift') 

271 elif kind == 'us-international': 

272 pass # To do. 

273 # 

274 # Handle Alt-Ctrl modifiers for chars whose that are not ascii. 

275 # Testing: Alt-Ctrl-E is '€'. 

276 if ord(actual_ch) > 127 and 'Alt' in mods and 'Control' in mods: 

277 return removeAltCtrl(mods) 

278 return mods 

279 #@+node:ekr.20180417161548.1: *5* filter.doLateTweaks 

280 def doLateTweaks(self, binding, ch): 

281 """Make final tweaks. g.KeyStroke does other tweaks later.""" 

282 # 

283 # These are needed because ch is separate from binding. 

284 if ch == '\r': 

285 ch = '\n' 

286 if binding == 'Escape': 

287 ch = 'Escape' 

288 # 

289 # Adjust the case of the binding string (for the minibuffer). 

290 if len(ch) == 1 and len(binding) == 1 and ch.isalpha() and binding.isalpha(): 

291 if ch != binding: 

292 binding = ch 

293 return binding, ch 

294 #@+node:ekr.20180419160958.1: *5* filter.doMacTweaks 

295 def doMacTweaks(self, actual_ch, ch, mods): 

296 """Replace MacOS Alt characters.""" 

297 if not g.isMac: 

298 return actual_ch, ch, mods 

299 if ch == 'Backspace': 

300 # On the Mac, the reported char can be DEL (7F) 

301 return '\b', ch, mods 

302 if len(mods) == 1 and mods[0] == 'Alt': 

303 # Patch provided by resi147. 

304 # See the thread: special characters in MacOSX, like '@'. 

305 mac_d = { 

306 '/': '\\', 

307 '5': '[', 

308 '6': ']', 

309 '7': '|', 

310 '8': '{', 

311 '9': '}', 

312 'e': '€', 

313 'l': '@', 

314 } 

315 if ch.lower() in mac_d: 

316 # Ignore the case. 

317 actual_ch = ch = g.checkUnicode(mac_d.get(ch.lower())) 

318 mods = [] 

319 return actual_ch, ch, mods 

320 #@+node:ekr.20110605121601.18544: *5* filter.qtKey 

321 def qtKey(self, event): 

322 """ 

323 Return the components of a Qt key event. 

324 

325 Modifiers are handled separately. 

326 

327 Return (keynum, text, toString, ch). 

328 

329 keynum: event.key() 

330 ch: chr(keynum) or '' if there is an exception. 

331 toString: 

332 For special keys: made-up spelling that become part of the setting. 

333 For all others: QtGui.QKeySequence(keynum).toString() 

334 text: event.text() 

335 """ 

336 text, toString, ch = '', '', '' # Defaults. 

337 # 

338 # Leo 6.4: Test keynum's directly. 

339 # The values are the same in Qt4, Qt5, Qt6. 

340 keynum = event.key() 

341 if keynum in ( 

342 0x01000020, # Key_Shift 

343 0x01000021, # Key_Control 

344 0x01000022, # Key_Meta 

345 0x01000023, # Key_Alt 

346 0x01001103, # Key_AltGr 

347 0x01000024, # Key_CapsLock 

348 ): 

349 # Disallow bare modifiers. 

350 return keynum, text, toString, ch 

351 # 

352 # Compute toString and ch. 

353 text = event.text() # This is the unicode character! 

354 toString = QtGui.QKeySequence(keynum).toString() 

355 # 

356 # #1244461: Numpad 'Enter' key does not work in minibuffer 

357 if toString == 'Enter': 

358 toString = 'Return' 

359 if toString == 'Esc': 

360 toString = 'Escape' 

361 try: 

362 ch = chr(keynum) 

363 except ValueError: 

364 pass 

365 return keynum, text, toString, ch 

366 #@+node:ekr.20120204061120.10084: *5* filter.qtMods 

367 def qtMods(self, event): 

368 """Return the text version of the modifiers of the key event.""" 

369 modifiers = event.modifiers() 

370 mod_table = ( 

371 (KeyboardModifier.AltModifier, 'Alt'), 

372 (KeyboardModifier.ControlModifier, 'Control'), 

373 (KeyboardModifier.MetaModifier, 'Meta'), 

374 (KeyboardModifier.ShiftModifier, 'Shift'), 

375 # #1448: Replacing this by 'Key' would make separate keypad bindings impossible. 

376 (KeyboardModifier.KeypadModifier, 'KeyPad'), 

377 ) 

378 # pylint: disable=superfluous-parens. 

379 mods = [b for a, b in mod_table if (modifiers & a)] 

380 return mods 

381 #@+node:ekr.20140907103315.18767: *3* filter.Tracing 

382 #@+node:ekr.20190922075339.1: *4* filter.traceKeys 

383 def traceKeys(self, obj, event): 

384 if g.unitTesting: 

385 return 

386 e = QtCore.QEvent 

387 key_events = { 

388 e.KeyPress: 'key-press', # 6 

389 e.KeyRelease: 'key-release', # 7 

390 e.Shortcut: 'shortcut', # 117 

391 e.ShortcutOverride: 'shortcut-override', # 51 

392 } 

393 kind = key_events.get(event.type()) 

394 if kind: 

395 mods = ','.join(self.qtMods(event)) 

396 g.trace(f"{kind:>20}: {mods:>7} {event.text()!r}") 

397 #@+node:ekr.20110605121601.18548: *4* filter.traceEvent 

398 def traceEvent(self, obj, event): 

399 if g.unitTesting: 

400 return 

401 # http://qt-project.org/doc/qt-4.8/qevent.html#properties 

402 exclude_names = ('tree', 'log', 'body', 'minibuffer') 

403 traceActivate = True 

404 traceFocus = False 

405 traceHide = False 

406 traceHover = False 

407 traceKey = False 

408 traceLayout = False 

409 traceMouse = False 

410 tracePaint = False 

411 traceUpdate = False 

412 c, e = self.c, QtCore.QEvent 

413 eventType = event.type() 

414 # http://doc.qt.io/qt-5/qevent.html 

415 show: List[Any] = [] 

416 ignore = [ 

417 e.MetaCall, # 43 

418 e.Timer, # 1 

419 e.ToolTip, # 110 

420 ] 

421 activate_events = ( 

422 (e.Close, 'close'), # 19 

423 (e.WindowActivate, 'window-activate'), # 24 

424 (e.WindowBlocked, 'window-blocked'), # 103 

425 (e.WindowUnblocked, 'window-unblocked'), # 104 

426 (e.WindowDeactivate, 'window-deactivate'), # 25 

427 ) 

428 focus_events = [ 

429 (e.Enter, 'enter'), # 10 

430 (e.Leave, 'leave'), # 11 

431 (e.FocusIn, 'focus-in'), # 8 

432 (e.FocusOut, 'focus-out'), # 9 

433 (e.ShowToParent, 'show-to-parent'), # 26 

434 ] 

435 if hasattr(e, 'FocusAboutToChange'): 

436 # pylint: disable=no-member 

437 focus_events.extend([ 

438 (e.FocusAboutToChange, 'focus-about-to-change'), # 23 

439 ]) 

440 hide_events = ( 

441 (e.Hide, 'hide'), # 18 

442 (e.HideToParent, 'hide-to-parent'), # 27 

443 # (e.LeaveEditFocus,'leave-edit-focus'), # 151 

444 (e.Show, 'show'), # 17 

445 ) 

446 hover_events = ( 

447 (e.HoverEnter, 'hover-enter'), # 127 

448 (e.HoverLeave, 'hover-leave'), # 128 

449 (e.HoverMove, 'hover-move'), # 129 

450 ) 

451 key_events = [ 

452 (e.KeyPress, 'key-press'), # 6 

453 (e.KeyRelease, 'key-release'), # 7 

454 (e.Shortcut, 'shortcut'), # 117 

455 (e.ShortcutOverride, 'shortcut-override'), # 51 

456 ] 

457 if hasattr(e, 'InputMethodQuery'): 

458 # pylint: disable=no-member 

459 key_events.extend([ 

460 (e.InputMethodQuery, 'input-method-query'), # 207 

461 ]) 

462 layout_events = [ 

463 (e.ChildAdded, 'child-added'), # 68 

464 (e.ChildRemoved, 'child-removed'), # 71 

465 (e.DynamicPropertyChange, 'dynamic-property-change'), # 170 

466 (e.FontChange, 'font-change'), # 97 

467 (e.LayoutRequest, 'layout-request'), # 76 

468 (e.Move, 'move'), # 13 widget's position changed. 

469 (e.Resize, 'resize'), # 14 

470 (e.StyleChange, 'style-change'), # 100 

471 (e.ZOrderChange, 'z-order-change'), # 126 

472 ] 

473 if hasattr(e, 'CloseSoftwareInputPanel'): 

474 layout_events.extend([ 

475 (e.CloseSoftwareInputPanel, 'close-sip'), # 200 

476 ]) 

477 mouse_events = ( 

478 (e.MouseMove, 'mouse-move'), # 155 

479 (e.MouseButtonPress, 'mouse-press'), # 2 

480 (e.MouseButtonRelease, 'mouse-release'), # 3 

481 (e.Wheel, 'mouse-wheel'), # 31 

482 ) 

483 paint_events = [ 

484 (e.ChildPolished, 'child-polished'), # 69 

485 (e.PaletteChange, 'palette-change'), # 39 

486 (e.ParentChange, 'parent-change'), # 21 

487 (e.Paint, 'paint'), # 12 

488 (e.Polish, 'polish'), # 75 

489 (e.PolishRequest, 'polish-request'), # 74 

490 ] 

491 if hasattr(e, 'RequestSoftwareInputPanel'): 

492 paint_events.extend([ 

493 (e.RequestSoftwareInputPanel, 'sip'), # 199 

494 ]) 

495 update_events = ( 

496 (e.UpdateLater, 'update-later'), # 78 

497 (e.UpdateRequest, 'update'), # 77 

498 ) 

499 option_table = ( 

500 (traceActivate, activate_events), 

501 (traceFocus, focus_events), 

502 (traceHide, hide_events), 

503 (traceHover, hover_events), 

504 (traceKey, key_events), 

505 (traceLayout, layout_events), 

506 (traceMouse, mouse_events), 

507 (tracePaint, paint_events), 

508 (traceUpdate, update_events), 

509 ) 

510 for option, table in option_table: 

511 if option: 

512 show.extend(table) 

513 else: 

514 for n, tag in table: 

515 ignore.append(n) 

516 for val, kind in show: 

517 if self.tag in exclude_names: 

518 return 

519 if eventType == val: 

520 tag = ( 

521 obj.objectName() if hasattr(obj, 'objectName') 

522 else f"id: {id(obj)}, {obj.__class__.__name__}" 

523 ) 

524 if traceKey: 

525 g.trace( 

526 f"{kind:-25} {self.tag:-25} " 

527 f"in-state: {repr(c.k and c.k.inState()):5} obj: {tag}") 

528 return 

529 if eventType not in ignore: 

530 tag = ( 

531 obj.objectName() if hasattr(obj, 'objectName') 

532 else f"id: {id(obj)}, {obj.__class__.__name__}" 

533 ) 

534 g.trace(f"{eventType:-25} {self.tag:-25} {tag}") 

535 #@+node:ekr.20131121050226.16331: *4* filter.traceWidget 

536 def traceWidget(self, event): 

537 """Show unexpected events in unusual widgets.""" 

538 verbose = False # Not good for --trace-events 

539 e = QtCore.QEvent 

540 assert isinstance(event, QtCore.QEvent) 

541 et = event.type() 

542 # http://qt-project.org/doc/qt-4.8/qevent.html#properties 

543 ignore_d = { 

544 e.ChildAdded: 'child-added', # 68 

545 e.ChildPolished: 'child-polished', # 69 

546 e.ChildRemoved: 'child-removed', # 71 

547 e.Close: 'close', # 19 

548 e.CloseSoftwareInputPanel: 'close-software-input-panel', # 200 

549 178: 'contents-rect-change', # 178 

550 # e.DeferredDelete:'deferred-delete', # 52 (let's trace this) 

551 e.DynamicPropertyChange: 'dynamic-property-change', # 170 

552 e.FocusOut: 'focus-out', # 9 (We don't care if we are leaving an unknown widget) 

553 e.FontChange: 'font-change', # 97 

554 e.Hide: 'hide', # 18 

555 e.HideToParent: 'hide-to-parent', # 27 

556 e.HoverEnter: 'hover-enter', # 127 

557 e.HoverLeave: 'hover-leave', # 128 

558 e.HoverMove: 'hover-move', # 129 

559 e.KeyPress: 'key-press', # 6 

560 e.KeyRelease: 'key-release', # 7 

561 e.LayoutRequest: 'layout-request', # 76 

562 e.Leave: 'leave', # 11 (We don't care if we are leaving an unknown widget) 

563 # e.LeaveEditFocus:'leave-edit-focus', # 151 

564 e.MetaCall: 'meta-call', # 43 

565 e.Move: 'move', # 13 widget's position changed. 

566 e.MouseButtonPress: 'mouse-button-press', # 2 

567 e.MouseButtonRelease: 'mouse-button-release', # 3 

568 e.MouseButtonDblClick: 'mouse-button-double-click', # 4 

569 e.MouseMove: 'mouse-move', # 5 

570 e.MouseTrackingChange: 'mouse-tracking-change', # 105 

571 e.Paint: 'paint', # 12 

572 e.PaletteChange: 'palette-change', # 39 

573 e.ParentChange: 'parent-change', # 21 

574 e.Polish: 'polish', # 75 

575 e.PolishRequest: 'polish-request', # 74 

576 e.RequestSoftwareInputPanel: 'request-software-input-panel', # 199 

577 e.Resize: 'resize', # 14 

578 e.ShortcutOverride: 'shortcut-override', # 51 

579 e.Show: 'show', # 17 

580 e.ShowToParent: 'show-to-parent', # 26 

581 e.StyleChange: 'style-change', # 100 

582 e.StatusTip: 'status-tip', # 112 

583 e.Timer: 'timer', # 1 

584 e.ToolTip: 'tool-tip', # 110 

585 e.WindowBlocked: 'window-blocked', # 103 

586 e.WindowUnblocked: 'window-unblocked', # 104 

587 e.ZOrderChange: 'z-order-change', # 126 

588 } 

589 focus_d = { 

590 e.DeferredDelete: 'deferred-delete', # 52 

591 e.Enter: 'enter', # 10 

592 e.FocusIn: 'focus-in', # 8 

593 e.WindowActivate: 'window-activate', # 24 

594 e.WindowDeactivate: 'window-deactivate', # 25 

595 } 

596 line_edit_ignore_d = { 

597 e.Enter: 'enter', # 10 (mouse over) 

598 e.Leave: 'leave', # 11 (mouse over) 

599 e.FocusOut: 'focus-out', # 9 

600 e.WindowActivate: 'window-activate', # 24 

601 e.WindowDeactivate: 'window-deactivate', # 25 

602 } 

603 none_ignore_d = { 

604 e.Enter: 'enter', # 10 (mouse over) 

605 e.Leave: 'leave', # 11 (mouse over) 

606 e.FocusOut: 'focus-out', # 9 

607 e.WindowActivate: 'window-activate', # 24 

608 } 

609 if et in ignore_d: 

610 return 

611 w = QtWidgets.QApplication.focusWidget() 

612 if verbose: # Too verbose for --trace-events. 

613 for d in (ignore_d, focus_d, line_edit_ignore_d, none_ignore_d): 

614 t = d.get(et) 

615 if t: 

616 break 

617 else: 

618 t = et 

619 g.trace(f"{t:20} {w.__class__}") 

620 return 

621 if w is None: 

622 if et not in none_ignore_d: 

623 t = focus_d.get(et) or et 

624 g.trace(f"None {t}") 

625 if isinstance(w, QtWidgets.QPushButton): 

626 return 

627 if isinstance(w, QtWidgets.QLineEdit): 

628 if et not in line_edit_ignore_d: 

629 t = focus_d.get(et) or et 

630 if hasattr(w, 'objectName'): 

631 tag = w.objectName() 

632 else: 

633 tag = f"id: {id(w)}, {w.__class__.__name__}" 

634 g.trace(f"{t:20} {tag}") 

635 return 

636 t = focus_d.get(et) or et 

637 if hasattr(w, 'objectName'): 

638 tag = w.objectName() 

639 else: 

640 tag = f"id: {id(w)}, {w.__class__.__name__}" 

641 g.trace(f"{t:20} {tag}") 

642 #@-others 

643#@-others 

644#@@language python 

645#@@tabwidth -4 

646#@@pagewidth 70 

647#@-leo