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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

266 statements  

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 ) 

66 # f"ch: {self.ch:>7s} " 

67 # f"= {self.actual_ch!r}" 

68 # f"mods: {self.mods}, {self.mods2}, {self.mods3}\n" 

69 # f"stroke: {self.stroke!r}\n" 

70 # f"text: {self.text!r}\n" 

71 # f"toString: {self.toString!r}\n" 

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 

143 # Whatever happens, suppress all other Qt key handling. 

144 #@+node:ekr.20110605195119.16937: *4* filter.createKeyEvent 

145 def createKeyEvent(self, event, c, w, ch, binding): 

146 

147 return leoGui.LeoKeyEvent( 

148 c=self.c, 

149 char=ch, 

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

151 # But really, the binding should suffice. 

152 event=event, 

153 binding=binding, 

154 w=w, 

155 x=getattr(event, 'x', None) or 0, 

156 y=getattr(event, 'y', None) or 0, 

157 x_root=getattr(event, 'x_root', None) or 0, 

158 y_root=getattr(event, 'y_root', None) or 0, 

159 ) 

160 #@+node:ekr.20180413180751.2: *4* filter.doNonKeyEvent 

161 def doNonKeyEvent(self, event, obj): 

162 """Handle all non-key event. """ 

163 c = self.c 

164 eventType = event.type() 

165 if eventType == Type.WindowActivate: 

166 g.app.gui.onActivateEvent(event, c, obj, self.tag) 

167 elif eventType == Type.WindowDeactivate: 

168 g.app.gui.onDeactivateEvent(event, c, obj, self.tag) 

169 elif eventType == Type.FocusIn: 

170 if self.tag == 'body': 

171 c.frame.body.onFocusIn(obj) 

172 if c.frame and c.frame.top and obj is c.frame.top.lineEdit: 

173 if c.k.getStateKind() == 'getArg': 

174 c.frame.top.lineEdit.restore_selection() 

175 elif eventType == Type.FocusOut and self.tag == 'body': 

176 c.frame.body.onFocusOut(obj) 

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

178 # Return True unless we have a key event. 

179 #@+node:ekr.20180413180751.3: *4* filter.shouldIgnoreKeyEvent 

180 def shouldIgnoreKeyEvent(self, event, obj): 

181 """ 

182 Return True if we should ignore the key event. 

183 

184 Alas, QLineEdit *only* generates ev.KeyRelease on Windows, Ubuntu, 

185 so the following hack is required. 

186 """ 

187 c = self.c 

188 t = event.type() 

189 isEditWidget = (obj == c.frame.tree.edit_widget(c.p)) 

190 if isEditWidget: 

191 return t != Type.KeyRelease 

192 # QLineEdit: ignore all key events except keyRelease events. 

193 if t == Type.KeyPress: 

194 # Hack Alert! 

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

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

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

198 # it could change in the future? 

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

200 return True 

201 return False # Never ignore KeyPress events. 

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

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

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

205 return True # Ignore everything else. 

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

207 def toBinding(self, event): 

208 """ 

209 Return (binding, actual_ch): 

210 

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

212 Spelling no longer fragile. 

213 actual_ch: The insertable key, or ''. 

214 """ 

215 mods = self.qtMods(event) 

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

217 actual_ch = text or toString 

218 # 

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

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

221 return None, None, None 

222 ch = ch or toString or '' 

223 if not ch: 

224 return None, None, None 

225 # 

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

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

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

229 # 

230 # Use *ch* in the binding. 

231 # Clearer w/o f-strings. 

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

233 # 

234 # Return the tweaked *actual* char. 

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

236 # 

237 # #1933: Create lossage data. 

238 lossage = LossageData( 

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

240 return binding, actual_ch, lossage 

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

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

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

244 

245 def removeAltCtrl(mods): 

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

247 if mod in mods: 

248 mods.remove(mod) 

249 return mods 

250 

251 # 

252 # Remove Alt, Ctrl for AltGr keys. 

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

254 

255 if keynum == Key.Key_AltGr: 

256 return removeAltCtrl(mods) 

257 # 

258 # Never alter complex characters. 

259 if len(actual_ch) != 1: 

260 return mods 

261 # 

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

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

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

265 kind = self.keyboard_kind.lower() 

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

267 and actual_ch in ":;" 

268 and 'Shift' in mods 

269 and 'Alt' not in mods and 'Control' not in mods 

270 ): 

271 mods.remove('Shift') 

272 elif kind == 'us-international': 

273 pass # To do. 

274 # 

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

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

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

278 return removeAltCtrl(mods) 

279 return mods 

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

281 def doLateTweaks(self, binding, ch): 

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

283 # 

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

285 if ch == '\r': 

286 ch = '\n' 

287 if binding == 'Escape': 

288 ch = 'Escape' 

289 # 

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

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

292 if ch != binding: 

293 binding = ch 

294 return binding, ch 

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

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

297 """Replace MacOS Alt characters.""" 

298 if not g.isMac: 

299 return actual_ch, ch, mods 

300 if ch == 'Backspace': 

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

302 return '\b', ch, mods 

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

304 # Patch provided by resi147. 

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

306 mac_d = { 

307 '/': '\\', 

308 '5': '[', 

309 '6': ']', 

310 '7': '|', 

311 '8': '{', 

312 '9': '}', 

313 'e': '€', 

314 'l': '@', 

315 } 

316 if ch.lower() in mac_d: 

317 # Ignore the case. 

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

319 mods = [] 

320 return actual_ch, ch, mods 

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

322 def qtKey(self, event): 

323 """ 

324 Return the components of a Qt key event. 

325 

326 Modifiers are handled separately. 

327 

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

329 

330 keynum: event.key() 

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

332 toString: 

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

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

335 text: event.text() 

336 """ 

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

338 # 

339 # Leo 6.4: Test keynum's directly. 

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

341 keynum = event.key() 

342 if keynum in ( 

343 0x01000020, # Key_Shift 

344 0x01000021, # Key_Control 

345 0x01000022, # Key_Meta 

346 0x01000023, # Key_Alt 

347 0x01001103, # Key_AltGr 

348 0x01000024, # Key_CapsLock 

349 ): 

350 # Disallow bare modifiers. 

351 return keynum, text, toString, ch 

352 # 

353 # Compute toString and ch. 

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

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

356 # 

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

358 if toString == 'Enter': 

359 toString = 'Return' 

360 if toString == 'Esc': 

361 toString = 'Escape' 

362 try: 

363 ch = chr(keynum) 

364 except ValueError: 

365 pass 

366 return keynum, text, toString, ch 

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

368 def qtMods(self, event): 

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

370 modifiers = event.modifiers() 

371 mod_table = ( 

372 (KeyboardModifier.AltModifier, 'Alt'), 

373 (KeyboardModifier.ControlModifier, 'Control'), 

374 (KeyboardModifier.MetaModifier, 'Meta'), 

375 (KeyboardModifier.ShiftModifier, 'Shift'), 

376 (KeyboardModifier.KeypadModifier, 'KeyPad'), 

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

378 ) 

379 # pylint: disable=superfluous-parens. 

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

381 return mods 

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

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

384 def traceKeys(self, obj, event): 

385 if g.unitTesting: 

386 return 

387 e = QtCore.QEvent 

388 key_events = { 

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

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

391 e.Shortcut: 'shortcut', # 117 

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

393 } 

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

395 if kind: 

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

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

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

399 def traceEvent(self, obj, event): 

400 if g.unitTesting: 

401 return 

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

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

404 traceActivate = True 

405 traceFocus = False 

406 traceHide = False 

407 traceHover = False 

408 traceKey = False 

409 traceLayout = False 

410 traceMouse = False 

411 tracePaint = False 

412 traceUpdate = False 

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

414 eventType = event.type() 

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

416 show: List[Any] = [] 

417 ignore = [ 

418 e.MetaCall, # 43 

419 e.Timer, # 1 

420 e.ToolTip, # 110 

421 ] 

422 activate_events = ( 

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

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

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

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

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

428 ) 

429 focus_events = [ 

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

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

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

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

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

435 ] 

436 if hasattr(e, 'FocusAboutToChange'): 

437 # pylint: disable=no-member 

438 focus_events.extend([ 

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

440 ]) 

441 hide_events = ( 

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

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

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

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

446 ) 

447 hover_events = ( 

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

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

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

451 ) 

452 key_events = [ 

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

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

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

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

457 ] 

458 if hasattr(e, 'InputMethodQuery'): 

459 # pylint: disable=no-member 

460 key_events.extend([ 

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

462 ]) 

463 layout_events = [ 

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

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

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

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

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

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

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

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

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

473 ] 

474 if hasattr(e, 'CloseSoftwareInputPanel'): 

475 layout_events.extend([ 

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

477 ]) 

478 mouse_events = ( 

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

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

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

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

483 ) 

484 paint_events = [ 

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

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

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

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

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

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

491 ] 

492 if hasattr(e, 'RequestSoftwareInputPanel'): 

493 paint_events.extend([ 

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

495 ]) 

496 update_events = ( 

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

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

499 ) 

500 option_table = ( 

501 (traceActivate, activate_events), 

502 (traceFocus, focus_events), 

503 (traceHide, hide_events), 

504 (traceHover, hover_events), 

505 (traceKey, key_events), 

506 (traceLayout, layout_events), 

507 (traceMouse, mouse_events), 

508 (tracePaint, paint_events), 

509 (traceUpdate, update_events), 

510 ) 

511 for option, table in option_table: 

512 if option: 

513 show.extend(table) 

514 else: 

515 for n, tag in table: 

516 ignore.append(n) 

517 for val, kind in show: 

518 if self.tag in exclude_names: 

519 return 

520 if eventType == val: 

521 tag = ( 

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

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

524 ) 

525 if traceKey: 

526 g.trace( 

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

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

529 return 

530 if eventType not in ignore: 

531 tag = ( 

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

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

534 ) 

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

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

537 def traceWidget(self, event): 

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

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

540 e = QtCore.QEvent 

541 assert isinstance(event, QtCore.QEvent) 

542 et = event.type() 

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

544 ignore_d = { 

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

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

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

548 e.Close: 'close', # 19 

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

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

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

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

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

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

555 e.Hide: 'hide', # 18 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

572 e.Paint: 'paint', # 12 

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

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

575 e.Polish: 'polish', # 75 

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

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

578 e.Resize: 'resize', # 14 

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

580 e.Show: 'show', # 17 

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

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

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

584 e.Timer: 'timer', # 1 

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

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

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

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

589 } 

590 focus_d = { 

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

592 e.Enter: 'enter', # 10 

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

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

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

596 } 

597 line_edit_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 e.WindowDeactivate: 'window-deactivate', # 25 

603 } 

604 none_ignore_d = { 

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

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

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

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

609 } 

610 if et in ignore_d: 

611 return 

612 w = QtWidgets.QApplication.focusWidget() 

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

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

615 t = d.get(et) 

616 if t: 

617 break 

618 else: 

619 t = et 

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

621 return 

622 if w is None: 

623 if et not in none_ignore_d: 

624 t = focus_d.get(et) or et 

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

626 if isinstance(w, QtWidgets.QPushButton): 

627 return 

628 if isinstance(w, QtWidgets.QLineEdit): 

629 if et not in line_edit_ignore_d: 

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 return 

637 t = focus_d.get(et) or et 

638 if hasattr(w, 'objectName'): 

639 tag = w.objectName() 

640 else: 

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

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

643 #@-others 

644#@-others 

645#@@language python 

646#@@tabwidth -4 

647#@@pagewidth 70 

648#@-leo