Gimptalk - Premier Gimp Community: An introduction to Script-fu - Gimptalk - Premier Gimp Community

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

An introduction to Script-fu

#1 User is offline   saulgoode 

  • Retired Staff
  • PipPipPip
  • Group: Retired Staff
  • Posts: 5,324
  • Joined: 22-August 05

Posted 11 October 2006 - 10:41 PM

One of the great things about the GIMP is that it lets its users add to it through the use of plug-ins and scripts. Plug-ins are written in the C language (the same as the GIMP itself) and need to be compiled before they can be added. Scripts are merely text files that contain a sequence of commands which are to be executed.

Scripts can be written in a variety of languages (Perl, Python, GLUAS, and Scheme) so long as the language is supported by the GIMP (this usually means that there has to be some C code added to the GIMP which will read the script's text file and execute the commands).

The primary scripting language of the GIMP is Scheme and support for it is included in the GIMP "out-of-the-box" regardless of whether you are using Linux, Solaris, Windows, or MacOS. Since Scheme was the first scripting language available in the GIMP, it is also known as "Script-fu" (the name was an homage to the Chinese martial art, "kung-fu"). By learning to write your own Script-fu programs, you will be able to not only automate repetitive, time-consuming, and complicated tasks but also add functions to the GIMP which you may find useful (and which you feel the developers may have "forgotten").


-----

GETTING STARTED

[*] Start a new session in the GIMP and open up the Script-fu console (go to "Xtns->Script-fu->Script-fu Console").

[url=http://www.imageox.net/share/8496-Script-fu-console.png][/url]

The welcome message refers to "SIOD, Scheme In One Defun". SIOD is a particular implementation of the Scheme programming language (just as MS Word is a particular implementation of a word processor). The "defun" refers to the original "define function" command which has since been replaced by the less cryptic "define" command.

[*] Type in the following command into the input line near the bottom of the window:

[quote]2[/quote]

In the log window, the result will appear as follows:

[quote]=> 2
2[/quote]

The first line (the one starting with "=>") is merely an echo of your command. The second line is the generated result. Throughout this tutorial, console input will be described in this form (do not type in the "=>" or the output).

What you have done is enter a value and Script-fu has evaluated it to produce the result.


-----

Type "*pi*" into the command line:

[quote]=> *pi*
3.1415927[/quote]

What you have done is enter a symbol and Script-fu has evaluated it to produce the result. This symbol's value has been defined already (to be the ratio of a circle's circumference to its diameter). Later, we will show how you can define your own symbols. It should be noted that, unlike most other programming languages, Script-fu permits symbols to contain any characters except for spaces, tabs, newlines, and parentheses.


-----

Type "(sqrt 2)" into the command line:

In the log window, this will appear as follows:

[quote]=> (sqrt 2)
1.41421[/quote]

In addition to values and symbols, Script-fu understands lists composed of values and symbols, which are separated by spaces and surrounded by a pair of parentheses. When Script-fu evaluates a list of items, it expects the first item to be a name of a function that is to be executed. (In this case, 'sqrt' is the "square root" function and what you have just done was to calculate the "square root of 2".


-----

Now try entering "(2 + 3)":

[quote]=> (2 + 3)
ERROR: bad function (see errobj)[/quote]

What happened here is that Script-fu tried to "evaluate" the list you gave it, looked for "2" (the first item in the list) in its catalog of defined functions, and generated an error when it couldn't find it. Again (this is important), Script-fu will treat the first item of a list as a function to be called. In order to make our code work, we have to rearrange the parameters so that the addition symbol is the first item.

[quote]=> (+ 2 3)
5[/quote]

Since our list is treated as a function call and the first item is the function name, all of the items that follow it in the list are called "arguments". In the "sqrt" example, the function expected one argument and in the addition example, we passed two arguments to the function. Different functions can expect different numbers of arguments (some functions expect no arguments: type "(realtime)" into the console and you will be shown the number of seconds that have transpired since the beginning of 1970).


-----

The question then arises, if Script-fu tries to evaluate every list of items you give it (as a function call followed by some arguments) then how do you create a list of items that isn't treated as a function call?

In order to enter a list such as "(1 2 3)" you must "turn off" the normal evaluation mode using the 'quote' function.

[quote]=>(quote (1 2 3))
(1 2 3)[/quote]

The output, "(1 2 3)", is our "non-evaluated" list. In fact, we could even pass a "normal" function call list to 'quote' and it won't be evaluated either:

[quote]=> (quote (+ 2 3))
(+ 2 3)[/quote]

Instead of this producing the result, "5", the log window displays "(+ 2 3)". This is not surprising because we now know that a function call is merely a list of items.

Whenever you need to have a list which is not evaluated (i.e., not treated as a function), the 'quote' function is needed. This is such a common occurence that there is a shorthand notation for it: the apostrophe.

[quote]=> '(+ 2 3)
(+ 2 3)[/quote]

This is precisely the same thing as the preceding example, merely a more convenient syntax.


-----

As stated earlier, Script-fu allows a symbol string to be used in place of a corresponding item. These symbols can be created using the 'define' function.

[quote]=> (define b 2)
2[/quote]

The 'define' function equates its first argument with its second. Typing the variable name in the entry window

[quote]=> b
2[/quote]

The item "b" is evaluated and its numeric value is returned (just like *pi* was evaluated earlier). If you were to not evaluate "b" by using "(quote b)", what would you expect the output to be? Give it a try.

Everything so far is fairly simple, though perhaps a bit unusual. What I have described is basically the entire language: composed entirely of items and lists of items which are either "evaluated" or not. Everything else is built upon this simple concept. Items can also be strings; which doesn't really complicate things at all, as strings are treated just like the other items. (Though I haven't mentioned it, items can also be "arrays", but that won't be covered here).

There are only a couple of things that need to be mentioned to complete our description of Script-fu. First is the fact that a list can contain zero items. Such a list is called an "empty list" (or "null list") and will prove important when we look at the "testing" functions such as 'if' and 'while'.

The second detail that I haven't mentioned is the fact that items in a list do not have to be the same type; we can mix numbers with variable with strings all in the same list. In fact (and this is IMPORTANT), items in a list can themselves be lists. This fact is the one piece we need to complete the puzzle. Not only does this mean that the result of a function (which until now was treated as an item) can be a list, but that a list of items can actually be a list of items where each item is itself a list. The importance of this will become apparent later on but for now, let's examine how lists can be accessed.

[quote]=> (define items (quote (1 2 (3 4) 5)))
(1 2 (3 4) 5)[/quote]

To fetch the first item of a list, we use the 'car' function.

[quote]=> (car items)
1[/quote]

To return the rest of the list (except for the first item), we use the 'cdr' function.

[quote]=> (cdr items)
(2 (3 4) 5)[/quote]

Note: 'cdr' is an example of a function returning a list.

To fetch the second item from the original list of items, we should notice that it is the first item in the list returned in the 'cdr' example (i.e., the second item is the first item in the rest of the list).

[quote](car (cdr items))
2[/quote]

The result, "2", is as expected. It should be noted that the "inner" function ('cdr') is evaluated before the "outer" function ('car'). In Script-fu, all functions are evaluated "inside-to-outside": the innermost group of parentheses are evaluated before the outer ones.

Now try the following in order to fetch the third item from the original list:

[quote](car (cdr (cdr items)))
(3 4)[/quote]

The result should not come as a surprise (if we follow the logic of the preceding examples); the only thing notable about the result is that it is a list. The third item in the original list was itself a list, and that list is what was returned.

The "car + cdr" notation may seem a little strange and perhaps awkward. It is derived from a historical background and some newer versions of Scheme provide for use of the synonym "first" in place of "car" and "rest" in place of "cdr". Script-fu does not do this (though you could easily do so yourself) but there are advantages to the "car + cdr" notation.

The two functions that we used to fetch the second item from the list

(car (cdr items))

can be abbreviated to the single function

(cadr items)

Likewise, the command

(car (cdr (cdr items)))

can be shortened to the single function

(caddr items)

This abbreviation proves handy for making your code more readable.

There are other sources on the web that go into greater detail but what we have here should provide enough of an introduction for our purpose.


-----

PART II: SPECIAL FUNCTIONS

At this point, you should have all of the basic knowledge needed to write scripts in the Script-fu language. It can be summarized in two fundamental rules:

1) All input consists of either an item or a list of items -- an "item" being either a core object (such as an number, string, or array) or itself a list.
2) Lists are treated as function calls, with the first item in the list being the called function; though in a few special cases, this treatment can be disabled (such as when the 'quote' function is used).

To illustrate the second point, type the following command into the console:

[quote]=> (car (5 6 7))
ERROR: bad function (see errobj)[/quote]

The "bad function" in this case is "5"; since the standard behavior is to treat "(5 6 7)" as a function call. It is necessary to "disable" the standard treatment in order for the code to work as expected.

[quote](car (quote (5 6 7)))
5[/quote]

The 'quote' function is "special" in that its argument is treated literally (i.e., as it appears) and it is not evaluated. There are only a few of these "special functions" but they play a critical role in the language.

Another special function is the 'define' that we used earlier.

[quote]=> (define f (+ 2 3))
5[/quote]

The first argument, "f", to 'define' is NOT evaluated, but is instead interpreted as a name of a variable to be assigned a value. Note: in this case, the second argument, "(+ 2 3)", IS evaluated.

The 'define' function also has an even more "special" form:

[quote]=> (define (f) (+ 2 3))
#[/quote]

When the first argument to 'define' is wrapped within parentheses, it is still not evaluated and it is still treated as a symbol to be defined (though the symbol is a name of a function). In this situation, the subsequent arguments are treated as if they were 'quoted' and are not evaluated (they are stored for later execution when 'f' is called). The "#" output displays the function as it is stored.

If any variable names appear after the function name in the first argument, they are treated as arguments to the function being defined (and will be replaced when the function is eventually evaluated).

[quote]=> (define (f x) (+ x 3))
#
=> (f 2)
5[/quote]

[quote]=> (define (f x y) (+ x y))
#
=> (f 2 3)
5[/quote]

If there is more than one item appearing in the definition of the function, it is the value of the last item evaluated that is returned by the function.


-----

The Script-fu 'if' function is also treated specially; and has the form

(if test-item TRUE-item FALSE-item)

When an 'if' is encountered, 'test-item' is evaluated and if it is true then 'TRUE-item' is evaluated. If 'test-item' evaluates to false then 'FALSE-item' is evaluated (if 'FALSE-item' is not present, nothing is evaluated). The value returned by the 'if' function is the value of either 'TRUE-item' or 'FALSE-item'; whichever is evaluated.

In order to call more than one function for either 'true-item' or 'false-item' (remember, they are only single items), it is necessary to combine all of the functions so that they are treated as a single item. This can be done using the 'begin' function.

(begin item1 item2 item3 ... itemX)

The 'begin' function will evaluate all of the items, returning the value of the last one.


-----

The Script-fu 'while' function is also special and has the form

(while test-item item1 item2 item3 ... itemX)

As long as 'test-item' evaluates to true, all of the items ('item1' through 'itemX') will be evaluated. It is necessary that one of the items being evaluated eventually change the result of the test condition so that the 'while' function stops looping.

Type the following into the console:

[quote]=> (define b 5)
5[/quote]

[quote]=> (while (> b 0) (print b) (define b (- b 1)))
5
4
3
2
1
()[/quote]

The 'test-item' here is "(> b 0)" -- in most programming languages this would be written "b > 0" but in Script-fu the function comes first -- and, as long as 'b' is greater than zero the other items will be executed. The last item, "(define b (- b 1))", subtracts one from the value of 'b'.


-----

That is pretty much it as far as the basics go, there are some other "special" functions (and several "normal" functions); but the ones covered are enough to write most any program you wish.

There are two issues that I will address in closing, and they concern the 'define' function.

Firstly, the use of 'define' in the WHILE loop above is not ideal. Whenever it is encountered, a new variable is 'defined' and the old one is abandoned. As you saw, this caused no problem with the program's execution but, nonetheless, it is a waste of memory (and eventually the "garbage collector" will have to come along and discard all of the abandoned variables).

For this reason, there is a special command, called 'set!', which will reassign the value of a variable (without creating a new one).

[quote]=> (define b 5)
5[/quote]

[quote]=> (while (> b 0) (print b) (set! b (- b 1)))
5
4
3
2
1
()[/quote]

The code executes the same, it is just more efficient and more clear what is happening.

Secondly, when symbols are defined, they are assigned that value throughout your program (and throughout all of Script-fu). If your program uses a variable named 'b' and calls another program which 'define's a variable 'b' also, then your variable is changed by the other program. In order to avoid this, Script-fu provides the 'let' function which limits your 'definition' to your program.

The format of the 'let' function is one of the most complicated that Script-fu has, so I will present it in its simplest form first:



The 'x', 'y', and 'z' symbols are created similar to if they had been 'define'd, but they only exist until the last right parenthesis (while all of the items are being evaluated). If one of them exists already as a symbol (for example, in another function that calls yours) then that pre-existing value is ignored and the "local" value is used. At the end of the 'let' statement (after 'itemX'), the original meaning is restored.

[quote]=> *pi*
3.14159[/quote]

[quote]=> (let ((*pi*)) (set! *pi* 2) (print *pi*))
2
()[/quote]

[quote]=> *pi*
3.14159[/quote]

The above commands demonstrates how '*pi*' is only temporarily defined as "2". Outside of the 'let' statement, '*pi*' has its normal value.

Note: you can both declare and assign the variables when using the 'let' statement. For example, the preceding example could be written as:

[quote]=> (let ((*pi* 2)) (print *pi*))
2
()[/quote]

A variation of the 'let' function is the 'let*' function:


The only difference between 'let*' and 'let' is that the variables ('x', 'y', and 'z') are guaranteed to be evaluated in the order they appear. If the 'let' function were used in the above example, it might fail because 'z' could be evaluated before 'x' and 'y'. (For this reason, it is more common to just use 'let*' in all cases.)

NOTE: Regardless of whether a variable is created with 'define' or with 'let*', the 'set!' function is still used to change its value.


-----

While I haven't shown anything about how the language is used in the GIMP (we didn't even have an image open), having an understanding of the philosophy of the basic Script-fu function and data elements is an important part of being able to use it effectively. Hopefully, this introduction will have helped developed such an understanding.
Everybody makes their own fun. If you don't make it yourself it's not fun, it's entertainment.
0

#2 User is offline   Mike1946 

  • Advanced Member
  • PipPipPip
  • Group: Members
  • Posts: 2,468
  • Joined: 09-August 06

Posted 21 October 2006 - 03:40 PM

wow,this helped tons,you should sticky this thread
Posted Image
^POWER UP^
0

#3 User is offline   Phyro_gp 

  • Advanced Member
  • PipPipPip
  • Group: Members
  • Posts: 3,882
  • Joined: 28-July 06

Posted 21 October 2006 - 03:42 PM

OW!!

My head hurts!!

Quote

I see a little silhouette of a man. Jennet reno, Jennet reno will you do the fandango? Clinton's noose is tightening very very frightening! Me! U.S Senate, U.S Congress, U.S Senate, U.S Congress, U.S Senate let me go! Let me go! I'm just a southern boy, nobody loves me. He's just a bad boy from the first family. There's no escape from her furosity!
0

#4 User is offline   Kavukamari 

  • Member
  • PipPip
  • Group: Members
  • Posts: 94
  • Joined: 11-August 06

Posted 21 October 2006 - 08:09 PM

mine too, 2 confusing 4 brain 2 handll now
0

#5 User is offline   PhotoComix 

  • GT Senior Moderator
  • Group: Senior Moderators
  • Posts: 11,288
  • Joined: 13-June 05

Posted 22 October 2006 - 01:54 PM

Quote

My head hurts!!

Quote

mine too, 2 confusing 4 brain 2 handl now
.
i think the only way to understund is follow the step n 1:

open up the Script-fu console
(go to "Xtns->Script-fu->Script-fu Console") and follow the tut ,step by step, typing on the consolle

It will work if you really want understund the sintax of script fu (this is not strictly required to use Gimp)

Could be useful to decipher a script and to understund how it works (and even this is not strictly required),and to figure out how to start to write ( or at least edit) a simple script

I try to read other "introduction to script fu" and this is the most simple and clear (and updated) i found till now.

So if you are puzzling your mind to understund script fu basic could be quite useful.. i don't think script fu could be explained in a more simple way
(i could be wrong ...if so please send me a link for more simple and clear tut on script fu :w:)
0

#6 User is offline   Kavukamari 

  • Member
  • PipPip
  • Group: Members
  • Posts: 94
  • Joined: 11-August 06

Posted 22 October 2006 - 07:51 PM

umm, can you tell us some of the commands to do stuff in GIMP, like bucket fill, select rectangle, shrink selection, etc, etc?
0

#7 User is offline   PhotoComix 

  • GT Senior Moderator
  • Group: Senior Moderators
  • Posts: 11,288
  • Joined: 13-June 05

Posted 23 October 2006 - 01:53 PM

:o:????

Please avoid off topic that has nothing to do with this script fu tutorial
0

#8 User is offline   Kavukamari 

  • Member
  • PipPip
  • Group: Members
  • Posts: 94
  • Joined: 11-August 06

Posted 24 October 2006 - 02:53 AM

I mean how do i make the script do that stuff :o: :c: :m:
0

#9 User is offline   saulgoode 

  • Retired Staff
  • PipPipPip
  • Group: Retired Staff
  • Posts: 5,324
  • Joined: 22-August 05

Posted 24 October 2006 - 03:31 AM

Quote

I mean how do i make the script do that stuff


That will require a tutorial (try the one in the GUM).
Everybody makes their own fun. If you don't make it yourself it's not fun, it's entertainment.
0

#10 User is offline   Chrios 

  • Member
  • PipPip
  • Group: Members
  • Posts: 23
  • Joined: 09-August 05

Posted 17 December 2006 - 02:41 PM

You know what.. I'm a bit of a budding young programmer, and this looks incredibly like... Oh, was it PERL or LISP...

LISP! That's right. Any chance that it is built upon LISP?
0

#11 User is offline   fencepost 

  • Retired Staff
  • PipPipPip
  • Group: Retired Staff
  • Posts: 2,643
  • Joined: 01-March 05

Posted 06 January 2008 - 01:36 AM

This is a very good tutorial and it has helped me immensely. BTW, I think this topic would be more appropriate in the tutorial section. Can you clarify the part highlighted in red for me? I just can't seem to wrap my brain around it at the moment.

If there is more than one item appearing in the definition of the function, it is the value of the last item evaluated that is returned by the function.

It comes from Part II of the tutorial on Special Functions and is preceded by this:

Quote

When the first argument to 'define' is wrapped within parentheses, it is still not evaluated and it is still treated as a symbol to be defined (though the symbol is a name of a function). In this situation, the subsequent arguments are treated as if they were 'quoted' and are not evaluated (they are stored for later execution when 'f' is called). The "#" output displays the function as it is stored.

If any variable names appear after the function name in the first argument, they are treated as arguments to the function being defined (and will be replaced when the function is eventually evaluated).

QUOTE:
=> (define (f x) (+ x 3))
#
=> (f 2)
5


QUOTE:
=> (define (f x y) (+ x y))
#
=> (f 2 3)
5

0

#12 User is offline   saulgoode 

  • Retired Staff
  • PipPipPip
  • Group: Retired Staff
  • Posts: 5,324
  • Joined: 22-August 05

Posted 06 January 2008 - 03:12 AM

Start with a very simple definiton:

=> (define (three) 3)

If you then execute the function, the value returned is the only item which appears in the executed part of the function; i.e., "3".

Quote

=> (three)
3


If there is more than one item, the value return is last item in the executed part of the function:

(define (six) 4 5 6)

Quote

=> (six)
6


In this case, the "4" and the "5" are evaluated but this doesn't really have much effect; only the last item, "6", is returned. However, remembering that items can actually be lists, and that lists are normally treated as function calls (with the first item of the list being the name of the function being called), it should be apparent that first two items could in fact be functions which DO have an effect.


;; first define some global variables to work with
(define red)
(define green)
(define blue)

(define (fg-brightness)
(set! red (car (car (gimp-context-get-foreground))))
(set! green (cadr (car (gimp-context-get-foreground))))
(set! blue (caddr (car (gimp-context-get-foreground))))
(/ (+ red green blue) 3)
)


In this code, it is only the last item (the result of the divide function call) which gets returned.
Everybody makes their own fun. If you don't make it yourself it's not fun, it's entertainment.
0

#13 User is offline   fencepost 

  • Retired Staff
  • PipPipPip
  • Group: Retired Staff
  • Posts: 2,643
  • Joined: 01-March 05

Posted 06 January 2008 - 03:54 AM

Very good. The example was nice. Thanks! It took me a couple of minutes to be able to "see" the values that were being returned. I kept expecting to see a value by copying/pasting your code into the Console and hitting enter. Then the light went on. If I needed to see the value of the function "fg-brightness", I had to enter "(fg-brightness)" in the the Console and to see the individual red, green, and blue values, I had to type in "red", "green" or "blue", respectively.
0

#14 User is offline   fencepost 

  • Retired Staff
  • PipPipPip
  • Group: Retired Staff
  • Posts: 2,643
  • Joined: 01-March 05

Posted 11 January 2010 - 03:01 PM

Re-read through this again and learned some new things. Thanks again for this tutorial; great to have resources such as this.
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic