About layouts and their settings.

62 views
Skip to first unread message

Edward K. Ream

unread,
Aug 30, 2024, 11:00:01 AM8/30/24
to leo-editor
This Engineering Notebook post discusses issues relating to Leo's new layouts. As of today, the "devel" branch contains these PRs:

- PR #4055: Add "@string qt-layout-name" setting.
- PR #4058: retire the reload-outline command.

These PRs ensure that Leo's new layouts will work as desired for almost all Leonistas.

Changing layouts during execution

Almost nobody, myself included, needs (or wants) to change the layout of a particular outline while Leo is running. Instead, Leonistas can:

- Assign default layouts for all outlines in myLeoSettings.leo.
- Assign per-outline layouts by adding the setting in individual outlines.

Also, Leonistas can change outlines dynamically using the use-whatever-layout commands. In today's codebase, these commands execute the restart-leo command.

Changing layouts without restarting Leo

Thomas wants to change layouts without restarting Leo. This isn't an unreasonable request, but doing it is fraught. The original attempt involved adding a 'reload-outline' command, but this command is too tricky for me to fix.

Summary

Leo's codebase should satisfy almost all users' desires regarding layouts. I would like to release Leo 6.8.2 on Thursday, September 5.

I have zero interest in resurrecting the reload-outline command, but I'll consider a PR that makes it work. This PR would be part of Leo 6.8.3.

Your comments, please.

Edward

Thomas Passin

unread,
Aug 30, 2024, 2:23:33 PM8/30/24
to leo-editor
Reload outline proof of principle:

filename = c.fileName()

if len(g.app.commanders()) == 1:
    c.doCommandByName('restart-leo')
else:
    c.doCommandByName('close-window')
    g.openWithFileName(filename, old_c=c)


If there is more than one outline open, the re-opened one opens to the right of the last tab.  I think that's a minor matter, and there is probably a simple solution for it. Even LeoPyRef.leo takes no more than 3 seconds to reload on my system. I can easily live with that.

g.openWithFileName()doesn't work if there is just one outline because Leo wants to close when the last outline is closed. In the process, the database connection is closed but it needs to be open to re-open the outline (other thing might be going south, too).  Maybe there's a way to prevent Leo from trying to close if the last outline is going to be re-opened, hopefully without major surgery.

Except for the length of time that Leo takes to open even a single short outline, this script works just fine.

Thomas Passin

unread,
Aug 30, 2024, 3:16:23 PM8/30/24
to leo-editor
Reopening an outline even if it's the only one open turns out to be even simpler"

filename = c.fileName()
g.app.closeLeoWindow(c.frame, new_c=None, finish_quit = False)
g.openWithFileName(filename, old_c=c)


Edward K. Ream

unread,
Aug 30, 2024, 6:00:56 PM8/30/24
to leo-e...@googlegroups.com
On Fri, Aug 30, 2024 at 2:16 PM Thomas Passin <tbp1...@gmail.com> wrote:

Reopening an outline even if it's the only one open turns out to be even simpler"

filename = c.fileName()
g.app.closeLeoWindow(c.frame, new_c=None, finish_quit = False)
g.openWithFileName(filename, old_c=c)

This code creates a shadow outline as in #4056.

Edward

Thomas Passin

unread,
Aug 30, 2024, 6:39:16 PM8/30/24
to leo-editor
I just discovered this.  The shadow opened exactly on top of the other, so I didn't realize it was there.  That should be fixable, I think.  And if not, the previous version of the script works.

Edward K. Ream

unread,
Aug 30, 2024, 10:20:50 PM8/30/24
to leo-editor
On Friday, August 30, 2024 at 5:39:16 PM UTC-5 tbp1...@gmail.com wrote:
I just discovered this.  The shadow opened exactly on top of the other, so I didn't realize it was there.  That should be fixable, I think.  And if not, the previous version of the script works.

The previous version does not work for me. Neither branch of the "if" statement works in general.

     filename = c.fileName()
    if len(g.app.commanders()) == 1:
        # c.doCommandByName('restart-leo')
        # c.redraw()
        c.restartLeo()

    else:
        c.doCommandByName('close-window')
        g.openWithFileName(filename, old_c=c)
        c.redraw()

Edward

Thomas Passin

unread,
Aug 31, 2024, 12:27:18 AM8/31/24
to leo-editor
Hmm, I've tinkered it down to this and it's just been working on my system:

filename = c.fileName()

if len(g.app.commanders()) == 1:
    c.doCommandByName('restart-leo')

else:
    c.doCommandByName('close-window')
    g.openWithFileName(filename, old_c=c)


It may not seem to do anything if there are more than one open outlines because it's so fast. Bear in mind that this code doesn't add or change the layout setting. This command would be executed after the layout setting had been updated. This code just demonstrates how to do the reload part of the task.  You could edit the layout setting by hand before reloading to demonstrate that it works.

The first branch, the one for just one open outline, does a full restart of Leo, which I hate because it's slow and disruptive. I tried opening a new empty temporary outline, then reloading the first one, then closing the temporary one, but I can't figure out how to close the temp outline without invoking the save dialog. 

If the save dialog for closing the temp outline could be avoided, the whole thing would happen so quickly that temporary outline's brief presence wouldn't be very disruptive, especially compared with a full restart. But Leo's code for saving and closing an outline would have to be refactored, and my quick-and-dirty efforts haven't worked yet.  Well, I didn't actually refactor anything, I just pulled together the minimal bits of existing code I thought should work.  But no.

Edward K. Ream

unread,
Aug 31, 2024, 7:05:01 AM8/31/24
to leo-e...@googlegroups.com
On Fri, Aug 30, 2024 at 11:27 PM Thomas Passin <tbp1...@gmail.com> wrote:
Hmm, I've tinkered it down to this and it's just been working on my system:

Thanks for your experiments.

Bear in mind that this code doesn't add or change the layout setting. This command would be executed after the layout setting had been updated. This code just demonstrates how to do the reload part of the task.  You could edit the layout setting by hand before reloading to demonstrate that it works.

Alright.

Let's consider a separate, parallel, approach. Before the new layout code became part of Leo, you used scripts to switch between layouts, correct? As a prototype, could you revise those scripts to work with the new layout code? Perhaps there is a way to reparent and repopulate the main and secondary splitters.

Does this approach seem feasible? If the prototype works we could add official helper functions to Leo.

Edward

Thomas Passin

unread,
Aug 31, 2024, 7:56:24 AM8/31/24
to leo-editor
Short answer: yes, with a few quirks in the case of added widgets (ones that are not  VR/VR3 and RPCalc).  These widgets may become lost but still retain their resources and their event hooks.

Long answer:

My scripts can be adapted and will work as long as no other other widgets are added besides VR/VR3 and RPCalc.  Other widgets would be unknown to the code and applying a new layout would work but undoing it wouldn't work completely because the code would not know they are there. A reload would fix that, of course.

My script to restore a default layout examines the main and secondary splitters and removes any splitters that were added. That works but if one of these added splitters contain an unknown widget (again, one of VR/VR3/RPCalc) its removal might lead to some dangling python objects. This is the same problem of deleting any PyQt widget: just running deleteLater removes the C++ parts but not necessarily the Python ones. The script could be modified to also remove added layouts and other widgets.

These unknown widgets could be hidden and inactivated - that's how I deal with VR/VR3 when they aren't wanted. There needs to be a way to find them if they will be wanted for reuse in a different layout. Even if we wanted to delete them and not worry about their leaked Python parts, for an arbitrary widget we wouldn't know how to disconnect them from Leo's event hooks. So it would be better to leave hidden hidden and de-parented rather than to destroy them.

My opinion is that this is an acceptable compromise.  Only script writers will know how to add other widgets.  If they know that they can also figure out how to remove them or keep them around but hidden for reuse in another layout. Of course we would write some guidance for them.

Long run:

Create a core Widget, inheriting from QWidget, that has known methods for finding, hiding, deleting, and reusing itself.  All new plugin GUI widgets should inherit from this ancestor.  Also create core methods for dealing with these objects when creating and removing a layout. We would convert VR/VR3/RPCalc to inherit from this ancestor instead of from QWidget.  In the case of VR/VR3 we have (or used to have) the controllers[h] way to find them and impose the singleton condition. That has worked well for me and might be part of this future work.

I would be happy to take the lead in this work if you would like that.

Edward K. Ream

unread,
Aug 31, 2024, 9:05:39 AM8/31/24
to leo-e...@googlegroups.com
On Sat, Aug 31, 2024 at 6:56 AM Thomas Passin wrote:

Short answer: yes, with a few quirks in the case of added widgets (ones that are not  VR/VR3 and RPCalc).  These widgets may become lost but still retain their resources and their event hooks.

Excellent. We can deal with event handlers later, if need be.

My script to restore a default layout examines the main and secondary splitters and removes any splitters that were added. That works but if one of these added splitters contain an unknown widget (again, one of VR/VR3/RPCalc) its removal might lead to some dangling python objects. This is the same problem of deleting any PyQt widget: just running deleteLater removes the C++ parts but not necessarily the Python ones. The script could be modified to also remove added layouts and other widgets.

Alright. Again, we can deal with the complications later.

My opinion is that this is an acceptable compromise.  Only script writers will know how to add other widgets.  If they know that they can also figure out how to remove them or keep them around but hidden for reuse in another layout. Of course we would write some guidance for them.

I agree.

Long run:

Create a core Widget, inheriting from QWidget, that has known methods for finding, hiding, deleting, and reusing itself. 

I prefer not to create subclasses of gui objects unless necessary, so I would prefer these be DynamicWindow methods, but let's defer this discussion.

I would be happy to take the lead in this work if you would like that.

Thanks. Having you do the work is the fastest way forward for you. The more general approach (getting reload-outline to work properly without ever calling restart-leo) might be ideal, but we both know that way isn't as easy as it appears.

Edward

Thomas Passin

unread,
Aug 31, 2024, 9:36:03 AM8/31/24
to leo-editor
One thing to resolve is where these kinds of layout scripts should be stored and managed. It's one thing for me to have a private collection of scripts and commands, but another to make them usable and available to other Leo users, and yet another to make it easy to add new ones.

I don't favor making them part of core Leo code.  Here are some alternatives:

1. The layout commands are supplied by one or more plugins in some fashion. This might make it harder to provide custom menus, since if I understand things, settings-defined menus are created before any plugin runs;

2. The layout commands are contained in a folder, whether "plugins" or a new "layouts" folder.  The core code ingests these commands at some point where they can be used by settings to make custom menus.

3. The commands are contained in a folder and a script writer can import commands fromt that folder;

4. The commands are located in @command settings nodes in LeoSettings.leo, which also would contain definitions for a new "Layout" menu.  As with other commands, a user could add other settings commands to myLeoSettings and make custom menus with them all.

I favor # 4. My own layout scripts all reside in myLeoSettings.leo and it works very well.  It's easy to change them.  Changes to the scripts in LeoSettings.leo could be committed without touching code Leo code and LeoPyRef, which seems like a good thing.

Edward K. Ream

unread,
Sep 1, 2024, 6:56:31 AM9/1/24
to leo-e...@googlegroups.com
On Sat, Aug 31, 2024 at 8:36 AM Thomas Passin <tbp1...@gmail.com> wrote:

One thing to resolve is where these kinds of layout scripts should be stored and managed. It's one thing for me to have a private collection of scripts and commands, but another to make them usable and available to other Leo users, and yet another to make it easy to add new ones.

Let's deal with these questions after you have a set of scripts that work for you.

Edward
Reply all
Reply to author
Forward
0 new messages