A few of the functions exported by GPS in the GPS shell or in python expect a subprogram as a parameter.
This is handled in different ways depending on what language your are using:
It isn't possible to define new functions in the GPS shell. However, this concept is similar to the GPS actions (see Defining Actions), which allow you to execute a set of commands and launch external processes.
Therefore, a subprogram parameter in the GPS shell is a string, which is the name of the action to execute.
For instance, the following code defines the action "on_edition", which is called every time a new file is edited. The action is defined in the shell itself, although this could be more conveniently done in a separate customization file.
parse_xml """<action name="on_edition"> <shell>echo "File edited"</shell></action>""" Hook "file_edited" Hook.add %1 "on_edition"
Python of course has its own notion of subprogram, and GPS is fully compatible with it. As a result, the syntax is much more natural than in the GPS shell. The following example has the same result as above:
import GPS def on_edition(self, *arg): print "File edited" GPS.Hook ("file_edited").add (on_edition)
Things are in fact slightly more complex if you want to pass methods as arguments. Python has basically three notions of callable subprograms, detailed below. The following examples all create a combo box in the toolbar, which calls a subprogram whenever its value is changed. The documentation for the combo box indicates that the callback in this case takes two parameters:
The first parameter is the instance of the combo box associated with the toolbar widget, and, as always in python, you can store your own data in the instance, as shown in the examples below.
Here is the description of the various subprograms:
These are standard subprograms, found outside class definitions. There is no implicit parameter in this case. However, if you need to pass data to such a subprogram, you need to use global variables
import GPS my_var = "global data" def on_changed (combo, choice): global my_var print "on_changed called: " + \ my_var + " " + combo.data + " " + choice combo = GPS.Combo \ ("name", label="name", on_changed=on_changed) GPS.Toolbar().append (combo) combo.data = "My own data"
These are methods of a class. You do not specify, when you pass the method in parameter to the combo box, what instance should be passed as its first parameter. Therefore, there is no extra parameter either.
Note however than whatever class the method is defined in, the first parameter is always an instance of the class documented in the GPS documentation (in this case a GPS.Combo instance), not an instance of the current class.
In this first example, since we do not have access to the instance of MyClass, we also need to store the global data as a class component. This is a problem if multiple instances of the class can be created.
import GPS class MyClass: my_var = "global data" def __init__ (self): self.combo = GPS.Combo \ ("name", label="name", on_changed=MyClass.on_changed) GPS.Toolbar().append (self.combo) self.combo.data = "My own data" def on_changed (combo, choice): ## No direct access to the instance of MyClass. print "on_changed called: " + \ MyClass.my_var + " " + combo.data + " " + choice MyClass()
As the example above explains, there is no direct access to MyClass when executing on_changed. An easy workaround is the following, in which the global data can be stored in the instance of MyClass, and thus be different for each instance of MyClass.
import GPS class MyClass: def __init__ (self): self.combo = GPS.Combo \ ("name", label="name", on_changed=MyClass.on_changed) GPS.Toolbar().append (self.combo) self.combo.data = "My own data" self.combo.myclass = self ## Save the instance self.my_var = "global data" def on_changed (combo, choice): print "on_changed called: " + \ combo.myclass.my_var + " " + combo.data + " " + choice MyClass()
The last example works as expected, but is not convenient to use. The solution here is to use a bound method, which is a method for a specific instance of a class. Such a method always has an extra first parameter, set implicitly by Python or GPS, which is the instance of the class the method is defined in.
Notice the way we pass the method in parameter to append(), and the extra third argument to on_changed in the example below.
import GPS class MyClass: def __init__ (self): self.combo = GPS.Combo \ ("name", label="name", on_changed=self.on_changed) GPS.Toolbar().append (self.combo) self.combo.data = "My own data" self.my_var = "global data" def on_changed (self, combo, choice): # self is the instance of MyClass specified in call to append() print "on_changed called: " + \ self.my_var + " " + combo.data + " " + choice MyClass()
It is often convenient to use the object-oriented approach when writing
python scripts. If for instance you want to spawn an external process,
GPS provides the GPS.Process
class. When you create an instance,
you specify a callback to be called when some input is made available
by the process. Matching the above example, the code would look something
like:
class MyClass: def __init__ (self): self.process = GPS.Process ("command_line", on_match = self.on_match) def on_match (self, process, matched, unmatched); print "Process output: " + unmatched + matched + "\n"
A more natural approach, rather than having a class that has a process
field, is to directly extend the GPS.Process
class, as in:
class MyClass (GPS.Process): def __init__ (self): GPS.Process.__init__ \ (self, "command_line", on_match = self.on_match) def on_match (self, matched, unmatched); print "Process output: " + unmatched + matched + "\n"
Any command that can be used on a process (such as send
) can then
directly be used on instances of MyClass.
There is one non-obvious improvement in the code above: the on_match
callback has one less parameter. What happens is the following: as per
the documentation of GPS.Process.__init__
, GPS gives three arguments
to its on_match
callback: the instance of the process (process
in the first example above), the string that matched the regular expression,
and the string before that match.
In the first example above, we are passing self.on_match
, ie a
bound method, as a callback. That tells python that it should automatically,
and transparently, add an extra first parameter when calling
MyClass.on_match
, which is self
. This is why the first example
has four parameters to on_match
.
However, the second example only has three parameters, because GPS has
detected that self
(the instance of MyClass
) and the
instance of GPS.Process
are the same in this case. Thus it doesn't
add an extra parameter (self
and process
would have been
the same).