Run @button's in ileo, and tab completion

7 views
Skip to first unread message

Ville M. Vainio

unread,
Sep 6, 2008, 7:39:52 AM9/6/08
to leo-e...@googlegroups.com
I would like to do the following things:

ILeo buttons
=======
Create @button nodes that would be pushed to ipython instead of normal
execution mechanism. How can I do that without adding lots of verbiage
in the @button nodes themselves? Can I create buttons directly without
relying on @button nodes, e.g. with something like:

c.new_button('hello', my_callback)


Tab completion
=======

I'd like to do proper tab completion in ILeo windows. I.e, when the
user presses some character sequence (ctrl + space), something special
gets called that relays the contents of the line up to the current
cursor position to ipython. I'd like this to be "enablable" for
selected nodes - the idea is that once you run alt+I once on a node,
it would set the tab completer to that of ileo.

--
Ville M. Vainio - vivainio.googlepages.com
blog=360.yahoo.com/villevainio - g[mail | talk]='vivainio'

Edward K. Ream

unread,
Sep 6, 2008, 9:18:39 AM9/6/08
to leo-e...@googlegroups.com
On Sat, Sep 6, 2008 at 6:39 AM, Ville M. Vainio <viva...@gmail.com> wrote:

> Can I create buttons directly without relying on @button nodes, e.g. with something like:
>
> c.new_button('hello', my_callback)

Of course. You don't think the @button code does all the work itself, do you?

Let me show you, Ville, and more importantly, less experienced Leo
users, how I answer such questions. Leo's source code, if printed out
50 lines per page, would comprise a book of well over 1000 pages. I
don't begin to try to remember the details of this code.

Instead, I start with some broad generalities, and then use Leo itself
to explore Leo's code.

In this case, I start with the following:

1. @button nodes are created by the mod_scripting plugin. All I
remember is that there are a lot of helper methods, and a lot of
callbacks. It's impossible for me to keep the code straight, but the
individual pieces are fairly small and self contained.

2. @button nodes are created *in* a Leo frame, so the actual code that
creates the buttons in leoFrame.py or leoTkinterFrame.py. Maybe both,
I don't remember.

That's *all* I start with! No details.

To answer your question, I could start with 2, because that's were the
helper code actually is. But what the heck, let's say I didn't know
2, and that all I know is 1. That is, let's say all I know is that
the mod_scripting plugin creates @button nodes.

So I open leoPlugins.leo, and find the mod_scripting plugin. I search
for mod_scripting.py, and eventually find the node.

Most of the immediate children of the @thin mod_scripting.py node are
boilerplate. Only the 'class scriptingController' node could contain
what we want. I open that node and see the node 'createAllButtons &
helpers. I open that node and see the complicated code (lots of
children) I remembered.

I see the node called 'handleAtButtonNode @button'. Since @button
nodes create other buttons, I know that this button will tell me how
to create buttons.

Near the end of the body text for that node I see the line::

b = self.createAtButtonHelper(p,h,statusLine,shortcut,verbose=False)

So I search for createAtButtonHelper. Not surprisingly, it's in the
'Utils' node. I won't give all the code here, but there are calls to
the following methods in it::

b = self.createIconButton(...)
self.iconBar.setCommandForButton(...)
k.registerCommand(...)

This is the good stuff. k.registerCommand creates a minibuffer
command bound to the button's command. Obviously, createIconButton
contains the answer to your original question. The createIconButton
method contains this line:

b = self.iconBar.add(text=truncatedText,command=command,bg=bg)

So that's how it is done. But what is self.iconBar? Well, the first
place to look for the definition of an ivar is the ctor. We're in
luck. We see:

self.iconBar = c.frame.getIconBarObject()

So we have been lead, at long last, to point 2. That is, we now have
access to frame methods that allow us to create icon buttons.

**Important**: c.frame is a Leo frame. It is created in
leoTkinterFrame.py, in a class that is a subclass of the base class in
leoFrame.leo. If we didn't know that, we could find it out by looking
at the commander creation logic in leoCommands.py.

So let's look at leoTkinterFrame.py to see what we can see. We see the node:

'leoTkinterFrame'

This node has a child: 'class tkIconBarClass'. We are close. Opening
this node we see the 'add' node whose docstring is:

"""Add a button containing text or a picture to the icon bar.

Pictures take precedence over text"""

So the answer to your question is:

iconBar = c.frame.getIconBarObject()
iconBar.add(...)

The keyword arguments to the add method are extracted as follows:

text = keys.get('text')
imagefile = keys.get('imagefile')
image = keys.get('image')
command = keys.get('command')
bg = keys.get('bg')

That is, the arguments are pretty much the same as the Tk.Button args.

I've gone through this in detail to illustrate that Leo makes it easy
to track down lots of details starting with only the most general
notions of what the code contains.

I answered the question *exactly* as I indicated, by opening Leo
nodes. Two weeks from now I will have forgotten all the details
again, but that doesn't matter. If I hadn't actually written this
post, I could have found the answer in less than two minutes.

Edward

Edward K. Ream

unread,
Sep 6, 2008, 9:23:51 AM9/6/08
to leo-e...@googlegroups.com
On Sat, Sep 6, 2008 at 6:39 AM, Ville M. Vainio <viva...@gmail.com> wrote:

> Tab completion

> I'd like to do proper tab completion in ILeo windows. I.e, when the
> user presses some character sequence (ctrl + space), something special
> gets called that relays the contents of the line up to the current
> cursor position to ipython. I'd like this to be "enablable" for
> selected nodes - the idea is that once you run alt+I once on a node,
> it would set the tab completer to that of ileo.

Tab completion is complex. See leoPy.leo, the class:

Code-->Gui Base classes-->@thin leoKeys.py-->class autoCompleterClass

In particular, see the helper methods, doTabCompletion, doBackspace
and computeCompletionList. These are wildly complicated, and depend
on lots of arcane ivars.

Feel free to ask questions. I have a fair ideas of where the bodies
are hidden in this code.

Edward

Lew Maestas

unread,
Sep 6, 2008, 12:04:47 PM9/6/08
to leo-e...@googlegroups.com
Edward,

Can an edit of this post be put in the Leo documentation?

Ville M. Vainio

unread,
Sep 7, 2008, 5:59:36 AM9/7/08
to leo-e...@googlegroups.com
On Sat, Sep 6, 2008 at 4:23 PM, Edward K. Ream <edre...@gmail.com> wrote:

> Tab completion is complex. See leoPy.leo, the class:
>
> Code-->Gui Base classes-->@thin leoKeys.py-->class autoCompleterClass
>
> In particular, see the helper methods, doTabCompletion, doBackspace
> and computeCompletionList. These are wildly complicated, and depend
> on lots of arcane ivars.

I guessed as much. Would it make sense to expose some kind of "simple"
api for stuff like this? In ipython, we abstracted various things to
ipapi that can be used to extend ipython. I think this would be a boon
to leo as well (and especially ileo, since completion there does not
require "analysis" of any kind).

As I said, all I need would be is for leo to call callback/hook with
signature like

def complete(line-up-to-cursor, current_node) => list of strings

This should make writing leo's own completers easier as well (it
doesn't need to be "hard wired" to leo core).

Edward K. Ream

unread,
Sep 7, 2008, 11:42:47 AM9/7/08
to leo-e...@googlegroups.com
On Sun, Sep 7, 2008 at 4:59 AM, Ville M. Vainio <viva...@gmail.com> wrote:

> As I said, all I need would be is for leo to call callback/hook with
> signature like
>
> def complete(line-up-to-cursor, current_node) => list of strings

I missed this the first time around. Glad you asked again.

This should not be too difficult to do. I'll do it after I fix the
two urgent bugs.

This is probably a matter of 5-20 lines of code. More importantly,
this suggests an alternative to "always-on" autocompletion. It would
be a nice generalization to allow the completion to be done anywhere
on a line, rather than having the logic start at the beginning of what
the code calls the autocompletion chain.

Actually, there is already logic to find the start of the chain, so
the whole thing may be quite easy to do without even having to handle
(explicitly) the behind-the-scenes ivars.

OTOH, I'm not sure how bound together the output code (writing to the
completion tab) is with the more fundamental code. Some refactoring
might be required. But probably not: at worst, the code could just
use a "suppress output" hack. We shall see.

Edward

Ville M. Vainio

unread,
Sep 13, 2008, 12:29:05 PM9/13/08
to leo-e...@googlegroups.com
On Sat, Sep 6, 2008 at 4:18 PM, Edward K. Ream <edre...@gmail.com> wrote:

>> Can I create buttons directly without relying on @button nodes, e.g. with something like:
>>
>> c.new_button('hello', my_callback)
>
> Of course. You don't think the @button code does all the work itself, do you?

Ok, it was indeed as simple as doing:

def mkbutton(text, node_to_push):
ib = c.frame.getIconBarObject()
ib.add(text = text, command = node_to_push.ipush)

Some notes:

- Perhaps stuff like this could be abstracted away from gui classes to "leo API"
- "callback" would be more descriptive keyword arg name for arg() than
'command' (since passing a normal callable to it seems to be ok).

Edward K. Ream

unread,
Sep 15, 2008, 10:02:21 AM9/15/08
to leo-e...@googlegroups.com
On Sat, Sep 13, 2008 at 11:29 AM, Ville M. Vainio <viva...@gmail.com> wrote:

> Ok, it was indeed as simple as doing:
>
> def mkbutton(text, node_to_push):
> ib = c.frame.getIconBarObject()
> ib.add(text = text, command = node_to_push.ipush)
>
> Some notes:
>
> - Perhaps stuff like this could be abstracted away from gui classes to "leo API"

I think what you are asking is for the tkIconBarClass class to be a
subclass of an inconBarClass, defined in leoFrame.py. This is the
general way that Leo handles such matters.

I was a bit surprised that such a class doesn't exist. But then I saw
the icon bar convenience methods. So the following should also work:

c.frame.addIconButton(text=text,command=node_to_push.ipush)

I'm inclined to leave the code alone. YMMV.

> - "callback" would be more descriptive keyword arg name for arg() than
> 'command' (since passing a normal callable to it seems to be ok).

The argument is called "command" because that's the term used by Tk.
It's not worth changing, imo.

Edward

Reply all
Reply to author
Forward
0 new messages