Attachment clone_nav.py is the Clone Navigator. Attachment
node_visit_history.py is the Node Visit Navigator.
Clone Navigator help:
The "Clone Navigator" plugin allows you to move between any two
positions of a clone by typing just two keystrokes.
The "Clone Navigator" plugin creates a button on the icon bar, installs
command clone-nav, and assigns Alt-Shift-c to command clone-nav.
Left clicking the cloneNav button, (or Alt-Shift-C, or command
clone-nav) pops up a menu listing the paths to root of all the parent
nodes of clones of the node at the current position.
If a clone is a root, then "Clone is a root" is displayed as its
path-to-root. The path-to-root for a non-root clone is the clone's
parent's headline, followed by "<-", followed by the clone's parent's
parent's headline, etc. -- till a root node is reached. The path-to-root
for the current position is marked with a right arrow icon.
Every path-to-root has an underline marking its shortcut character (if
any is possible). Typing a shortcut character selects the menu item.
Left-clicking a menu item also selects it.
Selecting a menu item positions to the clone corresponding to the menu
item and also destroys the pop-up menu.
Doing anything other than selecting a clone menu item, also destroys the
pop-up menu. Consequently, you should never see a pop-up messsage
telling you "Selected clone position no longer exists." but this
condition is tested and reported.
-----------
Node Visit History plugin help:
The "Node Visit History" plugin allows you to go to any recently visited
node position by typing just two keystrokes.
The "Node Visit History" plugin creates the nodeVisitHist button on the
Icon Bar, installs command node-visit-history, and assigns Alt-Shift-h
to command node-visit-history.
The path-to-root for a node is the node's headline, followed by "<-",
followed by the node's parent's headline, etc. -- till a root node is
reached. The path-to-root for the current position is marked with a
right arrow icon.
Every path-to-root has an underline marking its shortcut character (if
any is possible). Typing a shortcut character selects the menu item.
Left-clicking a menu item also selects it.
Selecting a menu item positions to that node in the outline and also
destroys the pop-up menu.
Doing anything other than selecting a pop-up menu item, also destroys
the pop-up menu.
If you select a position which no longer exists because you have changed
the outline, a message box pops up saying "Selected position no longer
exists.
----------------
clone_nav.py and node_visit_history.py were created using Leo-Editor.
You can look at them using Leo-editor by using the menu command File -->
Import File. This pops up a file selection dialog which lets you
choose the file to import. The imported file is then rooted by an @file
node following the current node position.
You can add these plugins to your Leo-Editor by placing them in the same
directory with the standard Leo-Editor plugins or by placing them in any
directory in the PYTHONPATH environment variable; and then enabling them
in your myLeoSettings.leo.
I'm confident that both of these plugins will behave as you expect and
will not cause you trouble, but you obviously try them at your own risk
and you should be careful.
------------
I have tested these plugins in the following environment:
Leo-Editor Revision: 4430
Python 2.7.1, qt version 4.7.2
linux2 -- Ubuntu Studio 11.04 (natty)
-----------
Help Please
I hope that someone, who knows more than I about PyQt4 and Leo-Editor,
will tell me if I'm misusing PyQt4 or Leo-Editor.
In particular, note that I create a new pop-up menu on each button click
and I never explicitly free the discarded menus. So far as I can tell,
this does not cause a "memory leak," but it worries me.
Is there some way to assign a shortcut keystroke to a command
implemented in a plugin that allows a user to change the shortcut
keystroke assigned to the command? I don't know any way to do this. My
testing indicates that a shortcut specifier in myLeoSettings is silently
ignored if the command it assigns is defined in a plugin or is not
defined anywhere.
> I hope that someone, who knows more than I about PyQt4 and Leo-Editor,
> will tell me if I'm misusing PyQt4 or Leo-Editor.
>
> In particular, note that I create a new pop-up menu on each button click
> and I never explicitly free the discarded menus. So far as I can tell,
> this does not cause a "memory leak," but it worries me.
I don't think you need to worry about that, if there are no references
left to the thing in question the python garbage collector should
dispose of it. The problem is more often the other way around, where
certain conditions can lead to the Qt C++ object being deleted while
you still have a python reference to its wrapper. But that causes a
traceback, so no need to worry about that either.
I would consider rolling your Node-Visit-History Navigator into the
nav_qt plugin, just because they go together and we should probably
take any chance we can to limit the number of plugins, they're
multiplying like rabbits (and I'm as guilty as anyone, of course).
You could put the context menu on the arrow buttons that plugin already
provides. That's what I'd do anyway, it's up to you of course.
> Is there some way to assign a shortcut keystroke to a command
> implemented in a plugin that allows a user to change the shortcut
> keystroke assigned to the command? I don't know any way to do this. My
> testing indicates that a shortcut specifier in myLeoSettings is silently
> ignored if the command it assigns is defined in a plugin or is not
> defined anywhere.
I think if you plugin provides a command like this (at top level in
module):
@g.command('my-new-command')
def do_thing(event):
c = event.get('c')
if not c:
return
c.my_plugin_controller.do_thing()
then a binding like
my-new-command = Ctrl-B
in the @shortcuts node in the @keys node in the @settings node should
work. I have a couple of things running that way.
Also, pick command names with a common prefix:
clonenav-next
clonenav-prev
etc. - makes them easier to find with Alt-X tab expansion and keeps the namespace clean.
Cheers -Terry
> That is, my command function needs to be a method.
No problem, in the __init__ for your class, do
c._my_class_name_here = self
then
> @g.command('my-new-command')
> def do_thing(event):
>
> c = event.get('c')
> if not c:
> return
> c._my_class_name_here.do_thing()
calls the do_thing() method of your class instance.
Cheers -Terry
> Would it be safe to hash the commander and use hash(c) as the key of a
> dictionary (which would replace the list)?
I think that would work, but I don't think it's a problem just sticking
an instance variable on the commander, as long as the name is something
that is unlikely to clash with anything else.
Cheers -Terry