It is currently Sat Aug 15, 2026 5:32 pm


All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 15 posts ] 
Author Message
 Post subject: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Sat Mar 01, 2014 8:31 pm  (#1) 
Offline
GimpChat Member

Joined: Mar 01, 2014
Posts: 8
Hi fellow Gimp Chat mebers. :) This is my first post here so I hope I won’t offend anyone by starting off asking for help.

My challenge is this: I want to be able to delete a display that has been created by a different plugin than the plugin that is going to close the display. This way I can open/create an image with one plugin, do stuff to it with another plugin that is called interactively (via the menu) and let that plugin close the image/display.

The only way I have found to do this is to pickle (using the python pickle module) the display object in the plugin that initiates the display and save the pickled display to a file and then un-pickle it in from the plugin that is going to close it again.

This actually works, but strangely it only works if the plugin that pickles the display takes parameters????
That’s really weird, but before going deeper into that, I would be happy to know if others have better methods for retaining display objects for use after the plugin that initiated the display has finished. Any help is greatly appreciated.


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: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Sat Mar 01, 2014 9:06 pm  (#2) 
Offline
Script Coder
User avatar

Joined: Apr 23, 2010
Posts: 1553
Location: not from Guildford after all
gowithpy wrote:
My challenge is this: I want to be able to delete a display that has been created by a different plugin than the plugin that is going to close the display. This way I can open/create an image with one plugin, do stuff to it with another plugin that is called interactively (via the menu) and let that plugin close the image/display.

It seems to me that you just have to pass the display ID to the second plug-in. To my knowledge, plug-ins can delete displays created by other plug-ins -- what they can't do is delete a display create by the user interface.

_________________
Any sufficiently primitive technology is indistinguishable from a rock.


Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Sat Mar 01, 2014 9:24 pm  (#3) 
Offline
GimpChat Member

Joined: Mar 01, 2014
Posts: 8
Thanks for your suggestion saulgoode, but unfortunately the procedure to delete a display: pdb.gimp_display_delete(disp), only accepts a display object. It will not accept the ID, which is basically just an integer. I am using python by the way. I don’t know if a display ID will be ok in scheme.
Any other way around this?


Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Sat Mar 01, 2014 9:38 pm  (#4) 
Offline
Script Coder
User avatar

Joined: Apr 23, 2010
Posts: 1553
Location: not from Guildford after all
I misunderstood that your first plug-in was not invoking the second plug-in.

If I were forced to program in Python, I would have that first plug-in (which creates the display) save the display object to a parasite. You still need to serialize the object to a string, but the second plug-in should not need to have any parameters.

The second plug-in should retrieve the parasite and deserialize its data to a display object.

_________________
Any sufficiently primitive technology is indistinguishable from a rock.


Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Sat Mar 01, 2014 9:58 pm  (#5) 
Offline
GimpChat Member

Joined: Mar 01, 2014
Posts: 8
Thanks saulgoode. The pickle protocol I have been using actually does just that. However, If I serialize it to an ASCII string it doesn’t work. It has to be written and read in binary mode. That’s why I am writing to a file and not a parasite.

The really strange thing is that if I in a plugin, that takes no parameters, pickle (serialize) a display object and immediately un-pickle it (just for testing) the display ID of the un-pickled display is zero.
But if the plugin that pickles the display takes parameters (defined in the register function), then the un-pickled display has the same ID as the original display and can be used to close the display just fine.

It seems a little clumsy to call a dummy parameter just to get around this, but maybe there is no way around that.


Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Sun Mar 02, 2014 8:31 am  (#6) 
Offline
Script Coder
User avatar

Joined: Oct 25, 2010
Posts: 4812
Another hackish way:

in first plugin:
  • store display ID as integer in a parasite

in second plugin:
  • create a Scheme script on the fly that retrieves the display ID and deletes the display (keep the SCM source i a hard-coded string and write it to file)
  • invoke it
  • remove it

_________________
Image


Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Sun Mar 02, 2014 8:49 am  (#7) 
Offline
Script Coder
User avatar

Joined: Apr 23, 2010
Posts: 1553
Location: not from Guildford after all
I don't understand why this doesn't work.

I can do the following in the Python-fu console:
import pickle
img = gimp.image_list()[0]
disp = gimp.Display(img)
p = gimp.Parasite('pickled-display', 0, pickle.dumps(disp))
gimp.parasite_attach(p)
and it works fine. I can open the Script-fu console and verify the parasite exists:

==> (gimp-parasite-find "pickled-display")
(("pickled-display" 0 "cgimp\n_id2display\np0\n(I3\ntp1\nRp2\n."))

However, if I write a Python plug-in doing the same thing (I omitted the registration):
import pickle
def new_display(img, drawable):
    disp = gimp.Display(img)
    p = gimp.Parasite('pickled-display', 0, pickle.dumps(disp))
    gimp.parasite_attach(p)
I get an error:

TypeError: can't pickle Display objects

_________________
Any sufficiently primitive technology is indistinguishable from a rock.


Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Sun Mar 02, 2014 11:23 am  (#8) 
Offline
Script Coder
User avatar

Joined: Oct 25, 2010
Posts: 4812
saulgoode wrote:
I don't understand why this doesn't work.

I can do the following in the Python-fu console:
import pickle
img = gimp.image_list()[0]
disp = gimp.Display(img)
p = gimp.Parasite('pickled-display', 0, pickle.dumps(disp))
gimp.parasite_attach(p)
and it works fine. I can open the Script-fu console and verify the parasite exists:

==> (gimp-parasite-find "pickled-display")
(("pickled-display" 0 "cgimp\n_id2display\np0\n(I3\ntp1\nRp2\n."))

However, if I write a Python plug-in doing the same thing (I omitted the registration):
import pickle
def new_display(img, drawable):
    disp = gimp.Display(img)
    p = gimp.Parasite('pickled-display', 0, pickle.dumps(disp))
    gimp.parasite_attach(p)
I get an error:

TypeError: can't pickle Display objects


What I don't understand if why the display is pickable in the console. If you can pick/unpickle, you create a duplicate object, and then delete one of them (which delete the image display). The remnant one becomes invalid.

_________________
Image


Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Mon Mar 03, 2014 4:41 pm  (#9) 
Offline
GimpChat Member

Joined: Mar 01, 2014
Posts: 8
saulgoode wrote:
I don't understand why this doesn't work.

I can do the following in the Python-fu console:
import pickle
img = gimp.image_list()[0]
disp = gimp.Display(img)
p = gimp.Parasite('pickled-display', 0, pickle.dumps(disp))
gimp.parasite_attach(p)
and it works fine. I can open the Script-fu console and verify the parasite exists:

==> (gimp-parasite-find "pickled-display")
(("pickled-display" 0 "cgimp\n_id2display\np0\n(I3\ntp1\nRp2\n."))

However, if I write a Python plug-in doing the same thing (I omitted the registration):
import pickle
def new_display(img, drawable):
    disp = gimp.Display(img)
    p = gimp.Parasite('pickled-display', 0, pickle.dumps(disp))
    gimp.parasite_attach(p)
I get an error:

TypeError: can't pickle Display objects


Any difference if you register? My guess is that registration actually does something to display objects. The display object contains functions and according to the pickle documentation, pickle can only handle objects that contain functions if they are defined at the top level of a module.

Actually, I don't know if that makes sense, but since I get different results if a function is registered with parameters vs. with no parameters, I conclude that the register function does something to display objects. Anyway, here's how I do it:

def saveobject(obj, filename):
    with open(filename, 'wb') as obj_dump:
        pickle.dump(obj, obj_dump, pickle.HIGHEST_PROTOCOL) # pickle.HIGHEST_PROTOCOL

I haven't tried it without registering the function, but I have tried pickling to a string as you do, which runs without errors as does unpickling. However the unpickled object is not accepted as a display object when delete is attempted. For that the pickling and unpickling have to be in binary mode.

Thanks for taking the time to dig into this saulgoode. I am very grateful.


Last edited by gowithpy on Mon Mar 03, 2014 4:54 pm, edited 1 time in total.

Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Mon Mar 03, 2014 4:52 pm  (#10) 
Offline
GimpChat Member

Joined: Mar 01, 2014
Posts: 8
ofnuts wrote:
What I don't understand if why the display is pickable in the console. If you can pick/unpickle, you create a duplicate object, and then delete one of them (which delete the image display). The remnant one becomes invalid.


Could it be that the display delete routine only extracts the ID from the display object making it irrelevant if it is a copy or not? Since Scheme only requires the ID, that might be the explanation.


Last edited by gowithpy on Mon Mar 03, 2014 6:12 pm, edited 1 time in total.

Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Mon Mar 03, 2014 5:20 pm  (#11) 
Offline
GimpChat Member

Joined: Mar 01, 2014
Posts: 8
ofnuts wrote:
Another hackish way:

in first plugin:
  • store display ID as integer in a parasite

in second plugin:
  • create a Scheme script on the fly that retrieves the display ID and deletes the display (keep the SCM source i a hard-coded string and write it to file)
  • invoke it
  • remove it


Thanks a lot ofnuts!
This has actually led me on the path to a bearable solution. :bigthup But if I were to follow your solution to the letter:

Would I have to register the Scheme script, save it in the scripts folder and then refresh scripts? If not, how do you run unregistered Scheme scripts from a python plugin (and vice versa for that matter).

Anyway, here is the solution I run with so far:

It turns out there is a script-fu interpreter in the pdb. So I can actually use just the display ID. For each display, I keep the display ID as well as a path to the display dump in a parasite. So, when I close a display, I unpickle the corresponding display dump and check the ID. If the ID is zero, I use the ID saved in the parasite. If not, I use the unpickled display object.
The reason that I don't go with the ID all the time is that the script-fu interpreter is very slow (deleting a display takes between one and two seconds).
Here is how I would delete a display with ID==4

pdb.plug_in_script_fu_eval('(gimp-display-delete 4)')


Again, thanks a lot for helping out!!!


Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Mon Mar 03, 2014 6:39 pm  (#12) 
Offline
Script Coder
User avatar

Joined: Oct 25, 2010
Posts: 4812
1) Yes
2) Nice find!

_________________
Image


Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Mon Mar 03, 2014 7:16 pm  (#13) 
Offline
Script Coder
User avatar

Joined: Apr 23, 2010
Posts: 1553
Location: not from Guildford after all
gowithpy wrote:
The reason that I don't go with the ID all the time is that the script-fu interpreter is very slow (deleting a display takes between one and two seconds).

The slowness here is not because of the Script-fu interpreter, it is owing to GTK having to deconstruct the display (particularly the menus) and deallocate the memory.

On my machine (only 2.2 GHz), deleting 100 displays takes 113 seconds, however, if I am in single-window mode (and thus the menus do not need to be destroyed), deleting those displays only takes 11 seconds. Similar benchmarks result from the building of new displays.

I can't imagine that Python could be a whole lot faster, other than by virtue of it taking advantage of multiple cores (Script-fu is single-threaded regardless of how many CPUs are available).

pdb.plug_in_script_fu_eval('(gimp-display-delete 4)')

Sort of a variation on Greenspun's Tenth Rule.

_________________
Any sufficiently primitive technology is indistinguishable from a rock.


Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Wed Mar 05, 2014 12:42 pm  (#14) 
Offline
Script Coder
User avatar

Joined: Oct 25, 2010
Posts: 4812
Looking for something else,, found the quick and undocumented answer: the "gimp" object has an '_id2display' method, that creates a Display using an ID. SO yu can save you display as an integer, and then do:

gimp.delete(gimp._id2display(saved_display_id))


Note that you still can't associate a display and an image, so you cannot close the display of a random image.

_________________
Image


Top
 Post subject: Re: What is the best way to retain GIMP displays between plugin calls?
PostPosted: Wed Apr 09, 2014 5:43 am  (#15) 
Offline
GimpChat Member

Joined: Mar 01, 2014
Posts: 8
gimp.delete(gimp._id2display(saved_display_id))


Thank you so much, ofnuts!!! This is really, really helpful :yes . My apologies for not checking back here sooner, but I honestly thought the thread had come to a conclusion, so I have been busy elsewhere.
This is really a better solution than I had hoped for. Fantastic find :tyspin . Now I can even ditch the (in this context) rather clumsy pickle routine :yes .

I have now tested this and in case anyone wonders, gimp._id2display does not create an entirely new display. Instead, it returns the display object of an existing display that has the passed display ID. Note, that passing an ID number that does not match any existing display does not result in any error. It just returns None, then.

Thanks again ofnuts.


Top
Post new topic Reply to topic  [ 15 posts ] 

All times are UTC - 5 hours [ DST ]



* Login  



Powered by phpBB3 © phpBB Group