Coverage for C:\leo.repo\leo-editor\leo\core\leoGui.py : 71%

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.20031218072017.3719: * @file leoGui.py
4#@@first
5"""
6A module containing the base gui-related classes.
8These classes hide the details of which gui is actually being used.
9Leo's core calls this class to allocate all gui objects.
11Plugins may define their own gui classes by setting g.app.gui.
12"""
13from leo.core import leoGlobals as g
14from leo.core import leoFrame
15 # for NullGui and StringTextWrapper.
16#@+others
17#@+node:ekr.20031218072017.3720: ** class LeoGui
18class LeoGui:
19 """The base class of all gui classes.
21 Subclasses are expected to override all do-nothing methods of this class.
22 """
23 #@+others
24 #@+node:ekr.20031218072017.3722: *3* LeoGui.__init__
25 def __init__(self, guiName):
26 """Ctor for the LeoGui class."""
27 self.active = None # Used only by qt_gui.
28 self.consoleOnly = True # True if g.es goes to console.
29 self.globalFindTabManager = None
30 self.globalFindTab = None
31 self.idleTimeClass = None
32 self.isNullGui = False
33 self.lastFrame = None
34 self.leoIcon = None
35 self.mGuiName = guiName
36 self.mainLoop = None
37 self.plainTextWidget = None
38 # For SpellTabHandler class only.
39 self.root = None
40 self.script = None
41 self.splashScreen = None
42 self.utils = None
43 # To keep pylint happy.
44 self.ScriptingControllerClass = NullScriptingControllerClass
45 #
46 # Define special keys that may be overridden is subclasses.
47 self.ignoreChars = []
48 # Keys that are always to be ignore.
49 self.FKeys = []
50 # The representation of F-keys.
51 self.specialChars = []
52 # A list of characters/keys to be handle specially.
53 #@+node:ekr.20061109212618.1: *3* LeoGui: Must be defined only in base class
54 #@+node:ekr.20110605121601.18847: *4* LeoGui.create_key_event (LeoGui)
55 def create_key_event(self, c,
56 binding=None, char=None, event=None, w=None,
57 x=None, x_root=None,
58 y=None, y_root=None,
59 ):
60 # Do not call strokeFromSetting here!
61 # For example, this would wrongly convert Ctrl-C to Ctrl-c,
62 # in effect, converting a user binding from Ctrl-Shift-C to Ctrl-C.
63 return LeoKeyEvent(c, char, event, binding, w, x, y, x_root, y_root)
64 #@+node:ekr.20031218072017.3740: *4* LeoGui.guiName
65 def guiName(self):
66 try:
67 return self.mGuiName
68 except Exception:
69 return "invalid gui name"
70 #@+node:ekr.20031218072017.2231: *4* LeoGui.setScript
71 def setScript(self, script=None, scriptFileName=None):
72 self.script = script
73 self.scriptFileName = scriptFileName
74 #@+node:ekr.20110605121601.18845: *4* LeoGui.event_generate (LeoGui)
75 def event_generate(self, c, char, shortcut, w):
76 event = self.create_key_event(c, binding=shortcut, char=char, w=w)
77 c.k.masterKeyHandler(event)
78 c.outerUpdate()
79 #@+node:ekr.20061109212618: *3* LeoGu: Must be defined in subclasses
80 #@+node:ekr.20031218072017.3725: *4* LeoGui.destroySelf
81 def destroySelf(self):
82 self.oops()
83 #@+node:ekr.20031218072017.3730: *4* LeoGui.dialogs
84 def runAboutLeoDialog(self, c, version, theCopyright, url, email):
85 """Create and run Leo's About Leo dialog."""
86 self.oops()
88 def runAskLeoIDDialog(self):
89 """Create and run a dialog to get g.app.LeoID."""
90 self.oops()
92 def runAskOkDialog(self, c, title, message=None, text="Ok"):
93 """Create and run an askOK dialog ."""
94 self.oops()
96 def runAskOkCancelNumberDialog(
97 self, c, title, message, cancelButtonText=None, okButtonText=None):
98 """Create and run askOkCancelNumber dialog ."""
99 self.oops()
101 def runAskOkCancelStringDialog(self, c, title, message, cancelButtonText=None,
102 okButtonText=None, default="", wide=False):
103 """Create and run askOkCancelString dialog ."""
104 self.oops()
106 def runAskYesNoDialog(self, c, title, message=None, yes_all=False, no_all=False):
107 """Create and run an askYesNo dialog."""
108 self.oops()
110 def runAskYesNoCancelDialog(self, c, title,
111 message=None, yesMessage="Yes", noMessage="No",
112 yesToAllMessage=None, defaultButton="Yes", cancelMessage=None,
113 ):
114 """Create and run an askYesNoCancel dialog ."""
115 self.oops()
117 def runPropertiesDialog(
118 self, title='Properties', data=None, callback=None, buttons=None):
119 """Dispay a modal TkPropertiesDialog"""
120 self.oops()
121 #@+node:ekr.20031218072017.3731: *4* LeoGui.file dialogs
122 def runOpenFileDialog(
123 self, c, title, filetypes, defaultextension, multiple=False, startpath=None):
124 """Create and run an open file dialog ."""
125 self.oops()
127 def runSaveFileDialog(self, c, title, filetypes, defaultextension):
128 """Create and run a save file dialog ."""
129 self.oops()
130 #@+node:ekr.20031218072017.3732: *4* LeoGui.panels
131 def createColorPanel(self, c):
132 """Create a color panel"""
133 self.oops()
135 def createComparePanel(self, c):
136 """Create Compare panel."""
137 self.oops()
139 def createFindTab(self, c, parentFrame):
140 """Create a find tab in the indicated frame."""
141 self.oops()
143 def createFontPanel(self, c):
144 """Create a hidden Font panel."""
145 self.oops()
147 def createLeoFrame(self, c, title):
148 """Create a new Leo frame."""
149 self.oops()
150 #@+node:ekr.20031218072017.3729: *4* LeoGui.runMainLoop
151 def runMainLoop(self):
152 """Run the gui's main loop."""
153 self.oops()
154 #@+node:ekr.20031218072017.3733: *4* LeoGui.utils
155 #@+at Subclasses are expected to subclass all of the following methods.
156 # These are all do-nothing methods: callers are expected to check for
157 # None returns.
158 # The type of commander passed to methods depends on the type of frame
159 # or dialog being created. The commander may be a Commands instance or
160 # one of its subcommanders.
161 #@+node:ekr.20031218072017.3734: *5* LeoGui.Clipboard
162 def replaceClipboardWith(self, s):
163 self.oops()
165 def getTextFromClipboard(self):
166 self.oops()
167 #@+node:ekr.20031218072017.3735: *5* LeoGui.Dialog utils
168 def attachLeoIcon(self, window):
169 """Attach the Leo icon to a window."""
170 self.oops()
172 def center_dialog(self, dialog):
173 """Center a dialog."""
174 self.oops()
176 def create_labeled_frame(
177 self, parent, caption=None, relief="groove", bd=2, padx=0, pady=0):
178 """Create a labeled frame."""
179 self.oops()
181 def get_window_info(self, window):
182 """Return the window information."""
183 self.oops()
184 #@+node:ekr.20031218072017.3737: *5* LeoGui.Focus
185 def get_focus(self, *args, **kwargs):
186 """Return the widget that has focus, or the body widget if None."""
187 self.oops()
189 def set_focus(self, commander, widget):
190 """Set the focus of the widget in the given commander if it needs to be changed."""
191 self.oops()
192 #@+node:ekr.20031218072017.3736: *5* LeoGui.Font
193 def getFontFromParams(self, family, size, slant, weight, defaultSize=12):
195 self.oops()
196 #@+node:ekr.20070212145124: *5* LeoGui.getFullVersion
197 def getFullVersion(self, c=None):
198 return 'LeoGui: dummy version'
199 #@+node:ekr.20070212070820: *5* LeoGui.makeScriptButton
200 def makeScriptButton(self, c,
201 args=None,
202 p=None,
203 script=None,
204 buttonText=None,
205 balloonText='Script Button',
206 shortcut=None,
207 bg='LightSteelBlue1',
208 define_g=True,
209 define_name='__main__',
210 silent=False,
211 ):
212 self.oops()
213 #@+node:ekr.20070228154059: *3* LeoGui: May be defined in subclasses
214 #@+node:ekr.20110613103140.16423: *4* LeoGui.dismiss_spash_screen
215 def dismiss_splash_screen(self):
216 pass # May be overridden in subclasses.
217 #@+node:tbrown.20110618095626.22068: *4* LeoGui.ensure_commander_visible
218 def ensure_commander_visible(self, c):
219 """E.g. if commanders are in tabs, make sure c's tab is visible"""
220 pass
221 #@+node:ekr.20070219084912: *4* LeoGui.finishCreate
222 def finishCreate(self):
223 # This may be overridden in subclasses.
224 pass
225 #@+node:ekr.20101028131948.5861: *4* LeoGui.killPopupMenu & postPopupMenu
226 # These definitions keep pylint happy.
228 def postPopupMenu(self, *args, **keys):
229 pass
230 #@+node:ekr.20031218072017.3741: *4* LeoGui.oops
231 def oops(self):
232 # It is not usually an error to call methods of this class.
233 # However, this message is useful when writing gui plugins.
234 if 1:
235 g.pr("LeoGui oops", g.callers(4), "should be overridden in subclass")
236 #@+node:ekr.20170612065049.1: *4* LeoGui.put_help
237 def put_help(self, c, s, short_title):
238 pass
239 #@+node:ekr.20051206103652: *4* LeoGui.widget_name (LeoGui)
240 def widget_name(self, w):
241 # First try the widget's getName method.
242 if not 'w':
243 return '<no widget>'
244 if hasattr(w, 'getName'):
245 return w.getName()
246 if hasattr(w, '_name'):
247 return w._name
248 return repr(w)
249 #@-others
250#@+node:ekr.20070228160107: ** class LeoKeyEvent
251class LeoKeyEvent:
252 """A gui-independent wrapper for gui events."""
253 #@+others
254 #@+node:ekr.20110605121601.18846: *3* LeoKeyEvent.__init__
255 def __init__(self, c, char, event, binding, w,
256 x=None, y=None, x_root=None, y_root=None
257 ):
258 """Ctor for LeoKeyEvent class."""
259 if g.isStroke(binding):
260 g.trace('***** (LeoKeyEvent) oops: already a stroke', binding, g.callers())
261 stroke = binding
262 else:
263 stroke = g.KeyStroke(binding) if binding else None
264 assert g.isStrokeOrNone(stroke), f"(LeoKeyEvent) {stroke!r} {g.callers()}"
265 if 0: # Doesn't add much.
266 if 'keys' in g.app.debug:
267 print(f"LeoKeyEvent: binding: {binding}, stroke: {stroke}, char: {char!r}")
268 self.c = c
269 self.char = char or ''
270 self.event = event # New in Leo 4.11.
271 self.stroke = stroke
272 self.w = self.widget = w
273 # Optional ivars
274 self.x = x
275 self.y = y
276 # Support for fastGotoNode plugin
277 self.x_root = x_root
278 self.y_root = y_root
279 #@+node:ekr.20140907103315.18774: *3* LeoKeyEvent.__repr__
280 def __repr__(self):
282 d = {'c': self.c.shortFileName()}
283 for ivar in ('char', 'event', 'stroke', 'w'):
284 d[ivar] = getattr(self, ivar)
285 return f"LeoKeyEvent:\n{g.objToString(d)}"
286 #@+node:ekr.20150511181702.1: *3* LeoKeyEvent.get & __getitem__
287 def get(self, attr):
288 """Compatibility with g.bunch: return an attr."""
289 return getattr(self, attr, None)
291 def __getitem__(self, attr):
292 """Compatibility with g.bunch: return an attr."""
293 return getattr(self, attr, None)
294 #@+node:ekr.20140907103315.18775: *3* LeoKeyEvent.type
295 def type(self):
296 return 'LeoKeyEvent'
297 #@-others
298#@+node:ekr.20031218072017.2223: ** class NullGui (LeoGui)
299class NullGui(LeoGui):
300 """Null gui class."""
301 #@+others
302 #@+node:ekr.20031218072017.2225: *3* NullGui.__init__
303 def __init__(self, guiName='nullGui'):
304 """ctor for the NullGui class."""
305 super().__init__(guiName)
306 self.clipboardContents = ''
307 self.focusWidget = None
308 self.script = None
309 self.lastFrame = None
310 # The outer frame, used only to set the g.app.log in runMainLoop.
311 self.isNullGui = True
312 self.idleTimeClass = g.NullObject
313 #@+node:ekr.20031218072017.3744: *3* NullGui.dialogs
314 def runAboutLeoDialog(self, c, version, theCopyright, url, email):
315 return self.simulateDialog("aboutLeoDialog", None)
317 def runAskLeoIDDialog(self):
318 return self.simulateDialog("leoIDDialog", None)
320 def runAskOkDialog(self, c, title, message=None, text="Ok"):
321 return self.simulateDialog("okDialog", "Ok")
323 def runAskOkCancelNumberDialog(self, c, title, message,
324 cancelButtonText=None,
325 okButtonText=None,
326 ):
327 return self.simulateDialog("numberDialog", -1)
329 def runAskOkCancelStringDialog(self, c, title, message,
330 cancelButtonText=None,
331 okButtonText=None,
332 default="",
333 wide=False,
334 ):
335 return self.simulateDialog("stringDialog", '')
337 def runCompareDialog(self, c):
338 return self.simulateDialog("compareDialog", '')
340 def runOpenFileDialog(self, c, title, filetypes, defaultextension,
341 multiple=False,
342 startpath=None,
343 ):
344 return self.simulateDialog("openFileDialog", None)
346 def runSaveFileDialog(self, c, title, filetypes, defaultextension):
347 return self.simulateDialog("saveFileDialog", None)
349 def runAskYesNoDialog(self, c, title,
350 message=None,
351 yes_all=False,
352 no_all=False,
353 ):
354 return self.simulateDialog("yesNoDialog", "no")
356 def runAskYesNoCancelDialog(self, c, title,
357 message=None,
358 yesMessage="Yes",
359 noMessage="No",
360 yesToAllMessage=None,
361 defaultButton="Yes",
362 cancelMessage=None,
363 ):
364 return self.simulateDialog("yesNoCancelDialog", "cancel")
366 def simulateDialog(self, key, defaultVal):
367 return defaultVal
368 #@+node:ekr.20170613101737.1: *3* NullGui.clipboard & focus
369 def get_focus(self, *args, **kwargs):
370 return self.focusWidget
372 def getTextFromClipboard(self):
373 return self.clipboardContents
375 def replaceClipboardWith(self, s):
376 self.clipboardContents = s
378 def set_focus(self, commander, widget):
379 self.focusWidget = widget
380 #@+node:ekr.20070301171901: *3* NullGui.do nothings
381 def alert(self, c, message):
382 pass
384 def attachLeoIcon(self, window):
385 pass
387 def destroySelf(self):
388 pass
390 def finishCreate(self):
391 pass
393 def getFontFromParams(self, family, size, slant, weight, defaultSize=12):
394 return g.app.config.defaultFont
396 def getIconImage(self, name):
397 return None
399 def getImageImage(self, name):
400 return None
402 def getTreeImage(self, c, path):
403 return None
405 def get_window_info(self, window):
406 return 600, 500, 20, 20
408 def onActivateEvent(self, *args, **keys):
409 pass
411 def onDeactivateEvent(self, *args, **keys):
412 pass
414 def set_top_geometry(self, w, h, x, y):
415 pass
416 #@+node:ekr.20070228155807: *3* NullGui.isTextWidget & isTextWrapper
417 def isTextWidget(self, w):
418 return True # Must be True for unit tests.
420 def isTextWrapper(self, w):
421 """Return True if w is a Text widget suitable for text-oriented commands."""
422 return w and getattr(w, 'supportsHighLevelInterface', None)
423 #@+node:ekr.20031218072017.2230: *3* NullGui.oops
424 def oops(self):
425 g.trace("NullGui", g.callers(4))
426 #@+node:ekr.20070301172456: *3* NullGui.panels
427 def createComparePanel(self, c):
428 """Create Compare panel."""
429 self.oops()
431 def createFindTab(self, c, parentFrame):
432 """Create a find tab in the indicated frame."""
433 pass # Now always done during startup.
435 def createLeoFrame(self, c, title):
436 """Create a null Leo Frame."""
437 gui = self
438 self.lastFrame = leoFrame.NullFrame(c, title, gui)
439 return self.lastFrame
440 #@+node:ekr.20031218072017.2229: *3* NullGui.runMainLoop
441 def runMainLoop(self):
442 """Run the null gui's main loop."""
443 if self.script:
444 frame = self.lastFrame
445 g.app.log = frame.log
446 self.lastFrame.c.executeScript(script=self.script)
447 else:
448 print('**** NullGui.runMainLoop: terminating Leo.')
449 # Getting here will terminate Leo.
450 #@-others
451#@+node:ekr.20080707150137.5: ** class NullScriptingControllerClass
452class NullScriptingControllerClass:
453 """A default, do-nothing class to be overridden by mod_scripting or other plugins.
455 This keeps pylint happy."""
457 def __init__(self, c, iconBar=None):
458 self.c = c
459 self.iconBar = iconBar
461 def createAllButtons(self):
462 pass
463#@+node:ekr.20171128093401.1: ** class StringCheckBox (leoGui.py)
464class StringCheckBox:
465 """Simulate a QCheckBox."""
467 def __init__(self, name, label=None):
468 self.label = label
469 self.name = name
470 self.value = True
472 def checkState(self):
473 return self.value
475 isChecked = checkState
477 def objectName(self):
478 return self.name
480 def setCheckState(self, value):
481 self.value = value
483 def toggle(self):
484 self.value = not self.value
485#@+node:ekr.20210221130549.1: ** class StringFindTabManager (leoGui.py) (new)
486class StringFindTabManager:
487 """A string-based FindTabManager class for unit tests."""
488 #@+others
489 #@+node:ekr.20210221130549.2: *3* sftm.ctor
490 #@@nobeautify
492 def __init__(self, c):
493 """Ctor for the FindTabManager class."""
494 self.c = c
495 self.entry_focus = None # Accessed directly from code(!)
496 # Find/change text boxes...
497 self.find_findbox = StringLineEdit('find_text')
498 self.find_replacebox = StringLineEdit('change_text')
499 # Check boxes...
500 self.check_box_ignore_case = StringCheckBox('ignore_case')
501 self.check_box_mark_changes = StringCheckBox('mark_changes')
502 self.check_box_mark_finds = StringCheckBox('mark_finds')
503 self.check_box_regexp = StringCheckBox('pattern_match')
504 self.check_box_search_body = StringCheckBox('search_body')
505 self.check_box_search_headline = StringCheckBox('search_headline')
506 self.check_box_whole_word = StringCheckBox('whole_word')
507 # Radio buttons...
508 self.radio_button_entire_outline = StringRadioButton('entire_outline')
509 self.radio_button_node_only = StringRadioButton('node_only')
510 self.radio_button_suboutline_only = StringRadioButton('suboutline_only')
511 # Init the default values.
512 self.init_widgets()
513 #@+node:ekr.20210221130549.5: *3* sftm.clear_focus & init_focus & set_entry_focus
514 def clear_focus(self):
515 pass
517 def init_focus(self):
518 pass
520 def set_entry_focus(self):
521 pass
522 #@+node:ekr.20210221130549.4: *3* sftm.get_settings
523 #@@nobeautify
525 def get_settings(self):
526 """
527 Return a g.bunch representing all widget values.
529 Similar to LeoFind.default_settings, but only for find-tab values.
530 """
531 return g.Bunch(
532 # Find/change strings...
533 find_text = self.find_findbox.text(),
534 change_text = self.find_replacebox.text(),
535 # Find options...
536 ignore_case = self.check_box_ignore_case.isChecked(),
537 mark_changes = self.check_box_mark_changes.isChecked(),
538 mark_finds = self.check_box_mark_finds.isChecked(),
539 node_only = self.radio_button_node_only.isChecked(),
540 pattern_match = self.check_box_regexp.isChecked(),
541 search_body = self.check_box_search_body.isChecked(),
542 search_headline = self.check_box_search_headline.isChecked(),
543 suboutline_only = self.radio_button_suboutline_only.isChecked(),
544 whole_word = self.check_box_whole_word.isChecked(),
545 )
546 #@+node:ekr.20210221130549.7: *3* sftm.init_widgets
547 def init_widgets(self):
548 """
549 Init widgets and ivars from c.config settings.
550 Create callbacks that always keep the LeoFind ivars up to date.
551 """
552 c, find = self.c, self.c.findCommands
553 # Find/change text boxes.
554 table1 = (
555 ('find_findbox', 'find_text', '<find pattern here>'),
556 ('find_replacebox', 'change_text', ''),
557 )
558 for widget_ivar, setting_name, default in table1:
559 w = getattr(self, widget_ivar)
560 s = c.config.getString(setting_name) or default
561 w.insert(s)
562 # Check boxes.
563 table2 = (
564 ('ignore_case', 'check_box_ignore_case'),
565 ('mark_changes', 'check_box_mark_changes'),
566 ('mark_finds', 'check_box_mark_finds'),
567 ('pattern_match', 'check_box_regexp'),
568 ('search_body', 'check_box_search_body'),
569 ('search_headline', 'check_box_search_headline'),
570 ('whole_word', 'check_box_whole_word'),
571 )
572 for setting_name, widget_ivar in table2:
573 w = getattr(self, widget_ivar)
574 val = c.config.getBool(setting_name, default=False)
575 setattr(find, setting_name, val)
576 if val != w.isChecked(): # Support leoInteg.
577 w.toggle()
578 # Radio buttons
579 table3 = (
580 ('node_only', 'node_only', 'radio_button_node_only'),
581 ('entire_outline', None, 'radio_button_entire_outline'),
582 ('suboutline_only', 'suboutline_only', 'radio_button_suboutline_only'),
583 )
584 for setting_name, ivar, widget_ivar in table3:
585 w = getattr(self, widget_ivar)
586 val = c.config.getBool(setting_name, default=False)
587 if ivar is not None:
588 assert hasattr(find, setting_name), setting_name
589 setattr(find, setting_name, val)
590 if val != w.isChecked():
591 w.toggle()
592 # Ensure one radio button is set.
593 if not find.node_only and not find.suboutline_only:
594 w = self.radio_button_entire_outline
595 if val != w.isChecked():
596 w.toggle()
597 #@+node:ekr.20210312122351.1: *3* sftm.set_body_and_headline_checkbox
598 def set_body_and_headline_checkbox(self):
599 """Return the search-body and search-headline checkboxes to their defaults."""
600 # #1840: headline-only one-shot
601 c = self.c
602 find = c.findCommands
603 if not find:
604 return
605 table = (
606 ('search_body', self.check_box_search_body),
607 ('search_headline', self.check_box_search_headline),
608 )
609 for setting_name, w in table:
610 val = c.config.getBool(setting_name, default=False)
611 if val != w.isChecked():
612 w.toggle()
613 #@+node:ekr.20210221130549.8: *3* sftm.set_radio_button
614 #@@nobeautify
616 def set_radio_button(self, name):
617 """Set the value of the radio buttons"""
618 d = {
619 'node-only': self.radio_button_node_only,
620 'entire-outline': self.radio_button_entire_outline,
621 'suboutline-only': self.radio_button_suboutline_only,
622 }
623 w = d.get(name)
624 if not w.isChecked():
625 w.toggle()
626 #@+node:ekr.20210221130549.3: *3* sftm.text getters/setters
627 def get_find_text(self):
628 s = self.find_findbox.text()
629 if s and s[-1] in ('\r', '\n'):
630 s = s[:-1]
631 return s
633 def get_change_text(self):
634 s = self.find_replacebox.text()
635 if s and s[-1] in ('\r', '\n'):
636 s = s[:-1]
637 return s
639 def set_find_text(self, s):
640 w = self.find_findbox
641 w.clear()
642 w.insert(s)
644 def set_change_text(self, s):
645 w = self.find_replacebox
646 w.clear()
647 w.insert(s)
648 #@+node:ekr.20210221130549.9: *3* sftm.toggle_checkbox
649 #@@nobeautify
651 def toggle_checkbox(self, checkbox_name):
652 """Toggle the value of the checkbox whose name is given."""
653 d = {
654 'ignore_case': self.check_box_ignore_case,
655 'mark_changes': self.check_box_mark_changes,
656 'mark_finds': self.check_box_mark_finds,
657 'pattern_match': self.check_box_regexp,
658 'search_body': self.check_box_search_body,
659 'search_headline': self.check_box_search_headline,
660 'whole_word': self.check_box_whole_word,
661 }
662 w = d.get(checkbox_name)
663 w.toggle()
664 #@-others
665#@+node:ekr.20170613095422.1: ** class StringGui (LeoGui)
666class StringGui(LeoGui):
667 """
668 A class representing all on-screen objects using subclasses of the
669 leoFrame.StringTextWrapper class.
670 """
671 #@+others
672 #@+node:ekr.20170613095422.7: *3* StringGui.oops
673 def oops(self):
675 g.trace("StringGui", g.callers(4))
676 #@+node:ekr.20170613114120.1: *3* StringGui.runMainLoop
677 def runMainLoop(self):
678 self.oops()
679 #@-others
680#@+node:ekr.20171128093503.1: ** class StringLineEdit (leoGui)
681class StringLineEdit:
682 """Simulate a QLineEdit."""
684 def __init__(self, name, disabled=False):
685 self.disabled = disabled
686 self.name = name
687 self.pos = 0
688 self.s = ''
690 def clear(self):
691 self.pos = 0
692 self.s = ''
694 def insert(self, s):
695 if s:
696 i = self.pos
697 self.s = self.s[:i] + s + self.s[i:]
698 self.pos += len(s)
700 def objectName(self):
701 return self.name
703 def text(self):
704 return self.s
705#@+node:ekr.20171128093602.1: ** class StringRadioButton (leoGui.py)
706class StringRadioButton:
707 """Simulate a QRadioButton."""
709 def __init__(self, name, label=None):
710 self.label = label
711 self.name = name
712 self.value = True
714 def isChecked(self):
715 return self.value
717 def objectName(self):
718 return self.name
720 def toggle(self):
721 self.value = not self.value
722#@+node:ekr.20031218072017.3742: ** class UnitTestGui (NullGui)
723class UnitTestGui(NullGui):
724 """A gui class for use by unit tests."""
725 # Presently used only by the import/export unit tests.
726 #@+others
727 #@+node:ekr.20031218072017.3743: *3* UnitTestGui.__init__
728 def __init__(self, theDict=None):
729 """ctor for the UnitTestGui class."""
730 self.oldGui = g.app.gui
731 super().__init__("UnitTestGui")
732 self.theDict = {} if theDict is None else theDict
733 g.app.gui = self
735 def destroySelf(self):
736 g.app.gui = self.oldGui
737 #@+node:ekr.20071128094234.1: *3* UnitTestGui.createSpellTab
738 def createSpellTab(self, c, spellHandler, tabName):
739 pass # This method keeps pylint happy.
740 #@+node:ekr.20111001155050.15484: *3* UnitTestGui.runAtIdle
741 if 1: # Huh?
743 def runAtIdle(self, aFunc):
744 """Run aFunc immediately for a unit test.
746 This is a kludge, but it is probably the best that can be done.
747 """
748 aFunc()
749 #@-others
750#@-others
751#@@language python
752#@@tabwidth -4
753#@@pagewidth 70
754#@-leo