Next: Python FAQ, Previous: The Python Interpreter, Up: Scripting GPS

#### 16.8.6 Subprogram parameters

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:

- GPS shell

  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

  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 instance of the combo
  - The current selection in the combo box

  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:

  - Global 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"

  - Unbound methods

    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()

  - Bound methods

    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).