It is currently Tue Apr 16, 2024 2:05 pm


All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 3 posts ] 
Author Message
 Post subject: (docs and code) pdb.gimp_progress_install pdb.gimp-progress-install
PostPosted: Tue Aug 25, 2015 3:29 pm  (#1) 
Offline
GimpChat Member
User avatar

Joined: Aug 13, 2015
Posts: 312
Location: Somewhere between lost and found.
GIMP 2.8.14 pdb procedure documentation and to a lesser extent source code.
This threads simple name is more for search engines than for humans. This material is hard to find, so I am trying to make it easier.

In attempting to write a tutorial on this subject, I ran into a snag which the gimp developer mailing list says may be a bug. So for now consider this as untenable.

Table of Contents
Purpose
Content Summary
What's Here
What's Not Here
Article Format Notes
Errors
Introduction
Registering The Callback
The Callback
The Parameters
Conclusion
Footnotes

 Purpose: 
Recently I had a need to write an external progress bar for a plugin. By external I mean outside of The GIMP's interface, or it's Script/Plugin interface. I wanted existing scripts and plugins called from my script to utilize this progress bar, instead of one of the other two. When I began searching the Internet for information on how to do this I came up empty. So I posted information requests on some forums, and one mailing list, and still came up empty. Finally I cloned the GIT repository of The Gimp (2.8.14) and began prowling their source code. It is almost not documented at all internally, and the external documentation contained nothing of any use on the subject. So after I had finally found what I needed, I realized that others might need it as well. So I decided to write four articles on the subject, 2 technical, and two easy to follow tutorials. This is the first of those four. It is my greatest hope that this article might save some one else the hours of research I had to do.


 Content Summary / Article Information: 
 What's Here: 
C source code relating to the pdb.gimp-progress-install procedure.
Documentation for the parts relevant to this article.
Pseudo code examples of use in python and scheme.
Plain english explanation of the code and pseudo-code.

 What's Not Here: 
Materials relevant to gimp.progress_install (separate article and tutorial forthcoming.)
A complete working tutorial (That is to placed in another thread later today or tomorrow :) )

 Article Format Notes: 
While there is a significant difference between a script and a plugin, from here on I will use the terms interchangeably, according to what sounds best in the sentence. I ask forgiveness from the technically minded readers.

In Scheme (The GIMP's native scripting language tool) functions/methods/procedures are defined using hyphens ("-") for word separators, while in python, underscores ("_") are used. The C-code, depending upon which parts you are looking at, and their intended purpose within the code, uses both. Because of this I will try to be use specific in my examples, but in the article text I may switch between the two. I am a Python coder, so by force of habit I will probably use the underscore most commonly (that is also the one used most prevalently in the C-code)

Pseudo-code will be posted inside the forums 'quote' handling blocks, while actual code will be in 'code' blocks.
Quote:
Pseudo code style

Code Style


 Errors: 
If I should make any errors in the comments I make here, please post that information as a respons in this thread AND send me a personal message on this forum so that they can be corrected!
Administrators and Moderators This information is useless if it is incorrect. If you should notice errors posted which I have not responded to in a timely fashion (say 24 hours) feel free to update the post!

 Introduction: 
Creating an external progress meter (or progress-bar if you prefer), for gimp scripts and plugins is not often needed. GIMP's Script-Fu[suanchorp]1[/sup] and Python-Fu1 UI handles that behind the scenes for most scripts and plugins. But the internal code for The GIMP makes it possible to handle this for ones self on those occasions. The intended method for handling such manual configuration in scripting, is the PDB2 Procedure gimp-progress-install.

This procedures documentation, quoted below, states that it "..installs" a handling procedure. This comment is what prompted this article, for it is technically correct, yet it yields the wrong idea when read by someone writing a script for The GIMP, who is probably unfamiliar with C-code.
Copy/Paste from the Procedure Browser in The GIMP 2.8.14 wrote:
This function installs a temporary PDB procedure which will handle all progress calls made by this plug-in and any procedure it calls. Calling this function multiple times simply replaces the old progress callbacks.

It does in fact 'install' the procedure, but into a C table in The GIMP, not into the PDB itself, as the documentation implies. Instead the temporary procedure3 must first first be created separately4, and then 'plugged into' the PDB via the gimp-progress-install procedure. The documentation also makes no mention of three arguments said temporary procedure must accept, in a certain order. And while it alludes to the fact that the temporary function must in fact act in place of the four main functions (gimp-progress-init, gimp-progress-set-text, gimp-progress-end, and gimp-progress-update) it is somewhat vague on that point. What about gimp-progress-pulse5, and gimp-progress-cancel?

My sniffing through the source code has identified the relevant code sections, and the parts of that code which indicate how a script writer must create these functions, and get them working. Since the call to create a temporary script must be done from Pyhon-Fu, I do not believe this can be done in a scheme script.6

 Registering The Callback Script: 
The temporary script is instanciated via a call to gimp.install_temp_proc(source and comments below). This call is very similar to a gimpfu.register7 call when creating a Python plugin for The GIMP. In fact it take the same parameters (although the gimpfu module handles inserting some of them for us in a call to )gimpfu.register8. Thus the callback script would be written, and registered with the PDB in a manner like this:
gimp.install_temp_proc(
       # This would be the name used to call the script from another script
       "some_script_name_for_the_db",
       # A short descriptive blurb.  In the Procedure Browser this is the main information
       # near the bottom of the windows right side.
       # For a temp script this can be anything non empty.
       "blurb",
       # This serves as a tooltip in a scripts menu emplacement for a regular script.
       # For a temp script this can be anything non empty.
       "tooltip text",
       # Author field in a regular script.
       # For a temp script this could be anything non empty.
       "author",
       # Copyright field in a regular script.  WHO holds the copyright
       # For a temp script this can be anything non empty.
       "copy",
       # Date field in a regular script.
       # For a temp script this can be anything non empty.
       "date",
       # Menu Label field in a regular script.
       # See below (can/should be empty)
       "<Image>/Filters/My Personal Sub Menu",
       # Image Types field in a regular script.
       # See below
       "RGB*",

       # This field is new.  It is where we identify this script as temporary.
       # AND IT MUST HOLD THIS VALUE
       gimpfu.TEMPORARY,

       # This field is new.  It is holds an integer which represents the
       # number of parameters the script takes.
       n_params,

       # This field is new.  It is holds an integer which represents the
       # number of return values the script generates.
       n_returns,

       # This field is a python list of 4-tuples.  This is the parameters field.
       # The number of tupples must match the value in the 'no. of parameters field'
       [
           # Each tupple must be in the form of
           # (int, string, string, type matched by what the int stands for (see below) as a default value)
           (PF_INT32, "variable_name", "Human readable description of the field", 0)
       ],

       # This section is identical to the parameters section, but defines return values.
       [
           # These are 3-tupples, in the form:
           # (int, string, string)  Where the int represents the return type, string 1 is a variable name
           # and string 2 a descriptive blurb about the return.
           (PF_INT32, "variable_name", "Human readable description of the return value")
       ],

       # This section contains the function which is to be called when this script
       # executed as a script from the PDB, or in this case, when a progress function
       # is called.  NOTE:  It is not a string, it is the name of the function, with no
       # parenthesise or parameters attached.
       the_callable_function)

gimp.pdb.gimp_progress_install(some_script_name_for_the_db)

Those who have written scripts in either Scheme or Python will notice there are three new parameters in that code. Namely the fields
for 'type', 'number of parameters', and 'number of returns'. Script-Fu and Python-Fu handle these for us behind the scenes when a regular script is written. Here we need to enter the values directly.

Menu Label Field This value tells a standard script, and presumably a temporary one (untested), where in The GIMP's menu structure an entry should be created for a script. For the purposes of an external progress bar, you would not want the user updating anything, so it should be empty.
Image Types Field This must be one or more (comma seperated text string) of the following:
[list]
# * - Any image
# gimpfu.RGB - Any RGB image (no alpha channel)
# gimpfu.RGB* - RGB with or without alpha channel
# gimpfu.GRAY - Greyscale images only (NOTE: GREY is not valid)
type Field This field is where we indicate that the procedure being added to the PDB is temporary in scope. gimpfu already contains a hard coded value in the 'constant' gimpfu.TEMPORARY9, and that constant should be used.
no. of parameters/return values The reasons for this fields existence and use, is that in the C language, memory must be set aside for an object to be stored before the object is used. So in the case of an array (C version of a Python list), the number of elements needs to be explained in advance, as well as what the elements are. Which is part of the reason why the parameters and return values are described in the way that they are.
type codes (the ints) in the parameter and return tuples: Valid values for these integers are many, but are most easily represented through the use tof the constants defined in the gimpfu module. The constants (copy - pasted directly from gimpfu.py) are:

PF_INT8        = PDB_INT8
PF_INT16       = PDB_INT16
PF_INanchorT32       = PDB_INT32
PF_INT         = PF_INT32
PF_FLOAT       = PDB_FLOAT
PF_STRING      = PDB_STRING
PF_VALUE       = PF_STRING
#PF_INT8ARRAY   = PDB_INT8ARRAY
#PF_INT16ARRAY  = PDB_INT16ARRAY
#PF_INT32ARRAY  = PDB_INT32ARRAY
#PF_INTARRAY    = PF_INT32ARRAY
#PF_FLOATARRAY  = PDB_FLOATARRAY
#PF_STRINGARRAY = PDB_STRINGARRAY
PF_COLOR       = PDB_COLOR
PF_COLOUR      = PF_COLOR
PF_ITEM        = PDB_ITEM
PF_DISPLAY     = PDB_DISPLAY
PF_IMAGE       = PDB_IMAGE
PF_LAYER       = PDB_LAYER
PF_CHANNEL     = PDB_CHANNEL
PF_DRAWABLE    = PDB_DRAWABLE
PF_VECTORS     = PDB_VECTORS
#PF_SELECTION   = PDB_SELECTION
#PF_BOUNDARY    = PDB_BOUNDARY
#PF_PATH        = PDB_PATH
#PF_STATUS      = PDB_STATUS

PF_TOGGLE      = 1000
PF_BOOL        = PF_TOGGLE
PF_SLIDER      = 1001
PF_SPINNER     = 1002
PF_ADJUSTMENT  = PF_SPINNER

PF_FONT        = 1003
PF_FILE        = 1004
PF_BRUSH       = 1005
PF_PATTERN     = 1006
PF_GRADIENT    = 1007
PF_RADIO       = 1008
PF_TEXT        = 1009
PF_PALETTE     = 1010
PF_FILENAME    = 1011
PF_DIRNAME     = 1012
PF_OPTION      = 1013

Note: Those values which are preceeded by a hash mark ('#') are commented out in the source code, and as such are invalid for use.
To use these codes in your script, simply insert the code from the LEFT column, with a reference to the gimpfu namespace. An example would be gimpfu.TEMPORARY.

The source code which represents these functions, and illustrates the required parameters, and how they are handled is listed below10 in the order in which the functions are processed when a call is made to gimp.install_temp_proc. As you can see from this first one, the parameters are required to appear in the registration in the order given above.
gimp_install_temp_proc (
                        const gchar        *name,
                        const gchar        *blurb,
                        const gchar        *help,
                        const gchar        *author,
                        const gchar        *copyright,
                        const gchar        *date,
        anchor                const gchar        *menu_label,
                        const gchar        *image_types,
                        GimpPDBProcType     type,
                        gint                n_params,
                        gint                n_return_vals,
                        const GimpParamDef *params,
                        const GimpParamDef *return_vals,
                        GimpRunProc         run_proc)
{
  g_return_if_fail (name != NULL);
  g_return_if_fail ((n_params == 0 && params == NULL) ||
                    (n_params > 0  && params != NULL));
  g_return_if_fail ((n_return_vals == 0 && return_vals == NULL) ||
                    (n_return_vals > 0  && return_vals != NULL));
  g_return_if_fail (type == GIMP_TEMPORARY);
  g_return_if_fail (run_proc != NULL);

  gimp_install_procedure (name,
                          blurb, help,
                          author, copyright, date,
                          menu_label,
                          image_types,
                          type,
                          n_params, n_return_vals,
                          params, return_vals);

  /*  Insert the temp proc run function into the hash table  */
  g_hash_table_insert (temp_proc_ht, g_strdup (name), (gpointer) run_proc);
}[/coanchorde]

[anchor=callback goto=][b][size=150]The Callback Script:[/size][/b][/anchor]
The call back script itself, the one which must be registered via the example just shown, also has it's form and function defined in the C code of several of the source files for The GIMP.  The contents of these functions, and the method of implication of the PDB define what any progress handler must accept as parameters, and the goal of a progress bar determines what they must do.

The required parameters are defined by a series of calls throughout the base code of The GIMP, but begin with the definitions provided by
[highlight=#80FFBF]register_progress_procs[/highlight] found in the file progress-cmds.c.  The entire body of that function is to large to post here, and involves many processes, but the relevant one is contained in this part of the code, occurring at line 426-449 in the file:
[code]
  /*
   * gimp-progress-install
   */
  procedure = gimp_procedure_new (progress_install_invoker);
  gimp_object_set_static_name (GIMP_OBJECT (procedure),
                               "gimp-progress-install");
  gimp_procedure_set_static_strings (procedure,
                                     "gimp-progress-install",
                                     "Installs a progress callback for the current plug-in.",
                                     "This function installs a temporary PDB procedure which will handle all progress calls made by this plug-in and any procedure it calls. Calling this function multiple times simply replaces the old progress callbacks.",
                                     "Michael Natterer <mitch@gimp.org>",
                                     "Michael Natterer",
                                     "2004",
                                     NULL);
  gimp_procedure_add_argument (procedure,
                               gimp_param_spec_string ("progress-callback",
                                                       "progress callback",
                                                       "The callback PDB proc to call",
                                                       FALSE, FALSE, TRUE,
                                                       NULL,
                                                       GIMP_PARAM_READWRITE));
  gimp_pdb_register_procedure (pdb, procedure);
  g_object_unref (procedure);[/code]
This code actually has very little to do with code which will need to be writen to define an external progress bar, but it defines the method which will install that code into the PDB, and therefore tells us how the method which is desired must be formed.  There are two parts of this code which are of specific interest, the first being [highlight=#80FFBF]procedure = gimp_procedure_new (progress_install_invoker);[/highlight] which tells us the name of the next function which is called in this chain of events, namely [highlight=#80FFBF]progress_install_invoker[/highlight].  The second is the first part of teh call to [highlight=#80FFBF]gimp_procedure_add_argument[/highlight], which indicates (after some digging) that only one argument can exist for this procedure.  Not the one to be writen, but the one which will register it.  So it proves that any external handler must be one and only one procedure.  By extension we then know that this procedure must be prepared to handle input values of more than one type, but what values?  That answer is to be found further into the chain.

The next step of the trace is [highlight=#80FFBF]progress_install_invoker[/highlight] found at line 208 of the same file.  As it's name implies, it is responsible for invoking the actual addition of the temporary script into the PDB.  It's code is short and simple.  Take the parametrs it receives, the temporary script, it's argument's, and it's return's and pass them off to the installing routine.

As can be seen in the code below, this function takes several parameters, but only two involve the temporary script, and they get inserted into the code automatically by either the registration action, or the call to [highlight=#80FFBF]pdb.gimp-progress-install[/highlight].  So someone writing an external progress bar has had their work simplified.  Additionally, it also gives the next link in the trace, in the call [highlight=#80FFBF]success = gimp_plug_in_progress_install (plug_in, progress_callback);[/highlight] occurring at line 225 in the file.
[code]
progress_install_invoker (
                          GimpProcedure      *procedure,
                          Gimp               *gimp,
                          GimpContext        *context,
        anchor                  GimpProgress       *progress,
                          const GValueArray  *args,
                          GError            **error)
{
  gboolean success = TRUE;
  const gchar *progress_callback;

  progress_callback = g_value_get_string (&args->values[0]);

  if (success)
    {
      GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;

      if (plug_in && plug_in->open)
        success = gimp_plug_in_progress_install (plug_in, progress_callback);
      else
        success = FALSE;
    }

  return gimp_procedure_get_return_values (procedure, success,
                                           error ? *error : NULL);
}
[/code]

[highlight=#80FFBF]gimp_plug_in_progress_install[/highlight] finally answers the question of what parameters are needed for an external progress bar.  The function is found at line 242 in the file gimpplugin-progress.c, and contains the following code:
[code]gboolean
gimp_plug_in_progress_install (GimpPlugIn  *plug_in,
                               const gchar *progress_callback)
{
  GimpPlugInProcFrame *proc_frame;
  GimpProcedure       *procedure;

  g_return_val_if_fail (GIMP_IS_PLUG_IN (plug_in), FALSE);
  g_return_val_if_fail (progress_callback != NULL, FALSE);

  procedure = gimp_pdb_lookup_procedure (plug_in->manager->gimp->pdb,
                                         progress_callback);

  if (! GIMP_IS_TEMPORARY_PROCEDURE (procedure)                ||
      GIMP_TEMPORARY_PROCEDURE (procedure)->plug_in != plug_in ||
      procedure->num_args                           != 3       ||
      ! GIMP_IS_PARAM_SPEC_INT32 (procedure->args[0])          ||
      ! G_IS_PARAM_SPEC_STRING   (procedure->args[1])          ||
      ! G_IS_PARAM_SPEC_DOUBLE   (procedure->args[2]))
    {
      return FALSE;
    }

  proc_frame = gimp_plug_in_get_proc_frame (plug_in);

  if (proc_frame->progress)
    {
      gimp_plug_in_progress_end (plug_in, proc_frame);

      if (proc_frame->progress)
        {
          g_object_unref (proc_frame->progress);
        anchor  proc_frame->progress = NULL;
        }
    }

  proc_frame->progress = g_object_new (GIMP_TYPE_PDB_PROGRESS,
                                       "pdb",           plug_in->manager->gimp->pdb,
                                       "context",       proc_frame->main_context,
                                       "callback-name", progress_callback,
                                       NULL);

  gimp_plug_in_progress_attach (proc_frame->progress);

  return TRUE;
}[/code]
This function takes as parameters two things, the temporary script, and the actual function call which must be made by The GIMP to use it.  Best of all it shows the sanity checks that the parameters undergo, so the parameters are finally identified.

If you look at the code which begins [highlight=#80FFBF]if (! GIMP_IS_TEMPORARY_PROCEDURE (procedure)[/highlight]... you will see the block which defines how the parameters of the the temporary script must appear.  For starters teh procedure must be a temporary procedure.  Secondly it may only take three parameters, and those parameters must be (in this order) an integer, a string, and a double (float in python terms)!

The only thing left in question is what are those parameters for, and how are they used?

[anchor=params goto=][b][size=150]The Parameters[/size][/b][/anchor]
To determine what the parameters were for, where they came from, and when they were received I cheated.  By now I was frustrated with crawling through unfamiliar code, so I simply wrote a quick Python script which accepted these parameters and printed them to the standard output.  The results were incredibly simple:
1.  An int and a string are received together only during a call from a progress bar initialization;
2.  A string by itself is only received during a call from an update text procedure;
3.  A double (float) is only received during a progress update procedure;
4.  A negative value float (-1) is received by itself, from a progress pulse;
5.  And nothing is received during a progress end procedure.[sup]11[/sup]

[anchor=conclusion goto=][b][size=150]Conclusion[/size][/b][/anchor]
Thus after six hours of digging, reading, and analysing source code, the way to build an external progress bar are finally revealed.  Write a python or scheme script, from within a python or C, or C++ program, which takes an int, a string, and a float or double (language of source program decides which), and register this a temporary script.  From this script perform your manipulations on the progress bar.  And in any scripts which you right ancillary to the main one, simply call the regular pdb progress functions, as they now have the temporary script as a handler.

As a final note, I also found that there is yet another way of doing this, which does not use the pdb functions, but only functions from within The GIMP's Python module 'gimp'.  This method requires writing four separate functions to handle the various procedures, but it is as of the date of writing this, yet to be fully tested.  Both a technical article and a simple tutorial for that method will eventually be posted as well.

A much simpler document will be published on gimpchat.com as an easy to follow tutorial on this subject in the near future.

[hr]
[anchor=notes]text[/anchor]
[b][size=150]Footnotes / References[/size][/b]
[b]1  Script-Fu / Python-Fu:[/b] These terms refer to the plugins inside The GIMP which allow Scheme (Script-Fu) and Python (Python-Fu) to be used to interact with The GIMP.  These are, generally speaking, interfaces or interpreters for their specific language.  The terms are formally implemented in The GIMP, and in The GIMP's help files.
[b]2 PDB:[/b]  The procedures in the data base correspond generally to the various available menu actions in The GIMP.  There are also valid entries for most scripts and plugins which have been installed, either by default, or by the user.  The contents of the PDB can be viewed directly from the Help Menu, via the Procedure Browser.
[b]3 temporary procedure:[/b] In essence a (dynamically) created script, created by another script, temporarily added to the PDB for use by the calling/creating script/plugin.  These scripts cease to exist (at the close of the primary script.[sup]A[/sup] ) after The GIMP is closed.
[b]4 Creating a temporary procedure:[/b] This is essentially done the same way any Script-Fu or Python-Fu script is done, however there is a difference in their creation.  They are created via a call to [highlight=#80FFBF]gimp.install_temp_proc[/highlight], not via the PDB.  [s]This is related to, but beyond, the scope of this document.[/s]
[b]5 [highlight=#80FFBF]gimp-progress-pulse[/highlight]:[/b] For the record I have yet to test this, but comments by developers lead me to believe this is merely the sending of a negative value in an [highlight=#80FFBF]gimp-progress-update[/highlight] call, so it should work.
[b]6 : [/b]  If anyone knows of a non-python way of implementing this, please let me know so I can update this.
[b]7: [highlight=#80FFBF]gimpfu.register[/highlight]:[/b]  Since most people do a call of [highlight=#80FFBF]from gimpfu imort *[/highlight] at the beginning of their scripts, this is usually seen as call to [highlight=#80FFBF]register()[/highlight].
[b]8 [highlight=#80FFBF]gimpfu.register[/highlight] insertions: [/b] If gimpfu.py detects certain elements in the parameters to a call to register, it inserts several different parameters automatically.  Pieces of the code representing this are shown below. (Comments inserted by me.)
[code]
# This code cannot be copy pasted, it will fail!  Only portions have been copied here.!
if need_compat_params and plugin_type == PLUGIN:
            # set this up in advance in case it is needed
            file_params = [(PDB_STRING, "filename", "The name of the file", ""),
                           (PDB_STRING, "raw-filename", "The name of the file", "")]

            if menu is None:
                pass
            elif menu.startswith("<Load>"): # Since this goes in the load menu, we must be loading something.
                params[0:0] = file_params
            elif menu.startswith("<Image>") or menu.startswith("<Save>"):            # <Image> means add this to part of the menu bar
                params.insert(0, (PDB_IMAGE, "image", "Input image", None))          # So it assumes an image and the currently
                params.insert(1, (PDB_DRAWABLE, "drawable", "Input drawable", None)) # selected drawable item are needed
                if menu.startswith("<Save>"):
                    params[2:2] = file_params                                                                # Save menu, so again with the file params

# And from a separate part of the program:
# This is inserted to every gimpfu script, but dynamically when needed.
params.insert(0, (PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"))

9 constants: As a note to new or novice script authors, and coders as well, when a code author defines a constant, it is wise to use it in your code. Constants are defined as places where changes can easily be made. But if your code uses the constant they provide, it (your code) will evolve with any changes made to the base code.
10: No changes have been made by me to this code except for altering some of the indentation, as the code tags in this forum do not seem to leave things 'as they are', as well as they should.
11: It was only as I wrote this article and reached this point that I discovered the existance of a pdb.gimp-progress-cancel. As of this time that has not been tested, but appears to work similar to the install procedure as it requires the pdb registered name of the callback as a parameter. Results of future testing of this element will be posted in the first comment after this post.


A: Found in a developer comment in the file gimp.c (line 668)

_________________
The answer was 42. The question is long forgotten. The computer that solved it is now destroyed.
The MK-2 has been built. Should this be the next question?
(Solve if you can ... ;) )
Image


Last edited by jazzon on Thu Aug 27, 2015 10:55 am, edited 23 times in total.

Share on Facebook Share on Twitter Share on Orkut Share on Digg Share on MySpace Share on Delicious Share on Technorati
Top
 Post subject: Re: (docs and code) pdb.gimp_progress_install pdb.gimp-progress-insta
PostPosted: Tue Aug 25, 2015 4:10 pm  (#2) 
Offline
GimpChat Member
User avatar

Joined: Aug 13, 2015
Posts: 312
Location: Somewhere between lost and found.
(reserved)

_________________
The answer was 42. The question is long forgotten. The computer that solved it is now destroyed.
The MK-2 has been built. Should this be the next question?
(Solve if you can ... ;) )
Image


Top
 Post subject: Re: (docs and code) pdb.gimp_progress_install pdb.gimp-progress-insta
PostPosted: Tue Aug 25, 2015 4:11 pm  (#3) 
Offline
GimpChat Member
User avatar

Joined: Aug 13, 2015
Posts: 312
Location: Somewhere between lost and found.
(reserved)

_________________
The answer was 42. The question is long forgotten. The computer that solved it is now destroyed.
The MK-2 has been built. Should this be the next question?
(Solve if you can ... ;) )
Image


Top
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC - 5 hours [ DST ]


   Similar Topics   Replies 
No new posts Attachment(s) Still Too Lost to make any progress

16

No new posts Attachment(s) Help on G'MIC install on Mac OS

6

No new posts Attachment(s) INSTALL. GIMP 2.10.10

35

No new posts GMIC install for Mac OS 10.13 worked

0

No new posts Rembg install issue

2



* Login  



Powered by phpBB3 © phpBB Group