> The only way I can see to make @command work would be to have the
> __call__ method make entries in some module-level dict, say
> leoCommands.commandsDict. Then c.finishCreate could copy the module-
> level dict into c.commandsDict.
>
> I'm not happy using module-level data structures, but using a module-
> dict would be about equivalent to using the module-level classesList
> list at the end of leoEditCommands.py, so perhaps the work-around is
> no worse than what is in use now. OTOH, the classesList hack must be
> considered one of the Leo's worst warts.
>
> Can anyone else think of a better way?
Something I've wondered for a long time - why do the commands need to
reside in the commander in the first place, as opposed to a global
dict (mapping string => callable) that's available everywhere? I don't
see the big benefit in having different commands available in
different leo documents...
--
Ville M. Vainio
http://tinyurl.com/vainio
Something I've wondered for a long time - why do the commands need to
reside in the commander in the first place, as opposed to a global
dict (mapping string => callable) that's available everywhere? I don't
see the big benefit in having different commands available in
different leo documents...
> This would be used as follows:
>
> @command('my-command-name)
> def myCommand (self,event=None):
> '''Return the set of all sets that are not members of
> themselves.'''
>
> But there is a hitch. The __init__ and __call__ methods are called at
> "compile time", and at that time, neither c nor c.commandsDict exist.
I have some plans to unify what has been talked about in this thread.
I want to make it easy to create 'global' commands and buttons in
plugins. I just think the current way of having to hook to frame
creation is a bit too "involved" to remember easily (without
copy-pasting the stuff from somewhere)
So to create command you would do
import leoPlugins
def mycmd(c,p, event):
print c,p
leoPlugins.expose_command('my-command', mycmd)
leoPlugins.expose_button('press-this',mycmd)
These would be available on *all* new commanders, and the current
commander as well (for easy testing). Plugins would not have to
contain more code than what is presented above.
The idea is to have a global command dict, and global button dict.
There is one after-create-frame handler that introduces all the
entries in this dict to the commander command dict.
I think functions are clearer than decorators here, because leo
already uses @ character extensively and it can be a source of
confusion.
Thoughts?
> But there is a hitch. The __init__ and __call__ methods are called at
> "compile time", and at that time, neither c nor c.commandsDict exist.
[snip]
So to create command you would do
import leoPlugins
def mycmd(c,p, event):
print c,p
leoPlugins.expose_command('my-command', mycmd)
leoPlugins.expose_button('press-this',mycmd)
These would be available on *all* new commanders, and the current
commander as well (for easy testing). Plugins would not have to
contain more code than what is presented above.
The idea is to have a global command dict, and global button dict.
There is one after-create-frame handler that introduces all the
entries in this dict to the commander command dict.
I think functions are clearer than decorators here, because leo
already uses @ character extensively and it can be a source of
confusion.
Thoughts?
>> The idea is to have a global command dict, and global button dict.
>
> The dict would be something like g.app.commandsDict, rather than the present
> c.commandsDict.
Exactly.
>> I think functions are clearer than decorators here, because leo
>> already uses @ character extensively and it can be a source of
>> confusion.
>
> I would prefer to use a decorator, perhaps even a do-nothing decorator, that
> clearly marks code as a command.
Well, that's just a matter of preference. We can do it as a decorator as well.
> I don't really understand all the technical details, but Python being what
> it is, I find it hard to believe that there are any insuperable problems
> lurking anywhere.
Yeah, it's a piece of cake to implement.
> Hmmm. Does passing c explicitly to the command make c available to the
> decorator? Probably not, but maybe it doesn't matter, since the entry would
> be made in g.app.commandsDict rather than c.commandsDict. In other words,
> the decorator only needs g at module-load time, and that it has.
Yeah, the decorator just registers the command to g.app.commandsDict.
Only the actual code that executes the function (simulateCommand /
whatever) will need to pass the c.
> So I think this is an excellent idea in all respects.
Alrgiht, I'll implement it today unless you wan't to do it yourself :-).
Alrgiht, I'll implement it today unless you wan't to do it yourself :-).
> Along the way, however, I thought I would experiment with using
> decorators to register commands. The idea is to have the decorator
> make an entry in c.commandsDict. Something like this:
I have now pushed g.command decorator. It pushes stuff to
g.app.global_commands_dict, which gets copied to c on commander
creation. it's rev 1889, you'll note that it's a simple
implementation. I didn't add g.button yet.
> I have now pushed g.command decorator. It pushes stuff to
Ah, stupid me, I didn't specify an example:
@g.command('bookmark')
def bookmark(event):
c = event.get('c')
p = c.currentPosition()
bookmarks.append(p.gnx)
g.es('bookmarked')
Now, if all your plugin did was specifying commands, you wouldn't need
to have anything more in the plugin module. In particular, you can
forego stuff like::
leoPlugins.registerHandler('after-create-leo-frame',onCreate)
[on decorators to indicate commands]
> 1. This would create a new pattern in which commands are functions,
> not members of the commands class. I suspect that methods can be a
> "function" in this sense. This might be useful so that commands
> could share code.
Not sure about methods decorated this way, where would you get an
instance to call them with? Unless they were class or static methods
of course.
Not that you need to be able to use methods for this approach to be
useful, just that I'm not sure it can include instance methods easily.
Cheers -Terry
> I notice that executing the example script doesn't make the command
> available to the present commander. It would be nice to fix that.
Yeah, that's in the cards.
> I notice that executing the example script doesn't make the command
> available to the present commander. It would be nice to fix that.
Done in trunk.
I also added g.app.commanders() which returns a list of commanders (it
seemed to be missing).
I think @g.command is now he simplest way to create new commands:
- They magically appear on all new commanders
- They (almost as magically) appear on every commander that already exists
I'm open for thoughts & feedback for a certain period of time before
documenting this as the recommended way to create commands ;-)
Note that if you are into this "Object oriented programming" fad, you
can register your commands using closures, like this:
class MyCommands:
def create(self):
@g.command('foo1')
def foo1_f(event):
self.foo = 1
@g.command('foo2')
def foo1_f(event):
self.foo = 2
@g.command('foo-print')
def foo_print_f(event):
g.es('foo is', self.foo)
o = MyCommands()
o.create()
I think @g.command is now he simplest way to create new commands:
- They magically appear on all new commanders
- They (almost as magically) appear on every commander that already exists
> I'm open for thoughts & feedback for a certain period of time before
> documenting this as the recommended way to create commands ;-)
Documented in LeoDocs, like this:
(no need to reply with typofixes, noted some already myself)