leojs icon indicating copy to clipboard operation
leojs copied to clipboard

Some U.A. from Leo are unsupported in LeoJS (it writes .leo files with corrupted UAs)

Open edreamleo opened this issue 7 months ago • 2 comments

The bug

  • Change a .leo file in leoJS.
  • Open the file with (desktop) Leo.

The following crash happens for many (all?) previously visited nodes:

getDescendentUnknownAttributes Can not unpickle str PLACE HOLDER 286470300a286470310a286470320a532769735f
Traceback (most recent call last):

  File "C:\Repos\leo-editor\leo\core\leoFileCommands.py", line 1197, in getDescendentUnknownAttributes
    val = pickle.loads(bin)  # Returns a Python object.

TypeError: unhashable type: 'dict'

Severity

These messages are annoying, but do not cause data loss.

Saving the .leo file eliminates the messages when desktop Leo next opens the file.

Workaround

On my machine I changed fc.getDescendentUnknownAttributes as follows to give the annoying message only once:


def getDescendentUnknownAttributes(self, s: str, v: VNode = None) -> Value:
    """Unhexlify and unpickle t/v.descendentUnknownAttribute field."""
    try:
        # Changed in version 3.2: Accept only bytestring or bytearray objects as input.
        s_bytes = g.toEncodedString(s)  # 2011/02/22
        # Throws a TypeError if val is not a hex string.
        bin = binascii.unhexlify(s_bytes)
        val = pickle.loads(bin)  # Returns a Python object.
        return val
    except Exception:
        # g.es_exception()
        # g.trace('Can not unpickle', s.__class__.__name__, v and v.h, s[:40])
        if v:
            g.print_unique_message(f"Can not unpickle: {v.h}")
        return None

edreamleo avatar May 28 '25 10:05 edreamleo

@edreamleo thanks!

If you can, please provide a leo file that reproduces the problem. (the one before it was saved in LeoJS)

boltex avatar May 28 '25 13:05 boltex

@boltex It appears that LeoJS corrupts any file containing a descendentVnodeUnknownAttributes attr. Here is one example:

<?xml version="1.0" encoding="utf-8"?>
<!-- Created by Leo: https://leo-editor.github.io/leo-editor/leo_toc.html -->
<leo_file xmlns:leo="https://leo-editor.github.io/leo-editor/namespaces/leo-python-editor/1.1" >
<leo_header file_format="2"/>
<globals/>
<preferences/>
<find_panel_settings/>
<vnodes>
<v t="ekr.20111002001133.2144" descendentVnodeUnknownAttributes="7d7100285803000000302e3071017d7102580b0000005f5f626f6f6b6d61726b7371037d7104580700000069735f647570657105580500000066616c7365710673735803000000302e3171077d710868037d7109580700000069735f64757065710a580500000066616c7365710b73735807000000302e322e362e30710c7d710d68037d710e580700000069735f64757065710f580500000066616c7365711073735808000000302e322e362e323471117d71125808000000616e6e6f7461746571137d71142858080000007072696f7269747971155804000000393939397116580a000000707269736574646174657117580a000000323032302d31312d3131711875735803000000302e3371197d711a68037d711b580700000069735f64757065711c580500000066616c7365711d73735806000000302e332e3133711e7d711f68037d7120580700000069735f647570657121580500000066616c7365712273735806000000302e332e313471237d712468037d7125580700000069735f647570657126580500000066616c7365712773735803000000302e3871287d712968137d712a2858080000007072696f72697479712b580400000039393939712c580a00000070726973657464617465712d580a000000323032312d30332d3330712e7573752e"><vh>Startup</vh>
<v t="ekr.20170713105049.6" descendentVnodeUnknownAttributes="7d710058010000003071017d7102580b0000005f5f626f6f6b6d61726b7371037d7104580700000069735f647570657105580500000066616c736571067373732e"><vh>#@persistence</vh></v>
<v t="ekr.20111002001133.2168" descendentVnodeUnknownAttributes="7d71002858010000003071017d7102580b0000005f5f626f6f6b6d61726b7371037d7104580700000069735f647570657105580500000066616c7365710673735808000000302e31302e362e3071077d710868037d7109580700000069735f64757065710a580500000066616c7365710b73735809000000302e31302e362e3234710c7d710d5808000000616e6e6f74617465710e7d710f2858080000007072696f7269747971105804000000393939397111580a000000707269736574646174657112580a000000323032302d31312d313171137573752e"><vh>@settings</vh>
<v t="ekr.20250321170720.1"><vh>@bool allow-script-return-result = True</vh></v>
<v t="ekr.20230317072300.1"><vh>@bool allow-text-zoom = True</vh></v>
<v t="ekr.20210306054745.1"><vh>@bool check-python-code-on-write = True</vh></v>
<v t="ekr.20200518064548.1"><vh>@bool use-german-keyboard = False</vh></v>
<v t="ekr.20230327032043.1"><vh>@bool use-mouse-expand-gestures = False</vh></v>
<v t="ekr.20210531084324.1"><vh>@data exec-script-commands</vh></v>
<v t="ekr.20210531094929.1"><vh>@data exec-script-patterns</vh></v>
<v t="ekr.20160316094834.1"><vh>@data history-list</vh></v>
<v t="ekr.20210414174148.1"><vh>@rclick say-hi @args=say hi</vh></v>
<v t="ekr.20240726151728.1"><vh>@string qt-layout-name = legacy</vh></v>
<v t="ekr.20140918130403.2193" descendentVnodeUnknownAttributes="7d7100285805000000302e362e3071017d7102580b0000005f5f626f6f6b6d61726b7371037d7104580700000069735f647570657105580500000066616c7365710673735806000000302e362e323471077d71085808000000616e6e6f7461746571097d710a2858080000007072696f72697479710b580400000039393939710c580a00000070726973657464617465710d580a000000323032302d31312d3131710e7573752e"><vh>Buttons &amp; commands</vh>
<v t="ekr.20171218081254.1"><vh>#@button count-children</vh></v>
<v t="ekr.20220704210149.1"><vh>#@button print-gnx @key=f6</vh></v>
<v t="ekr.20171022115210.1"><vh>#@button show-uas</vh></v>
<v t="ekr.20230618112751.1"><vh>#@button take-screen-shot</vh></v>
<v t="ekr.20241012032943.1"><vh>#@command test @key=f5</vh></v>
<v t="ekr.20170411194404.1"><vh>@button backup</vh></v>
<v t="ekr.20150402122915.1" descendentVnodeUnknownAttributes="7d7100285803000000302e3071017d7102580b0000005f5f626f6f6b6d61726b7371037d7104580700000069735f647570657105580500000066616c7365710673735804000000302e323471077d71085808000000616e6e6f7461746571097d710a2858080000007072696f72697479710b580400000039393939710c580a00000070726973657464617465710d580a000000323032302d31312d3131710e7573752e"><vh>@ignore Disabled buttons</vh>
<v t="ekr.20190515192551.1" descendentVnodeUnknownAttributes="7d710058010000003071017d7102580b0000005f5f626f6f6b6d61726b7371037d7104580700000069735f647570657105580500000066616c736571067373732e"><vh>@button adoc</vh></v>
<v t="ekr.20131017180426.2539"><vh>@button clean-text</vh></v>
<v t="ekr.20150511065348.1"><vh>@button create decorators</vh>
<v t="ekr.20150511065348.2"><vh>create_d</vh></v>
<v t="ekr.20150511065348.3"><vh>create_decorator</vh></v>
<v t="ekr.20150511065348.4"><vh>create_decorators</vh></v>
<v t="ekr.20150511065348.5"><vh>create_fixups</vh></v>
<v t="ekr.20150511065348.6"><vh>find_class</vh></v>
<v t="ekr.20150511065348.7"><vh>find_next_clone</vh></v>
<v t="ekr.20150511065348.8"><vh>munge_lines</vh></v>
<v t="ekr.20150511065348.9"><vh>run</vh></v>
</v>
<v t="ekr.20170301014406.1"><vh>@button cycling syntax coloring</vh></v>
<v t="ekr.20180407182938.1"><vh>@button dump uA</vh></v>
<v t="ekr.20241110171219.1"><vh>@button dump-state-dict</vh></v>
<v t="ekr.20170610081535.1"><vh>@button expand-section</vh></v>
<v t="ekr.20200322213121.1"><vh>@button graalvm</vh></v>
<v t="ekr.20160517122729.1"><vh>@button introspect</vh></v>
<v t="ekr.20111002001214.6923"><vh>@button join-path</vh></v>
<v t="ekr.20231123111324.1"><vh>@button load-readme</vh></v>
<v t="ekr.20150511065408.1"><vh>@button make-decorators2</vh>
<v t="ekr.20150511065408.2"><vh>create_d</vh></v>
<v t="ekr.20150511065408.3"><vh>create_decorator</vh></v>
<v t="ekr.20150511065408.4"><vh>create_decorators V2</vh></v>
<v t="ekr.20150511065408.5"><vh>define_s</vh></v>
<v t="ekr.20150511065408.6"><vh>munge_lines</vh></v>
<v t="ekr.20150511065408.7"><vh>run V2</vh></v>
</v>
<v t="ekr.20180327111918.1"><vh>@button munge</vh></v>
<v t="ekr.20141021085632.2299"><vh>@button print-gnx</vh></v>
<v t="ekr.20140918125647.2189"><vh>@button print-ss</vh></v>
<v t="ekr.20160304051536.1"><vh>@button print-ua</vh></v>
<v t="ekr.20200303124744.1"><vh>@button proto</vh></v>
<v t="ekr.20200303123433.1"><vh>@button qt-demo</vh></v>
<v t="ekr.20170629162056.1"><vh>@button rclick-test</vh>
<v t="ekr.20180124064122.1"><vh>@rclick hi</vh></v>
</v>
<v t="ekr.20181028130924.1"><vh>@button reload-leowapp</vh></v>
<v t="ekr.20180926104417.1"><vh>@button save-clipboard</vh></v>
<v t="ekr.20180926061406.1"><vh>@button screen-shot @key=ctrl-1</vh></v>
<v t="ekr.20141021100322.2315"><vh>@button set-ua</vh></v>
<v t="ekr.20111002001214.6922"><vh>@button split-path</vh></v>
<v t="ekr.20201109162524.1" descendentVnodeUnknownAttributes="7d710058010000003071017d71025808000000616e6e6f7461746571037d71042858080000007072696f7269747971055804000000393939397106580a000000707269736574646174657107580a000000323032302d31312d313171087573732e"><vh>@button test-undo-body</vh></v>
<v t="ekr.20201110091730.1"><vh>@button test-undo-head</vh></v>
<v t="ekr.20150501042918.1"><vh>@button timeit</vh></v>
<v t="ekr.20131121085011.1751"><vh>@button toggle-debug-app</vh></v>
<v t="ekr.20160531065801.1"><vh>@button unit-tests</vh></v>
<v t="ekr.20140913151035.3604"><vh>@command test @key=Ctrl-F7</vh></v>
</v>
</v>
<v t="ekr.20250329085219.1"><vh>Chapters</vh>
<v t="ekr.20250329085219.2"><vh>@bool use-chapter-tabs = True</vh></v>
<v t="ekr.20250329085219.3"><vh>@bool use-chapters = True</vh></v>
<v t="ekr.20250329085219.4"><vh>@bool chapter-dropdown-left = False</vh></v>
</v>
<v t="ekr.20250329085342.1"><vh>@chapter Chapter 1</vh>
<v t="ekr.20250329085411.1"><vh>Chapter 1</vh></v>
</v>
<v t="ekr.20250329085349.1"><vh>@chapter Chapter 2</vh>
<v t="ekr.20250329085443.1"><vh>Chapter 2</vh>
<v t="ekr.20250331190146.1"><vh>Chapter 2, child 1</vh></v>
</v>
</v>
</v>
<v t="ekr.20140918130403.2193" descendentVnodeUnknownAttributes="7d7100285805000000302e362e3071017d7102580b0000005f5f626f6f6b6d61726b7371037d7104580700000069735f647570657105580500000066616c7365710673735806000000302e362e323471077d71085808000000616e6e6f7461746571097d710a2858080000007072696f72697479710b580400000039393939710c580a00000070726973657464617465710d580a000000323032302d31312d3131710e7573752e"></v>
<v t="ekr.20180126175239.1" descendentVnodeUnknownAttributes="7d71002858010000003071017d7102580b0000005f5f626f6f6b6d61726b7371037d7104580700000069735f647570657105580500000066616c7365710673735804000000302e313371077d710868037d7109580700000069735f64757065710a580500000066616c7365710b73735804000000302e3134710c7d710d68037d710e580700000069735f64757065710f580500000066616c736571107373752e"><vh>Scripts</vh>
<v t="ekr.20150115062912.1"><vh> Recursive import script</vh>
<v t="ekr.20240208210018.1"><vh>&lt;&lt; rust dir_list &gt;&gt;</vh></v>
</v>
<v t="ekr.20130422173617.9359"><vh>script: check syntax of all docutils files</vh></v>
<v t="ekr.20150302052400.3"><vh>script: clean ekr-spellpyx.txt</vh></v>
<v t="ekr.20180211124129.1"><vh>script: compare LeoDocs.leo</vh></v>
<v t="ekr.20221022090126.1"><vh>script: delete the "Settings" tab</vh></v>
<v t="ekr.20160811105733.1"><vh>script: Filter New Trier class list</vh></v>
<v t="ekr.20180126051628.1"><vh>script: get issues</vh></v>
<v t="ekr.20180126053155.1"><vh>script: get user tips</vh></v>
<v t="ekr.20240302052316.1"><vh>script: import to .txt files</vh></v>
<v t="ekr.20130115100852.9016"><vh>script: insert icon</vh></v>
<v t="ekr.20160926055309.1"><vh>script: massage book for rst</vh></v>
<v t="ekr.20231023035556.1"><vh>script: remove copyright</vh></v>
<v t="ekr.20111016205745.5062"><vh>script: rename picture files</vh></v>
<v t="ekr.20180126174551.1" descendentVnodeUnknownAttributes="7d710058010000003071017d7102580b0000005f5f626f6f6b6d61726b7371037d7104580700000069735f647570657105580500000066616c736571067373732e"><vh>script: render latex formula</vh></v>
<v t="ekr.20190602065922.1" descendentVnodeUnknownAttributes="7d710058010000003071017d7102580b0000005f5f626f6f6b6d61726b7371037d7104580700000069735f647570657105580500000066616c736571067373732e"><vh>script: split body</vh></v>
<v t="ekr.20180228080856.1"><vh>script: switch between pytest code and test</vh></v>
<v t="ekr.20210724082245.1"><vh>script: update ~/SlideShow</vh>
<v t="ekr.20210724085930.1"><vh>&lt;&lt; other directories &gt;&gt;</vh></v>
</v>
<v t="ekr.20241207020428.1"><vh>Test scripts</vh>
<v t="ekr.20240624052855.1"><vh>test-script: Add red frame (works)</vh>
<v t="ekr.20240624053504.1"><vh>helpers</vh></v>
</v>
<v t="ekr.20240521150404.1"><vh>test-script: detach VR pane (works)</vh></v>
<v t="ekr.20240518074638.1"><vh>test-script: insert in body (works)</vh>
<v t="ekr.20240518074638.2"><vh>helpers</vh></v>
</v>
<v t="ekr.20240517034559.1"><vh>test-script: insert in main splitter (works)</vh>
<v t="ekr.20240518073948.1"><vh>helpers</vh></v>
</v>
</v>
</v>
<v t="ekr.20220327060129.1"><vh>--- recent settings</vh>
<v t="ekr.20140907200120.2073"><vh>@bool qt-use-scintilla = False</vh></v>
<v t="ekr.20220913071804.2"><vh>@bool tree-declutter = False</vh></v>
<v t="ekr.20161206100037.1"><vh>@string target-language = python</vh></v>
</v>
<v t="ekr.20150115062912.1"></v>
<v t="ekr.20160316094834.1"></v>
<v t="ekr.20240726151728.1"></v>
<v t="ekr.20200316054710.1" descendentVnodeUnknownAttributes="7d710058010000003071017d71025808000000616e6e6f7461746571037d71042858080000007072696f7269747971055804000000393939397106580a000000707269736574646174657107580a000000323032312d30332d333071087573732e"><vh>@enabled-plugins</vh></v>
</v>
<v t="ekr.20250528090321.1"><vh>A node</vh></v>
</vnodes>
<tnodes>
<t tx="ekr.20111002001133.2144"></t>
<t tx="ekr.20111002001133.2168" __bookmarks="7d7100580700000069735f647570657101580500000066616c73657102732e">@language rest
@wrap

The @settings tree contains all active settings. 

Settings outside this tree have no effect.</t>
<t tx="ekr.20111002001214.6922">'''@button (ekr.leo) Split the body text at semicolons'''
# No longer needed on Windows 10
w = c.frame.body.widget
p.b = '\n'.join(p.b.split(';'))
c.bodyWantsFocusNow()
# w.selectAllText()
</t>
<t tx="ekr.20111002001214.6923">'''@button (ekr.leo) Join the lines, with ; separators'''
# No longer needed on Windows 10
w = c.frame.body.widget
aList = [z.rstrip() for z in p.b.split('\n')]
p.b = ';'.join(aList)
c.bodyWantsFocusNow()
</t>
<t tx="ekr.20111016205745.5062">"""
Pictures are in

C:\Users\edreamleo\AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets

Copy them by hand to C:\Users\edreamleo\Pictures\WinSpotLight folder.
"""

import glob, shutil

path = r'C:\Users\edreamleo\Pictures\WinSpotLight\*'
files = glob.glob(path)

# g.printObj(sorted(files), tag='BEFORE')

for s in sorted(files):
    # Just add .jpg
    if not s.endswith('.jpg'):
        shutil.move(s,  s + '.jpg')

g.printObj(sorted(glob.glob(path)), tag='AFTER')
</t>
<t tx="ekr.20130115100852.9016"># Use delete-node-icons to remove icons.
table = (
    'edittrash.png',
    'connect_no.png',
    'error.png',
)
for icon in table:
    fn = g.os_path_finalize_join(g.app.loadDir,
        '..', 'Icons', 'Tango', '16x16', 'status', icon)
    if g.os_path_exists(fn):
        c.editCommands.insertIconFromFile(path=fn)
</t>
<t tx="ekr.20130422173617.9359"># The following files fail on Python 3 because of Python 2.x syntax for unicode characters:
# (now passes) utils/punctuation_chars.py, 
# utils/math/latex2mathml.py,
# writers/manpage.py,
# writers/latex2e/__init__.py

g.cls()
import os
path = g.os_path_finalize_join(g.app.loadDir,'..','extensions','docutils')
if g.isPython3:
    exclude = ('punctuation2.py',)
else:
    exclude = ('punctuation3.py',)
for root, dirs, files in os.walk(path):
    for fn in files:
        if fn.endswith('.py'):
            fn = g.os_path_join(root,fn)
            if not g.shortFileName(fn) in exclude:
                s,e = g.readFileIntoString(fn)
                c.testManager.checkFileSyntax(fn,s,reraise=False,suppress=False)
print('all files in leo/extensions/docutils pass')
</t>
<t tx="ekr.20131017180426.2539">'''(ekr.leo) Clean newlines from EKR text.'''
s = p.b.replace('\n\n','\nPARA\n')
s = s.replace('\n',' ')
s = s.replace(' PARA ','\n\n')
s = s.replace('  ',' ')
p.b = s
</t>
<t tx="ekr.20131121085011.1751">g.app.debug_app = not g.app.debug_app
g.app.debug_widgets = not g.app.debug_widgets
print('g.app.debug_app: %s' % g.app.debug_app)
print('g.app.debug_widgets: %s' % g.app.debug_widgets)</t>
<t tx="ekr.20140907200120.2073"></t>
<t tx="ekr.20140913151035.3604">print('Test')</t>
<t tx="ekr.20140918125647.2189">c.k.simulateCommand('print-style-sheet')</t>
<t tx="ekr.20140918130403.2193">@language python
</t>
<t tx="ekr.20141021085632.2299">'''@button (ekr.leo) print the gnx.'''
# g.cls()
print('timestamp: %s lastIndex: %s' % (g.app.nodeIndices.timeString,g.app.nodeIndices.lastIndex))
print('gnxs: -----')
for p in c.p.self_and_subtree():
    print('%s %s' % (p.v.gnx,p.h))
print('uAs: -----')
for p in c.p.self_and_subtree():
    if p.v.u:
        print('%s %s' % (p.v.u,p.h))
# print('done')
</t>
<t tx="ekr.20141021100322.2315">'''@button (ekr.leo) set the ua to 'test-head' '''

p.v.u = {'test-head':p.h}
p.setDirty()
c.setChanged(True)
c.redraw()
</t>
<t tx="ekr.20150115062912.1">@language python
"""Recursively import all python files in a directory and clean the result."""
@tabwidth -4 # For a better match.
g.cls()
&lt;&lt; rust dir_list &gt;&gt;

# dir_ = r'C:\Python\Python3.12\Lib\site-packages\jupytext'
# dir_ = r'C:\Python\Python3.12\Lib\site-packages\jupytext_config'
dir_ = r'C:\Python\Python3.13\Lib\site-packages\IPython\core\historyapp.py'

c.recursiveImport(
    dir_=dir_,
    kind = '@clean', # '@auto', '@clean', '@nosent','@file',
    recursive = True,
    safe_at_file = True,
    # '.html', '.js', '.json', '.py', '.rs', '.svg', '.ts', '.tsx']
    # '.codon', '.cpp', '.cc', '.el', '.scm',
    theTypes = ['.py',],
    verbose = True,
)
if 1:
    last = c.lastTopLevel()
    last.expand()
    if last.hasChildren():
        last.firstChild().expand()
    c.redraw(last)
print('Done')</t>
<t tx="ekr.20150302052400.3">if 1:
    fn = g.os_path_finalize_join(g.app.loadDir,'..','..','ekr-spellpyx.txt')
    with open(fn) as f:
        s = ''.join(sorted(set(g.splitLines(f.read().strip()+'\n'))))
    with open(fn,'w') as f:
        f.write(s)
    g.es('done',len(s))
else:
    p.b = ''.join(sorted(set(g.splitLines(p.b.rstrip()+'\n'))))</t>
<t tx="ekr.20150402122915.1"></t>
<t tx="ekr.20150501042918.1">c.testManager.runTimerOnNode(p,count=100000)
</t>
<t tx="ekr.20150511065348.1">g.cls()

print('===== Start =====')

class CreateDecorators:
    '''
    A class to create decorators from tables in getPublicCommands.
    
    Note: the node "Found: getPublicCommands" must exist.
    '''
    def __init__(self,c,make_changes):
        self.c = c
        self.fixups = self.create_fixups()
        self.n = 0
        self.n_fail = 0
        self.make_changes=make_changes
        self.suppress = [
            'c.frame.body and c.frame.body.addEditor',
            'cls','cloneFindParents','cycleTabFocus',
            'k and k.keyboardQuit',
            'menuShortcutPlaceHolder','removeBlankLines',
            'saveBuffersKillLeo',
        ]
    @others

CreateDecorators(c,make_changes=False).run()
</t>
<t tx="ekr.20150511065348.2">def create_d(self,lines,publicCommands):
    '''Create a dict. keys are method names; values are command names.'''
    trace = False
    if trace:
        print('')
        g.trace(publicCommands.h)
    d = {}
    for s in lines:
        aList = s.split()
        if len(aList) &gt; 2:
            aList = [aList[0],' '.join(aList[1:])]
        c_name,f_name = aList[0].strip(),aList[1].strip()
        if ' ' not in f_name:
            f_name = f_name.split('.')[-1]
        # if '(' in f_name:
            # f_name = f_name[:f_name.find('(')]
        if trace: g.trace('%45s %s' % (c_name,f_name))
        d [f_name] = c_name
    return d
</t>
<t tx="ekr.20150511065348.3">def create_decorator(self,c_name,f_name,root):
    '''
    Search root for a definition of f_name.
    If found, insert @cmd(f_name) before the definition.
    '''
    # g.trace('%45s %s' % (c_name,f_name))
    trace = False
    found = False
    decorator = "@cmd('%s')\n" % (c_name)
    for p in root.self_and_subtree():
        changed,result = False,[]
        for s in g.splitLines(p.b):
            if g.match_word(s,0,'def ' + f_name):
                if found:
                    if f_name not in self.suppress:
                        g.trace('duplicate def',f_name)
                else:
                    changed,found = True,True
                    result.append(decorator)
                    # print('%s%s' % (decorator,s))
            result.append(s)
        # if changed and self.make_changes:
            # new_body = ''.join(result)
            # # use git as our undo :-)
            # p.b = new_body
    return found
</t>
<t tx="ekr.20150511065348.4">def create_decorators(self,d,root):
    '''Create decorators for all items in d in root's tree.'''
    # print('***** %s' % root.h)
    if root.h in self.fixups:
        roots = []
        aList = self.fixups.get(root.h)
        for root2_h in aList:
            root2 = g.findNodeAnywhere(self.c,root2_h)
            if root2:
                # g.trace(root.h,'=====&gt;',root2.h)
                roots.append(root2)
            else:
                g.trace('===== not found',root2_h)
    else:
        roots = [root]
    for f_name in sorted(d.keys()):
        found = False
        for root in roots:
            c_name = d.get(f_name)
            found = self.create_decorator(c_name,f_name,root)
            if found: break
        if not found and f_name not in self.suppress:
            print('===== not found: %30s %s' % (root.h,f_name))
            self.n_fail += 1
</t>
<t tx="ekr.20150511065348.5">def create_fixups(self):
    '''
    Return a fixup dict.
    Keys are headlines for classes.
    Values are new headlines of nodes containing the actual class.
    '''
    return {
        'ChapterCommandsClass': ['class ChapterController'],
        'EditCommandsClass': [
            'EditCommandsClass',
            'class Commands',
            'class LeoQtFrame',
            'class LeoBody',
        ],
        'class SearchCommandsClass': ['class LeoFind (LeoFind.py)'],
        'KeyHandlerCommandsClass (add docstrings)': [
            'class KeyHandlerClass',
            'class AutoCompleterClass',
        ]
    }
</t>
<t tx="ekr.20150511065348.6">def find_class(self,p):
    '''Return the position of the class enclosing p.'''
    for p2 in p.parents():
        if p2.h.lower().find('class') &gt; -1 and p2.b.find('class') &gt; -1:
            return p2
    else:
        g.trace('*** no class for p.h')
        return None
</t>
<t tx="ekr.20150511065348.7">def find_next_clone(self,p):
    v = p.v
    p = p.copy()
    p.moveToThreadNext()
    wrapped = False
    while 1:
        # g.trace(p.v,p.h)
        if p and p.v == v:
            break
        elif p:
            p.moveToThreadNext()
        elif wrapped:
            break
        else:
            wrapped = True
            p = c.rootPosition()
    return p
</t>
<t tx="ekr.20150511065348.8">def munge_lines(self,root,publicCommands):
    '''Return munged lines of '''
    # print('')
    # g.trace(root.h)
    s = publicCommands.b
    i,j = s.find('{'),s.find('}')
    s = s[i+1:j]
    # print(s)
    lines = sorted([z.strip() for z in g.splitLines(s) if z.strip()])
    lines = [z for z in lines if not z.startswith('#')]
    lines = [z[:z.find('#')] if z.find('#') &gt; -1 else z for z in lines]
    lines = [z.rstrip().rstrip(',') for z in lines]
    lines = [z[1:] for z in lines]
    lines = [z.replace("':",' ') for z in lines]
    # print('\n'.join(lines))
    self.n += len(lines)
    return lines
</t>
<t tx="ekr.20150511065348.9">def run(self):
    '''Top-level code.'''
    self.n = 0
    found = g.findNodeAnywhere(c,'Found: getPublicCommands')
    assert found
    for child in found.children():
        publicCommands = self.find_next_clone(child)
        root = self.find_class(publicCommands)
        if root:
            lines = self.munge_lines(root,publicCommands)
            d = self.create_d(lines,publicCommands)
            self.create_decorators(d,root)
    print('\n%s commands %s failed' % (self.n,self.n_fail))
</t>
<t tx="ekr.20150511065408.1">g.cls()

# Changed files:
# leoApp.py
# leoAtFile.py
# leoCommands.py
# leoFileCommands.py
# leoFrame.py
# leoUndo.py
# qt_frame.py

make_changes = True
    # True, actually make the change

class CreateDecorators:
    '''
    A class to create decorators from tables in getPublicCommands.
    
    Note: the node "Found: getPublicCommands" must exist.
    '''
    def __init__(self):
        self.n = 0
        self.n_fail = 0
        self.s = self.define_s()
    @others

CreateDecorators().run()
</t>
<t tx="ekr.20150511065408.2">def create_d(self,lines):
    '''Create a dict. keys are method names; values are command names.'''
    trace = False
    d = {}
    for s in lines:
        aList = s.split()
        if len(aList) &gt; 2:
            aList = [aList[0],' '.join(aList[1:])]
        c_name,f_name = aList[0].strip(),aList[1].strip()
        if ' ' not in f_name:
            f_name = f_name.split('.')[-1]
        # if '(' in f_name:
            # f_name = f_name[:f_name.find('(')]
        if trace: g.trace('%45s %s' % (c_name,f_name))
        d [f_name] = c_name
    return d
</t>
<t tx="ekr.20150511065408.3">def create_decorator(self,c_name,f_name,root):
    '''
    Search root for a definition of f_name.
    If found, insert @cmd(f_name) before the definition.
    '''
    trace = True
    found = False
    decorator = "@cmd('%s')\n" % (c_name)
    for p in root.self_and_subtree():
        changed,result = False,[]
        for s in g.splitLines(p.b):
            if g.match_word(s,0,'def ' + f_name):
                if found:
                    if f_name not in self.suppress:
                        g.trace('duplicate def',f_name)
                else:
                    changed,found = True,True
                    result.append(decorator)
                    # print('%s%s' % (decorator,s))
            result.append(s)
        if changed and make_changes:
            new_body = ''.join(result)
            print('%40s %s' % (p.h[:40],decorator.rstrip()))
            # use git as our undo :-)
            # p.b = new_body
    return found
</t>
<t tx="ekr.20150511065408.4">def create_decorators(self,d): ### ,root):
    '''Create decorators for all items in d in root's tree.'''
    table = (
        'class Commands', # c.
        'class LeoQtFrame', # f.
        'class LeoFrame', # f.
        'class LeoApp', # g.app.
        '@file leoAtFile.py', # c.atFileCommands
        '@file leoFileCommands.py', # c.fileCommands
        'class Undoer', # c.undoer
    )
    roots = []
    for h in table:
        root = g.findNodeAnywhere(c,h)
        assert root,h
        roots.append(root)
    for f_name in sorted(d.keys()):
        found = False
        for root in roots:
            c_name = d.get(f_name)
            found = self.create_decorator(c_name,f_name,root)
            if found: break
        if not found and f_name not in self.suppress:
            print('===== not found: %s' % (f_name))
            self.n_fail += 1
</t>
<t tx="ekr.20150511065408.5"># 'check-all-python-code':      c.checkAllPythonCode,
# 'check-python-code':          c.checkPythonCode,
# 'extract-python-method':        c.extractPythonMethod,
# 'extract-section':              c.extractSection,
# 'import-at-file':               c.importAtFile,
# 'import-at-root':               c.importAtRoot,
# 'import-cweb-files':            c.importCWEBFiles,
# 'import-derived-file':          c.importDerivedFile,
# 'import-flattened-outline':     c.importFlattenedOutline,
# 'import-noweb-files':           c.importNowebFiles,
# 'mark-changed-roots':           c.markChangedRoots,
# 'mark-clones':                c.markClones,
# 'open-compare-window':        c.openCompareWindow,
# 'open-online-tutorial':       c.leoTutorial,
# 'reformat-body':              c.reformatBody, # 2013/10/02.
def define_s(self):
    return '''
'abort-edit-headline':          f.abortEditLabelCommand,
'about-leo':                    c.about,
'add-comments':                 c.addComments,     
'beautify':                     c.beautifyPythonCode,
'beautify-all':                 c.beautifyAllPythonCode,
'beautify-c':                   c.beautifyCCode,
'beautify-tree':                c.beautifyPythonTree,
'cascade-windows':              f.cascade,
'check-derived-file':           c.atFileCommands.checkDerivedFile,
'check-leo-file':               c.fileCommands.checkLeoFile,
'check-outline':                c.fullCheckOutline,
'clean-recent-files':           c.cleanRecentFiles,
'clear-recent-files':           c.clearRecentFiles,
'clone-node':                   c.clone,
'clone-node-to-last-node':      c.cloneToLastNode,
'close-window':                 c.close,
'contract-all':                 c.contractAllHeadlines,
'contract-all-other-nodes':     c.contractAllOtherNodes,
'contract-node':                c.contractNode,
'contract-or-go-left':          c.contractNodeOrGoToParent,
'contract-parent':              c.contractParent,
'convert-all-blanks':           c.convertAllBlanks,
'convert-all-tabs':             c.convertAllTabs,
'convert-blanks':               c.convertBlanks,
'convert-tabs':                 c.convertTabs,
'copy-node':                    c.copyOutline,
'copy-text':                    f.copyText,
'cut-node':                     c.cutOutline,
'cut-text':                     f.cutText,
'de-hoist':                     c.dehoist,
'delete-comments':              c.deleteComments,
'delete-node':                  c.deleteOutline,
'demote':                       c.demote,
'dump-outline':                 c.dumpOutline,
'edit-headline':                c.editHeadline,
'end-edit-headline':            f.endEditLabelCommand,
'equal-sized-panes':            f.equalSizedPanes,
'execute-script':               c.executeScript,
'exit-leo':                     g.app.onQuit,
'expand-all':                   c.expandAllHeadlines,
'expand-all-subheads':          c.expandAllSubheads,
'expand-ancestors-only':        c.expandOnlyAncestorsOfNode,
'expand-and-go-right':          c.expandNodeAndGoToFirstChild,
'expand-next-level':            c.expandNextLevel,
'expand-node':                  c.expandNode,
'expand-or-go-right':           c.expandNodeOrGoToFirstChild,
'expand-prev-level':            c.expandPrevLevel,
'expand-to-level-1':            c.expandLevel1,
'expand-to-level-2':            c.expandLevel2,
'expand-to-level-3':            c.expandLevel3,
'expand-to-level-4':            c.expandLevel4,
'expand-to-level-5':            c.expandLevel5,
'expand-to-level-6':            c.expandLevel6,
'expand-to-level-7':            c.expandLevel7,
'expand-to-level-8':            c.expandLevel8,
'expand-to-level-9':            c.expandLevel9,
'export-headlines':             c.exportHeadlines,
'extract':                      c.extract,
'extract-names':                c.extractSectionNames,
'find-next-clone':              c.findNextClone,
'flatten-outline':              c.flattenOutline,
'flatten-outline-to-node':      c.flattenOutlineToNode,
'go-back':                      c.goPrevVisitedNode,
'go-forward':                   c.goNextVisitedNode,
'goto-first-node':              c.goToFirstNode,
'goto-first-sibling':           c.goToFirstSibling,
'goto-first-visible-node':      c.goToFirstVisibleNode,
'goto-last-node':               c.goToLastNode,
'goto-last-sibling':            c.goToLastSibling,
'goto-last-visible-node':       c.goToLastVisibleNode,
'goto-next-changed':            c.goToNextDirtyHeadline,
'goto-next-clone':              c.goToNextClone,
'goto-next-history-node':       c.goToNextHistory,
'goto-next-marked':             c.goToNextMarkedHeadline,
'goto-next-node':               c.selectThreadNext,
'goto-next-sibling':            c.goToNextSibling,
'goto-next-visible':            c.selectVisNext,
'goto-parent':                  c.goToParent,
'goto-prev-history-node':       c.goToPrevHistory,
'goto-prev-node':               c.selectThreadBack,
'goto-prev-sibling':            c.goToPrevSibling,
'goto-prev-visible':            c.selectVisBack,
'hide-invisibles':              c.hideInvisibles,
'hoist':                        c.hoist,
'import-file':                  c.importAnyFile,
'indent-region':                c.indentBody,
'insert-body-time':             c.insertBodyTime,
'insert-child':                 c.insertChild,
'insert-node':                  c.insertHeadline,
'insert-node-before':           c.insertHeadlineBefore,
'mark':                         c.markHeadline,
'mark-changed-items':           c.markChangedHeadlines,
'mark-subheads':                c.markSubheads,
'match-brackets':               c.findMatchingBracket,
'minimize-all':                 f.minimizeAll,
'move-outline-down':            c.moveOutlineDown,
'move-outline-left':            c.moveOutlineLeft,
'move-outline-right':           c.moveOutlineRight,
'move-outline-up':              c.moveOutlineUp,
'new':                          c.new,
'open-cheat-sheet-leo':         c.openCheatSheet,
'open-leoDocs-leo':             c.leoDocumentation,
'open-leoPlugins-leo':          c.openLeoPlugins,
'open-leoSettings-leo':         c.openLeoSettings,
'open-local-settings':          c.selectAtSettingsNode,
'open-myLeoSettings-leo':       c.openMyLeoSettings,
'open-offline-tutorial':        f.leoHelp,
'open-online-home':             c.leoHome,
'open-online-toc':              c.openLeoTOC,
'open-online-tutorials':        c.openLeoTutorials,
'open-online-videos':           c.openLeoVideos,
'open-outline':                 c.open,
'open-python-window':           c.openPythonWindow,
'open-quickstart-leo':          c.leoQuickStart,
'open-scripts-leo':             c.openLeoScripts,
'open-users-guide':             c.openLeoUsersGuide,
'open-with':                    c.openWith,
'outline-to-cweb':              c.outlineToCWEB,
'outline-to-noweb':             c.outlineToNoweb,
'paste-node':                   c.pasteOutline,
'paste-retaining-clones':       c.pasteOutlineRetainingClones,
'paste-text':                   f.pasteText,
'pretty-print-all-python-code': c.prettyPrintAllPythonCode,
'pretty-print-python-code':     c.prettyPrintPythonCode,
'promote':                      c.promote,
'read-at-auto-nodes':           c.readAtAutoNodes,
'read-at-file-nodes':           c.readAtFileNodes,
'read-at-shadow-nodes':         c.readAtShadowNodes,
'read-file-into-node':          c.readFileIntoNode,
'read-outline-only':            c.readOutlineOnly,
'redo':                         c.undoer.redo,
'reformat-paragraph':           c.reformatParagraph,
'refresh-from-disk':            c.refreshFromDisk,
'remove-sentinels':             c.removeSentinels,
'resize-to-screen':             f.resizeToScreen,
'revert':                       c.revert,
'save-all':                     c.saveAll,
'save-file':                    c.save,
'save-file-as':                 c.saveAs,
'save-file-as-unzipped':        c.saveAsUnzipped,
'save-file-as-zipped':          c.saveAsZipped,
'save-file-to':                 c.saveTo,
'set-colors':                   c.colorPanel,
'set-font':                     c.fontPanel,
'settings':                     c.preferences,
'show-invisibles':              c.showInvisibles,
'sort-children':                c.sortChildren,
'sort-recent-files':            c.sortRecentFiles,
'sort-siblings':                c.sortSiblings,
'tangle':                       c.tangle,
'tangle-all':                   c.tangleAll,
'tangle-marked':                c.tangleMarked,
'toggle-active-pane':           f.toggleActivePane,
'toggle-angle-brackets':        c.toggleAngleBrackets,
'toggle-invisibles':            c.toggleShowInvisibles,
'toggle-sparse-move':           c.toggleSparseMove,
'toggle-split-direction':       f.toggleSplitDirection,
'undo':                         c.undoer.undo,
'unformat-paragraph':           c.unformatParagraph,
'unindent-region':              c.dedentBody,
'unmark-all':                   c.unmarkAll,
'untangle':                     c.untangle,
'untangle-all':                 c.untangleAll,
'untangle-marked':              c.untangleMarked,
'weave':                        c.weave,
'write-at-auto-nodes':          c.atFileCommands.writeAtAutoNodes,
'write-at-file-nodes':          c.fileCommands.writeAtFileNodes,
'write-at-shadow-nodes':        c.fileCommands.writeAtShadowNodes,
'write-dirty-at-auto-nodes':    c.atFileCommands.writeDirtyAtAutoNodes,
'write-dirty-at-file-nodes':    c.fileCommands.writeDirtyAtFileNodes,
'write-dirty-at-shadow-nodes':  c.fileCommands.writeDirtyAtShadowNodes,
'write-file-from-node':         c.writeFileFromNode,
'write-missing-at-file-nodes':  c.fileCommands.writeMissingAtFileNodes,
'write-outline-only':           c.fileCommands.writeOutlineOnly,
'''
</t>
<t tx="ekr.20150511065408.6">def munge_lines(self,s):
    '''Return munged lines of s. '''
    lines = sorted([z.strip() for z in g.splitLines(s) if z.strip()])
    lines = [z for z in lines if not z.startswith('#')]
    lines = [z[:z.find('#')] if z.find('#') &gt; -1 else z for z in lines]
    lines = [z.rstrip().rstrip(',') for z in lines]
    lines = [z[1:] for z in lines]
    lines = [z.replace("':",' ') for z in lines]
    self.n += len(lines)
    return lines
</t>
<t tx="ekr.20150511065408.7">def run(self):
    '''Top-level code.'''
    lines = self.munge_lines(self.s)
    d = self.create_d(lines)
    self.create_decorators(d)
    print('%s commands %s failed' % (self.n,self.n_fail))
</t>
<t tx="ekr.20160304051536.1">d = p.v.u
if d:
    for key in sorted(d):
        print('%10s %s' % (key, d.get(key)))</t>
<t tx="ekr.20160316094834.1">beautify-tree
backup
ctrl-click-at-cursor

# expand-main-splitter
# contract-main-splitter
# expand-body-pane

# import-ipynb
# exec-py-file
# find-source-for-command

# execute-script
# expand-vr-pane
# contract-body-pane
# contract-vr-pane
# contract-vr3-pane
# contract-log-pane
# parse-ipynb
# test.ipynb
# vr-toggle
# vr3-toggle

# add-editor
# beautify-files
# clone-to-at-spot
# help-for-layouts
# import-any-file
# import-to-indented-c
# import-to-indented-lisp
# import-to-indented-typescript
# merge-node-with-next-node
# merge-node-with-prev-node
# pylint
# refresh-from-disk
# reload-outline
# reload-settings
# show-layouts
# show-plugin-handlers
# stickynote
</t>
<t tx="ekr.20160517122729.1">@language python
"""Introspect"""

# By Terry Brown.  Requires Python 2.x.

# https://groups.google.com/forum/#!msg/leo-editor/Qu2HccpC_wc/_ee11jIvAQAJ

import types

sub_mode = 'instance'
# 'instance' or 'class' - controls which, instance or class names,
# are put it a subnode.  'instance class' sub-nodes both.
# '' appends classes after names, not useful.

def classname(thing):
    if hasattr(thing, '__class__'):
        return thing.__class__.__name__
    else:
        return thing.__name__

if not hasattr(c.p.v, '_introspection_target'):
    txt = g.app.gui.runAskOkCancelStringDialog(
        c, "Introspect what", "Introspect what")
    if txt is not None:
        o = eval(txt)
        c.p.v._introspection_target = o
        c.p.h = "%s %s" % (txt, classname(o))

# c.p.deletePositionsInList([i.copy() for i in p.children()])

obj = c.p.v._introspection_target
g.es(classname(obj))

def show_obj(c, obj):

    inames = sorted(dir(obj))
    
    things = {}
    instances = []
    for iname in inames:
        
        if iname.startswith('__'):
            continue
        
        o = getattr(obj, iname)
        cname = classname(o)
        instances.append((iname, o))
        things.setdefault(cname, []).append(instances[-1])

    if 'instance' in sub_mode:
        tnd = c.p.v.insertAsNthChild(0)
        tnd.h = "&lt;by name&gt;"
    else:
        tnd = c.p.v

    instances.sort()
    for iname, o in instances:
        
        if classname(o) == 'position':
            # apparently this collapses the space-time continuum?
            continue
        
        nd = tnd.insertAsLastChild()
        
        if not seen_already(tnd, nd, iname, o):
            nd.h = "%s %s" % (iname, format_type(nd, o))
            nd._introspection_target = o

    if 'class' in sub_mode:
        ttnd = c.p.v.insertAsNthChild(0)
        ttnd.h = "&lt;by class&gt;"
    else:
        ttnd = c.p.v

    for cname in sorted(things):
    
        if len(things[cname]) == 1:
            tnd = ttnd
        else:
            tnd = ttnd.insertAsLastChild()
            tnd.h = "&lt;%s&gt;"%cname
    
        for iname, o in sorted(things[cname]):
            
            if cname == 'position':
                # apparently this collapses the space-time continuum?
                continue
            
            nd = tnd.insertAsLastChild()
            if not seen_already(tnd, nd, iname, o):
                show_child(nd, iname, o)
                nd._introspection_target = o
         
def seen_already(tnd, nd, iname, o):
        
    up = tnd.parents
    while up:
        if (hasattr(up[0], '_introspection_target') and
            up[0]._introspection_target is o):
            break
        up = up[0].parents
    else:
        return False
        
    nd.h = "[%s %s]" % (classname(o), iname)
    pos = c.vnode2position(up[0])
    nd.b = pos.get_UNL(with_file=True, with_proto=True)
    
    return True
            
def show_child(nd, iname, o):
                
    nd._introspection_target = o
    nd.h = "%s %s" % (format_type(nd, o), iname)
    
docable = (
    types.ClassType, types.MethodType, types.UnboundMethodType, 
    types.BuiltinFunctionType, types.BuiltinMethodType,
)
    
def format_type(nd, o):
    
    if isinstance(o, docable):
        if hasattr(o, '__doc__'):
            nd.b = o.__doc__
    
    if isinstance(o, (str, unicode)):
        nd.b = o
        return "%s '%s'" % (classname(o), o[:20])
    elif isinstance(o, bool):
        return "%s %s" % (classname(o), 'T' if o else 'F')
    elif isinstance(o, (int, float)):
        return "%s %s" % (classname(o), o)
    elif isinstance(o, (tuple, list, dict)):
        return "%s %s" % (classname(o), len(o))
    else:
        return classname(o)
    
def show_list(c, list_):
    
    if len(list_) &gt; 100:
        nd = c.p.v.insertAsLastChild()
        nd.h = "&lt;%s of %d items truncated&gt;" % len(list_.__class__.__name__, list_)
        
    if len(list_) == 0:
        nd = c.p.v.insertAsLastChild()
        nd.h = "&lt;%s of 0 items&gt;" % list_.__class__.__name__
        
    for n, i in enumerate(list_[:100]):
        nd = c.p.v.insertAsLastChild()
        show_child(nd, '', i)
        nd.h = "%d: %s" % (n, nd.h)
        nd._introspection_target = i

def show_dict(c, dict_):
    
    if len(dict_) &gt; 100:
        nd = c.p.v.insertAsLastChild()
        nd.h = "&lt;dict of %d items truncated&gt;" % len(dict_)
        
    if len(dict_) == 0:
        nd = c.p.v.insertAsLastChild()
        nd.h = "&lt;dict of 0 items&gt;"
        
    keys = dict_.keys()
    keys.sort()
        
    for k in keys[:100]:
        nd = c.p.v.insertAsLastChild()
        i = dict_[k]
        show_child(nd, '', i)
        nd.h = "%s: %s" % (k, nd.h)
        nd._introspection_target = i

dispatch = {
    list: show_list,
    tuple: show_list,
    dict: show_dict,
}

func = dispatch.get(type(obj), show_obj)

func(c, obj)
   
c.p.expand()
c.redraw()
</t>
<t tx="ekr.20160531065801.1">g.cls()

import unittest
# import pyflakes
# print(pyflakes)
import os
import sys
path = g.os_path_finalize_join(os.curdir, '..')
print(path)
if 0:
    import importlib
    import importlib.util
    # importlib.invalidate_caches()
    pyflakes = importlib.util.spec_from_file_location("pyflakes", path)
    # importlib.import_module('pyflakes', path)

else:
    if 'pyflakes' in sys.modules:
        del sys.modules['pyflakes']
    if sys.path[0] != path:
        sys.path.insert(0, path)
    import pyflakes
print(pyflakes)
if 1:
    from pyflakes.test import test_api, test_doctests, test_imports, test_other
    from pyflakes.test import test_return_with_arguments_inside_generator
    from pyflakes.test import test_undefined_names
    tests = (
        test_api,
        test_doctests,
        test_imports, test_other,
        test_return_with_arguments_inside_generator,
        test_undefined_names,
    )
    loader = unittest.TestLoader()
    suite = unittest.TestSuite()
    for module in tests:
        suite.addTest(loader.loadTestsFromModule(module))
    unittest.TextTestRunner(verbosity=1).run(suite)</t>
<t tx="ekr.20160811105733.1">g.cls()
errors, n = 0, 0
path = r'c:\prog\NT1967masterLIST.csv'
s = open(path,'r').read()
s = g.toUnicode(s)
lines = g.splitLines(s)
print(len(lines))
for line in lines[1:]:
    line = line.strip()
    if line:
        fields = line.split(',')
        if len(fields) &gt; 2 and ('deceased' in fields[2].lower()):
            try:
                date = fields[2].lower().replace('deceased','').strip()
                # print('%4s %20s %s %s' % (fields[0], date, fields[3], fields[1]))
                print('%10s %s %s' % (date, fields[3], fields[1]))
                n += 1
            except Exception:
                print('%4s **********' % (fields[0]))
                errors += 1
if errors:
    print('%s errors' % errors)
print('%s deceased classmates' % n)
</t>
<t tx="ekr.20160926055309.1">use_tags = False
if use_tags:
    tc = c.theTagController
    assert tc, 'when use_tags is True this script requires nodetags.py plugin'
root1 = g.findNodeAnywhere(c, 'my book')
assert root1, 'my book not found'
root2 = g.findNodeAnywhere(c, 'my book (sphinx)')
if root2:
    while root2.hasChildren():
        root2.firstChild().doDelete()
else:
    last = c.lastTopLevel()
    root2 = last.insertAfter()
    root2.h = 'my book (sphinx)'
nasm = '​.. code:: nasm\n\n'
for p in root1.self_and_subtree():
    if not use_tags or use_tags and 'nasm' in tc.get_tags(p):
        print(p.h)
        p2 = root2.insertAsLastChild()
        p2.h = p.h
        p2.b = nasm + p.b
root2.expand()
c.redraw()</t>
<t tx="ekr.20161206100037.1"></t>
<t tx="ekr.20170301014406.1">@language python

'''Cycle syntax coloring when there are multiple @langauge directives in a node.'''
# Original by Terry Brown, Revised by EKR.
while not p.isRoot():
    if p.b.strip().startswith("@language "):
        lines = g.splitLines(p.b)
        words = lines[0].split()
        if len(words) &gt; 2 and words[2][0].isalpha():
            # Cycle the languages on line 1.
            line0 = '%s %s %s\n' % (words[0], ' '.join(words[2:]), words[1])
            p.b = line0 + ''.join(lines[1:])
            c.selectVisBack()
            c.selectVisNext()
            break
    p.moveToParent()
else:
    g.es("No ambiguous @language directive found")</t>
<t tx="ekr.20170411194404.1">"""
Back up this .leo file.

os.environ['LEO_BACKUP'] must be the path to an existing (writable) directory.
"""
c.backup_helper(sub_dir='ekr')
</t>
<t tx="ekr.20170610081535.1">'''take a selected region,  convert each line to a section reference.'''
# For Eric S. Johansson
current = c.p
body = c.frame.body
u,undoType = c.undoer, 'expand sections'
last = c.lastTopLevel()
wrapper = c.frame.body.wrapper
sel_1, sel_2 = wrapper.getSelectionRange()
ins = wrapper.getInsertPoint()
tab_width = c.getTabWidth(c.p)
head, lines, tail, oldSel, oldYview = c.getBodyLines()
u.beforeChangeGroup(current, undoType)
lines_3 = []
for s in lines:
    start_of_line, width_of_line = g.skip_leading_ws_with_indent(s, 0, c.tab_width)
    new_section_heading = g.angleBrackets('{}')
    l2 = s[start_of_line:].strip()
    header = new_section_heading.format(l2)
    # calculate replacement line for source material
    lines_3.append((" "*start_of_line) + l2 + "\n")
    undo_data = u.beforeInsertNode(current)
    new_node = p.insertAsLastChild()
    new_node.h = header
    new_node.b = "'''\n'''"
    u.afterInsertNode(new_node, 'Insert-Node', undo_data)
        # New
    c.redraw(current) # Selects the old node.
preserveSel = sel_1 == sel_2
if preserveSel:
    ins += tab_width
    oldSel = ins, ins
c.updateBodyPane(head, ''.join(lines_3), tail, undoType, oldSel, oldYview, preserveSel)
u.afterChangeGroup(current, undoType)
</t>
<t tx="ekr.20170629162056.1">print(p.h)
assert False</t>
<t tx="ekr.20170713105049.6" __bookmarks="7d7100580700000069735f647570657101580500000066616c73657102732e">@nosearch
</t>
<t tx="ekr.20171022115210.1">d = {
'annotate': {'priority': 2, 'prisetdate': '2017-10-22'}
   'icons': [{'yoffset': 0,
              'where': 'beforeHeadline',
              'file': 'C:\\leo.repo\\leo-editor\\leo\\Icons\\cleo\\pri2.png',
              'type': 'file',
              'xoffset': 2,
              'relPath': 'cleo\\pri2.png',
              'on': 'vnode',
              'xpad': 1,
              'cleoIcon': '1'}]
}

g.printDict(p.u)
</t>
<t tx="ekr.20171218081254.1">print(c.p.numberOfChildren())</t>
<t tx="ekr.20180124064122.1">print('hi: %s' % p.h)</t>
<t tx="ekr.20180126051628.1">g.getGitIssues(
    c = c,
    include_body=False,
    label_list = ['Bug', 'Enhancement'],
    milestone = '5.7',
)
</t>
<t tx="ekr.20180126053155.1">import leo.core.leoTips as leoTips
leoTips.make_tip_nodes(c)
</t>
<t tx="ekr.20180126174551.1" __bookmarks="7d7100580700000069735f647570657101580500000066616c73657102732e">"""Render a formula in latex to formula.png"""

# http://stackoverflow.com/questions/9818279/render-equation-to-png-file-using-python

# \theta=\theta+C(1+\theta-\beta)\sqrt{1-\theta}succ_mul

import leo.core.leoGlobals as g

from io import BytesIO as StringIO
import matplotlib.pyplot as plt
import time

def render_latex(formula, fontsize=12, dpi=300, format_='svg'):
    """Renders LaTeX formula into image.
    """
    fig = plt.figure(figsize=(0.01, 0.01))
    fig.text(0, 0, u'${}$'.format(formula), fontsize=fontsize)
    buffer_ = StringIO()
    fig.savefig(buffer_, dpi=dpi, transparent=True, format=format_, bbox_inches='tight', pad_inches=0.0)
    plt.close(fig)
    return buffer_.getvalue()
    
t1 = time.process_time()
formula = r'\theta=\theta+C(1+\theta-\beta)\sqrt{1-\theta}succ_mul'
image_bytes = render_latex(formula, fontsize=10, dpi=200, format_='png')
with open('formula.png', 'wb') as image_file:
    image_file.write(image_bytes)

g.es_print('%5.2f sec' % (time.process_time()-t1))

</t>
<t tx="ekr.20180126175239.1" __bookmarks="7d7100580700000069735f647570657101580500000066616c73657102732e"></t>
<t tx="ekr.20180211124129.1"># Note: everything is in test/compare, so no need to worry about changing files.
import leo.core.leoCompare as leoCompare
outlines = [
    'LeoDocs.leo',
    'LeoDocs-matt1.leo',
    'LeoDocs-matt2.leo'
]
paths = [g.os_path_finalize_join('c:/test/compare', z) for z in outlines]
for path in paths:
    if not g.os_path_exists(path):
        print('does not exist: %r' % path)
        break
else:
    leoCompare.CompareLeoOutlines(c).diff_list_of_files(paths,visible=True)
</t>
<t tx="ekr.20180228080856.1">"""
switch between code and test

If you're using `pytest`[1] and use the layout described below, this @button code
will jump you between a function and its test, creating the (test)function
if it doesn't already exist.  Also the tests folder and `test_foo.py` file.
It assumes use of the `active_path` plugin which headlines folders as
`/myFolder/` with body text `@path myFolder`.

This code is very heavy on assumptions, but a lot of those are driven
by pytest default behavior.

To run tests, use `python -m pytest`, as anything involving py.test is
deprecated, and for some reason `pytest` finds files but runs no tests.
Tested with pytest 3.x, note Ubuntu 16.04 seems to still be on 2.x

Assumed layout:

/tests/
    test_utils.py
        def test_add_one()...
        def test_sub_one()...
    test_gui.py
        def test_load_buttons()...
utils.py
    def add_one()...
    def sub_one()...
gui.py
    def load_buttons()...

So running this code from a button will jump you from
test_sub_one() back to sub_one() and visa versa creating any
missing parts of the hierarchy in the process.

[1] https://docs.pytest.org/en/latest/
"""

import re
tests_path = c.config.getString("pytest-path") or "tests"
info = {}
# climb up node tree collecting info.
for nd in p.self_and_parents_iter():
    definition = re.match(r'def ([^( ]*)\(', p.b)
    if definition and not info.get('func'):
        info['func'] = definition.group(1)
    if nd.h.endswith('.py') and not info.get('file'):
        info['file'] = nd.h.split()[-1].split('/')[-1]
    if nd.h.strip('/') == tests_path.strip('/'):
        info['test'] = True

nd = p.copy()

if info.get('test'):  # we started in these tests folder
    while nd.h.strip('/') != tests_path.strip('/'):
        nd = nd.parent()  # climb up to code folder
    if info.get('file'):  # find or create code file
        target = info['file'][5:]
        for sib in nd.self_and_siblings():
            if sib.h.endswith(target):
                nd = sib
                break
        else:
            nd = nd.insertAfter()
            nd.h = '@auto ' + target
    if info.get('func'):  # find or create code function
        target = info['func'][5:]
        for child in nd.children():
            if child.h == target:
                nd = child
                break
        else:
            nd = nd.insertAsLastChild()
            nd.h = target
            nd.b = 'def'  # let abbreviation build the rest
else:  # we stared in the code folder
    if info.get('func'):  # get up to file level (weak, could be deeper)
        nd.moveToParent()
    for sib in nd.self_and_siblings():  # find or create tests folder
        if sib.h.strip('/') == tests_path.strip('/'):
            nd = sib
            break
    else:
        nd = nd.insertBefore()
        nd.h = "/%s/" % tests_path.strip('/')
        nd.b = "@path %s" % tests_path
    if info.get('file'):  # find or create test file
        target = 'test_' + info['file']
        for child in nd.children():
            if child.h.endswith(target):
                nd = child
                break
        else:
            nd = nd.insertAsLastChild()
            nd.h = "@auto %s" % target
            nd.b = "import %s\n\n@others\n" % info['file'].replace('.py', '')
    if info.get('func'):  # find or create test function
        target = 'test_' + info['func']
        for child in nd.children():
            if child.h == target:
                nd = child
                break
        else:
            nd = nd.insertAsLastChild()
            nd.h = target
            nd.b = "def %s():\n    assert %s. == 'otters'\n" % (
                target, info['file'].replace('.py', ''))

c.selectPosition(nd)
c.redraw()</t>
<t tx="ekr.20180327111918.1"># Make sustitutions...
aList = [
    ('pandas', 'pd'),
    ('numpy', 'np'),
]
s = p.b
for pat1, pat2 in aList:
    s = s.replace(pat1, pat2)
# Create a new node, with the new text.
p2 = c.p.insertAfter()
p2.h = "Substitutions"
p2.b = s
c.selectPosition(p2)
c.redraw()
</t>
<t tx="ekr.20180407182938.1">g.cls()
d = p.v.u
print(p.h)
g.printDict(d)
</t>
<t tx="ekr.20180926061406.1">'''Create a screenshot of the present Leo outline and save it to .leo/screen_captures.'''
#  --window-size=682x1264 is recommended.
from leo.core.leoQt import isQt5, QtGui
import time
base = r'C:\Users\edreamleo\.leo\screen_captures'
assert g.os_path_exists(base), repr(base)
window = g.app.gui.qtApp.activeWindow()
w = window.grab() if isQt5 else QtGui.QPixmap.grabWindow(window.winID())
fn = '%s.png' % time.strftime('%Y-%m-%d-%H-%M-%S')
path = g.os_path_finalize_join(base, fn)
w.save(path, 'png')
g.es_print('saved: %s' % path)
</t>
<t tx="ekr.20180926104417.1">'''
Save an image on the clipboard to .leo/screen_captures.
Use Alt-PrintScn to copy the active window to the clipboard.
'''
import time
app = g.app.gui.qtApp
app.processEvents()
image = app.clipboard().image()
if not image.isNull():
    base = r'C:\Users\edreamleo\.leo\screen_captures'
    assert g.os_path_exists(base), repr(base)
    fn = '%s.png' % time.strftime('%Y-%m-%d-%H.%M.%S')
    path = g.os_path_finalize_join(base, fn)
    image.save(path, 'png')
    g.es_print('saved: %s' % path)
</t>
<t tx="ekr.20181028130924.1"># g.cls()
if 1:
    print('----- restarting -----')
    c.k.simulateCommand('reload-all-settings')
    import imp
    import leo.plugins.leowapp as leowapp
    imp.reload(leowapp)
    pc = g.app.pluginsController
    pc.unloadOnePlugin('leoapp', verbose=True)
    pc.loadOnePlugin('leowapp', tag='open0', verbose=True)
g.execute_shell_commands('&amp;moz http://127.0.0.1:8100/ekr.leo')
</t>
<t tx="ekr.20190515192551.1" __bookmarks="7d7100580700000069735f647570657101580500000066616c73657102732e">'''A prototype of running adoc and asciidoctor.'''
import os
p = g.findNodeAnywhere(c, r'@adoc ad_test.adoc')
assert p
paths = c.asciiDoctorCommands.adoc_command(event=g.Bunch(p=p),verbose=False)
paths = [os.path.abspath(os.path.normpath(z)) for z in paths]
input_paths = ' '.join(paths)
g.execute_shell_commands(['asciidoctor %s' % input_paths])
output_paths = ' '.join([z.replace('.adoc', '.html') for z in paths])
for path in paths:
    print(f"wrote: {path}\n")
if 0:
    # optional: open .html files in the default browser.
    g.execute_shell_commands([output_paths])
</t>
<t tx="ekr.20190602065922.1" __bookmarks="7d7100580700000069735f647570657101580500000066616c73657102732e">root = c.lastTopLevel().insertAfter()
root.h = p.h
for i, line in enumerate(g.splitLines(p.b)):
    p2 = root.insertAsLastChild()
    p2.h = 'Node %s' % i
    p2.b = line
c.redraw()
    
    </t>
<t tx="ekr.20200303123433.1">demo = r"C:\leo.repo\qt-examples\src\pyqt-official/qtdemo/qtdemo.py"
g.execute_shell_commands(rf'&amp;python "{demo}"')
</t>
<t tx="ekr.20200303124744.1">demo = r"C:\test\qt_proto.py"
g.execute_shell_commands(rf'&amp;python {demo}')
</t>
<t tx="ekr.20200316054710.1" annotate="7d71002858080000007072696f7269747971015804000000393939397102580a000000707269736574646174657103580a000000323032312d30332d33307104752e"> # Recommended plugins, from leoSettings.leo:

plugins_menu.py
contextmenu.py      # Required by the vim.py and xemacs.py plugins.
mod_scripting.py
nav_qt.py
viewrendered.py
# viewrendered3.py

### Testing

# define_qt_layouts.py
# nodetags.py
# quicksearch.py
# todo.py
# stickynotes.py

### All others.

# backlink.py
# bookmarks.py
# freewin.py
# mod_autosave.py
# quickMove.py
# screenshots.py
# settings_finder.py
# rpcalc.py
# wikiview.py
</t>
<t tx="ekr.20200322213121.1">"""
Run the Javascripot code in the node selected in the outline.
"""
from subprocess import run
graal = r"C:\apps\graalvm-ce-java11-20.0.0\languages\js\bin\js.exe"
progfile = r"C:\test\grall_input.txt"
s = c.atFileCommands.stringToString(
    p, p.b, forcePythonSentinels=False, sentinels=False)
prog = s.replace("\r\n", "\n")  # Use brute force. 
with open(progfile, 'w') as f:
    f.write(s)
cmd = f'{graal} {progfile}'
result = run(cmd, capture_output=True, text=True, encoding='utf-8')
print(result.stdout or 'no output')
</t>
<t tx="ekr.20200518064548.1">
</t>
<t tx="ekr.20201109162524.1" annotate="7d71002858080000007072696f7269747971015804000000393939397102580a000000707269736574646174657103580a000000323032302d31312d31317104752e">u = c.undoer
command = 'Uppercase Body'
bunch = u.beforeChangeBody(p)
p.b = p.b.upper()
c.setChanged()
p.setDirty()
u.afterChangeBody(p, command, bunch)
</t>
<t tx="ekr.20201110091730.1">u = c.undoer
command = 'Uppercase Head'
bunch = u.beforeChangeHead(p)
p.h = p.h.upper()
c.setChanged()
p.setDirty()
u.afterChangeHead(p, command, bunch)
</t>
<t tx="ekr.20210306054745.1"></t>
<t tx="ekr.20210414174148.1">g.cls()
print('rclick hi: %s' % c.p.h)
print('dir()', dir())
print(script_args)
print(script_gnx)</t>
<t tx="ekr.20210531084324.1"># This node contains the commands needed to execute a program in a particular language.

# Format: language-name: command

# Create a temporary file if c.p is not any kind of @&lt;file&gt; node.

# Compute the final command as follows:

# 1. If command contains &lt;FILE&gt;, replace &lt;FILE&gt; with the full path to the external file.
# 2. If command contains &lt;NO-FILE&gt;, just remove &lt;NO-FILE&gt;.
# 3. Otherwise, append the full path to the external file to the command.

go: go run . &lt;NO-FILE&gt;
python: python
rust: rustc
</t>
<t tx="ekr.20210531094929.1"># This node contains the regex pattern to determine the line number in error messages.
# Format: language-name: regex pattern
#
# Patterns must define two groups, in either order:
# One group, containing only digits, defines the line number.
# The other group defines the file name.

go: ^\s*(.*):([0-9]+):([0-9]+):.+$
python: ^\s*File "(.+)", line ([0-9]+), in .+$
rust: ^\s*--&gt; (.+):([0-9]+):([0-9]+)\s*$</t>
<t tx="ekr.20210724082245.1">g.cls()
import random
import os
import shutil
base_path = r'C:\Users\Edward~1\Edward4'
assert os.path.exists(base_path), base_path
slideshow_path = r'C:\Users\Edward~1\SlideShow'
assert os.path.exists(slideshow_path), slideshow_path
assert os.path.isdir(slideshow_path)
kinds = {}
files = []
directories = []
starting_directories_s = r"""\
main\lust
"""
&lt;&lt; other directories &gt;&gt;
starting_directories = starting_directories_s.split('\n')[1:]
# g.printObj(starting_directories)
starting_directories = [os.path.join(base_path, z) for z in starting_directories if z]
g.printObj(starting_directories)
#
# Delete all files in ~/Slideshow
for filename in os.listdir(slideshow_path):
    file_path = os.path.join(slideshow_path, filename)
    if os.path.isfile(file_path):
        os.unlink(file_path)

def do_dir(path):
    for z in os.scandir(path):
        if z.is_dir():
            directories.append(z.path)
            do_dir(z.path)
        elif z.is_file():
            ext = os.path.splitext(z.path)[1].lower()
            kinds [ext] = kinds.get(ext, 0) + 1
            if ext in ('.jpeg', '.jpg', '.bmp', '.png'):
                files.append(z.path)

# do_dir(base_path)
for dir_ in starting_directories:
    do_dir(dir_)
print(f"Found {len(files)} pictures in {len(directories)} directories")
#
# Add randomized files to ~/Slideshow.
random_files = random.sample(files, min(100, len(files)))
for f in random_files:
    shutil.copy(f, slideshow_path)
print('Updated', slideshow_path)
</t>
<t tx="ekr.20210724085930.1"># main\lust\bdsm
# main\lust\couples
# main\lust\couples\before-after
# main\lust\couples\couples-other
# main\lust\couples\couples-sex
# main\lust\couples\cum
# main\lust\couples\mutual
# main\lust\couples\vintage-gf
# main\lust\ready
# main\lust\ready\Lust-2003 &amp; earlier
# main\lust\ready\Lust-2004
# main\lust\ready\Lust-2005
# main\lust\ready\Lust-2006
# main\lust\ready\Lust-2007
# main\lust\ready\Lust-2008
# main\lust\ready\Lust-2009
# main\lust\ready\Lust-2010
# main\lust\ready\Lust-2011
# main\lust\ready\Lust-2012
# main\lust\ready\Lust-2013
# main\lust\ready\Lust-2014
# main\lust\ready\Lust-2015
# main\lust\ready\Lust-2016
# main\lust\ready\Lust-2017
# main\lust\ready\Lust-2018
# main\lust\ready\Lust-2019
# main\Lust-2020
# main\multiple-best
# main\nn
# main\nn\bikini
# main\nn\faces-for-cum
# main\nn\honeymoon
# main\nn\honeymoon\New
# main\nn\prom-single
# main\nn\swimmers
# main\selfie
# main\voy
# main\young
# others
# others\cl
# others\cl\face-clit
# others\face-cock
# others\pro
# others\vintage
</t>
<t tx="ekr.20220327060129.1"></t>
<t tx="ekr.20220704210149.1">print(p.gnx)</t>
<t tx="ekr.20220913071804.2">Set to True to enable node appearance modifications
See tree-declutter-patterns
</t>
<t tx="ekr.20221022090126.1"># show-settings creates this tab.
c.frame.log.deleteTab('Settings')</t>
<t tx="ekr.20230317072300.1"></t>
<t tx="ekr.20230327032043.1"></t>
<t tx="ekr.20230618112751.1">@language python

"""Take a screenshot and save it in ~/.leo"""

g.app.pluginsController.loadOnePlugin('leo.plugins.screenshots', verbose=True)

c.k.simulateCommand('take-global-screen-shot')
</t>
<t tx="ekr.20231023035556.1">h = '--- @edit files'
root = g.findTopLevelNode(c, h)
start_s = 'LilyPond is free software:'
end_s = 'If not, see &lt;http://www.gnu.org/licenses/&gt;.'
for p in root.children():
    s = p.b
    i = s.find(start_s)
    j = s.find(end_s)
    if -1 &lt; i &lt; j:
        s = s[:i] + s[j + len(end_s):]
        p.b = s
    else:
        print('not found', p.h)
print('done')</t>
<t tx="ekr.20231123111324.1">g.cls()

import os

file_name = g.os_path_finalize_join(g.app.loadDir, '..', '..', 'README.md')
assert os.path.exists(file_name), file_name
g.openWithFileName(file_name, old_c = c)</t>
<t tx="ekr.20240208210018.1">dir_list = (
    r'C:\Repos\RustPython\common\src',
    r'C:\Repos\RustPython\compiler\codegen\src',
    r'C:\Repos\RustPython\compiler\core\src',
    r'C:\Repos\RustPython\compiler\src',
    r'C:\Repos\RustPython\compiler\codegen\src',  # compile.rs: AST to bytecode.
    r'C:\Repos\RustPython\compiler\core\src', # bytecode.rs: implements bytecodes.
    
    r'C:\Repos\RustPython\derive\src',
    r'C:\Repos\RustPython\derive-impl\src',
    r'C:\Repos\RustPython\pylib\src',
    r'C:\Repos\RustPython\src',
    r'C:\Repos\RustPython\stdlib\src',
    r'C:\Repos\RustPython\vm\src', # compiler.rs.
    r'C:\Repos\RustPython\vm\src\stdlib', # *****ast.rs  Also, many .rs versions of stdlib.
    r'C:\Repos\RustPython\vm\src\vm',  # compile.rs.
    r'C:\Repos\RustPython\vm\src\stdlib\ast', # gen.rs automatically generated by ast/asdl_rs.py.
)
</t>
<t tx="ekr.20240302052316.1">g.cls()
import os
fn = 'README.md'
path = g.os_path_finalize_join(g.app.loadDir, '..', '..', fn)
contents = g.readFileIntoUnicodeString(path)
last = c.lastTopLevel()
p = last.insertAfter()
p.h = fn
c.importCommands.createOutline(p, ext='.txt', s=contents)
c.redraw(p)</t>
<t tx="ekr.20240517034559.1">"""
Example script to insert/hide a red frame in the main splitter.
"""

from leo.core.leoQt import QtWidgets
QFrame = QtWidgets.QFrame

@others  # Define helpers
gui = g.app.gui
red_frame = gui.find_widget_by_name(c, 'red-frame')
if red_frame:
    toggle(red_frame)
else:
    main_splitter = gui.find_widget_by_name(c, 'main_splitter')
    red_frame = make_red_frame()
    main_splitter.insertWidget(1, red_frame)
</t>
<t tx="ekr.20240518073948.1">def toggle(w: QFrame) -&gt; None:
    if w.isVisible():
        w.hide()
    else:
        w.show()
        
def make_red_frame() -&gt; QFrame:
    w = QtWidgets.QFrame()
    max_width = w.maximumSize().width()
    desired_height = 200
    w.setMinimumSize(max_width, desired_height)
    w.setStyleSheet("background: 'red';")
    w.setObjectName('red-frame')
    return w
</t>
<t tx="ekr.20240518074638.1">"""
Example script: Create or toggle a blue QSplitter containing a red QFrame.
"""
from leo.core.leoQt import QtWidgets
QFrame = QtWidgets.QFrame
QSplitter = QtWidgets.QSplitter

@others  # Define helpers

gui = g.app.gui
blue_splitter = gui.find_widget_by_name(c, 'blue-splitter')
red_frame = gui.find_widget_by_name(c, 'red-frame')
if red_frame:
    toggle(blue_splitter)
else:
    red_frame = make_red_frame()
    parent_w = gui.find_widget_by_name(c, 'bodyFrame')
    assert(parent_w)
    blue_splitter = make_blue_splitter()
    blue_splitter.setParent(parent_w)
    blue_splitter.insertWidget(0, red_frame)
    blue_splitter.show()</t>
<t tx="ekr.20240518074638.2">def toggle(w: QFrame) -&gt; None:
    if w.isVisible():
        w.hide()
    else:
        w.show()
        
def make_red_frame() -&gt; QFrame:
    w = QtWidgets.QFrame()
    max_width = w.maximumSize().width()
    desired_height = 100
    w.setMinimumSize(max_width, desired_height)
    w.setStyleSheet("background: 'red';")
    w.setObjectName('red-frame')
    return w
    
def make_blue_splitter() -&gt; QSplitter:
    w = QtWidgets.QSplitter()
    w.setStyleSheet("background: 'blue';")
    w.setObjectName('blue-splitter')
    return w
</t>
<t tx="ekr.20240521150404.1">"""
Example script to activate the VR plugin and detach its pane.
"""

gui = g.app.gui
c.doCommandByName('vr')  # Try to activate the VR plugin.
vr = gui.find_widget_by_name(c, 'viewrendered_pane')
if vr:
    vr.setParent(None)
    vr.resize(500, 400)
    vr.move(50, 50)
    vr.show()
</t>
<t tx="ekr.20240624052855.1">"""
Add a red frame to the body pane or a new splitter, depending on the main
splitter's *initial* orientation.

This script can only be run once. To see both possibilities, execute
toggle-split-orientation before running this script.
"""
from leo.core.leoQt import Orientation, QtWidgets
QFrame = QtWidgets.QFrame
QSplitter = QtWidgets.QSplitter

@others  # Define helpers

gui = g.app.gui
main_splitter = gui.find_widget_by_name(c, 'main_splitter')
red_frame = gui.find_widget_by_name(c, 'red-frame')
if red_frame:
    toggle(red_frame)
elif main_splitter.orientation() == Orientation.Vertical:
    # Share the red pane with the body pane by creating a new splitter.
    new_splitter = make_splitter()
    red_frame = make_red_frame()
    main_splitter.addWidget(new_splitter)
    body_frame = gui.find_widget_by_name(c, 'bodyFrame')
    new_splitter.addWidget(body_frame)
    new_splitter.addWidget(red_frame)
    # Give equal width to all splitter panes.
    new_splitter.setSizes([100000] * len(new_splitter.sizes()))
    main_splitter.setSizes([100000] * len(main_splitter.sizes()))
else:
    # Add the red frame in the secondary splitter.
    secondary_splitter = gui.find_widget_by_name(c, 'secondary_splitter')
    red_frame = make_red_frame()
    secondary_splitter.addWidget(red_frame)
    # Give equal width to the panes in the secondary splitter.
    secondary_splitter.setSizes([100000] * len(secondary_splitter.sizes()))
c.redraw()
c.bodyWantsFocusNow()
</t>
<t tx="ekr.20240624053504.1">def toggle(w: QFrame) -&gt; None:
    if w.isVisible():
        w.hide()
    else:
        w.show()
  
def make_blue_splitter() -&gt; QSplitter:
    w = QtWidgets.QSplitter()
    # w.setStyleSheet("background: 'blue';")
    w.setObjectName('blue-splitter')
    return w
    
def make_red_frame() -&gt; QFrame:
    w = QtWidgets.QFrame()
    max_width = w.maximumSize().width()
    desired_height = 200
    w.setMinimumSize(max_width, desired_height)
    w.setStyleSheet("background: 'red';")
    w.setObjectName('red-frame')
    return w
    
def make_splitter() -&gt; QSplitter:
    w = QtWidgets.QSplitter()
    w.setObjectName('new-splitter')
    return w
</t>
<t tx="ekr.20240726151728.1"># legacy: (default) Leo's legacy layout
# big-tree: replaces @bool big-outline-pane
# horizontal-thirds: VR &amp; VR3 panes at bottom.</t>
<t tx="ekr.20241012032943.1">print('@command test')</t>
<t tx="ekr.20241110171219.1">g.cls()
colorizer = c.frame.body.colorizer
g.printObj(colorizer.stateDict)</t>
<t tx="ekr.20241207020428.1"></t>
<t tx="ekr.20250321170720.1">True: Scripts to be executed (from @command, @button, CTRL+B from body pane, etc... ) will be wrapped in 

def main():
    &lt;script&gt;

result = main()

to allow for return values to be captured if your script tries to 'return' a value.

Example: If you have an @command my-command node that contains:

return 2+2

then you could run a script that contains

output = c.doCommandByName('my-command')
print(output) 

which would print 4.

Note: By default, script execution will return the value it may have set in a global variable named "result". (or None)</t>
<t tx="ekr.20250329085219.1"></t>
<t tx="ekr.20250329085219.2">True:  Chapter tabs appear in the outline pane.
False: Chapter tabs do not appear.</t>
<t tx="ekr.20250329085219.3">True:  Chapter commands are functional.
False: Chapter commands do not exists.

Leo shows the chapters dropdown box only if this setting is True.
</t>
<t tx="ekr.20250329085219.4">True: Position the Chapters drop-down at the left of the icon bar.</t>
<t tx="ekr.20250329085342.1"></t>
<t tx="ekr.20250329085349.1"></t>
<t tx="ekr.20250329085411.1">Chapter 1</t>
<t tx="ekr.20250329085443.1">Chapter 2</t>
<t tx="ekr.20250331190146.1"></t>
<t tx="ekr.20250528090321.1">Blah blah blah</t>
</tnodes>
</leo_file>

edreamleo avatar May 28 '25 14:05 edreamleo