Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Try to transform a python script to go/back/and forth between Darktable and GIMP
#1
Python 
The original script is for LightZone and GIMP, which works nicely, from GIMP to LZ and back to GIMP

So my changes works nicely from GIMP to Darktable, but to get back from Darktable to GIMP again, I got an error

   

Any idea, help? I can't think anymore my head gets narrow vision now and is on fire Big Grin

the code I did some change for Darktable:
Code:
#!/usr/bin/env python

'''
Lightzone.py
call LightZone passing the active layer as a temp file.

Author:
Rob Antonishen
Modified by
Partha Bagchi
Modified by Martin Pohl
Modified by
Stefano Azzi
Modified by
Masahiro Kitagawa

Version:
0.8c Made it compatible with Windows, macOS, and Linux
0.8b Made it specific for LightZone
0.8 Made it specific to Nik Collection
0.7 fixed file save bug where all files were png regardless of extension
0.6 modified to allow for a returned layer that is a different size
   than the saved layer for
0.5 file extension parameter in program list.
0.4 modified to support many optional programs.

this script is modelled after the mm extern LabCurves trace plugin
by Michael Munzert http://www.mm-log.com/lab-curves-gimp

and thanks to the folds at gimp-chat has grown a bit ;)

License:

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 3 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

The GNU Public License is available at
http://www.gnu.org/copyleft/gpl.html

'''

from gimpfu import *
import shlex
import subprocess
import os, sys
import tempfile
from shutil import copyfile

def gimp_log( text ):
    pdb.gimp_message( text )

def plugin_main( image, drawable, visible ):
 pdb.gimp_image_undo_group_start(image)
 
 # Copy so the save operations doesn't affect the original
 if visible == 0:
   # Save in temporary.  Note: empty user entered file name
   temp = pdb.gimp_image_get_active_drawable(image)
 else:
   # Get the current visible
   temp = pdb.gimp_layer_new_from_visible(image, image, "Darktable")
   image.add_layer(temp, 0)

 buffer = pdb.gimp_edit_named_copy(temp, "ShellOutDTTemp")

 #save selection if one exists
 hassel = pdb.gimp_selection_is_empty(image) == 0
 if hassel:
   savedsel = pdb.gimp_selection_save(image)

 tempimage = pdb.gimp_edit_named_paste_as_new(buffer)
 pdb.gimp_buffer_delete(buffer)
 if not tempimage:
   raise RuntimeError
 pdb.gimp_image_undo_disable(tempimage)

 tempdrawable = pdb.gimp_image_get_active_layer(tempimage)
 
 tempfilename = os.path.join(tempfile.gettempdir(), "ShellOutDTTempFile.tif" )
 

 # !!! Note no run-mode first parameter, and user entered filename is empty string
 pdb.gimp_progress_set_text ("Saving a copy")
 pdb.gimp_file_save(tempimage, tempdrawable, tempfilename, tempfilename)

 # Build command line call
 if sys.platform.startswith('win'):
   progtorun = "\"" + os.environ["ProgramW6432"] + "\\darktable\\darktable.exe\""
 elif sys.platform.startswith('darwin'):
   progtorun = "open -W -a \"darktable.app\""
 elif sys.platform.startswith('linux'):
   progtorun = "\"darktable\""
 command = progtorun + " \"" + tempfilename + "\""
 args = shlex.split(command)

 # Invoke external command
 pdb.gimp_progress_set_text ("calling Darktable...")
 pdb.gimp_progress_pulse()
 child = subprocess.Popen(args, shell=False)
 child.communicate()

 # put it as a new layer in the opened image
 try:
    darktablefile = os.path.join(tempfile.gettempdir(), "ShellOutDTTempFile.tif")    
    copyfile( darktablefile, tempfilename )
    newlayer2 = pdb.gimp_file_load_layer(tempimage, tempfilename)
 except:
   RuntimeError
    
 tempimage.add_layer(newlayer2,-1)
 buffer = pdb.gimp_edit_named_copy(newlayer2, "ShellOutDTTemp")

 if visible == 0:
   drawable.resize(newlayer2.width,newlayer2.height,0,0)
   sel = pdb.gimp_edit_named_paste(drawable, buffer, 1)
   drawable.translate((tempdrawable.width-newlayer2.width)/2,(tempdrawable.height-newlayer2.height)/2)
 else:
   temp.resize(newlayer2.width,newlayer2.height,0,0)
   sel = pdb.gimp_edit_named_paste(temp, buffer, 1)
   temp.translate((tempdrawable.width-newlayer2.width)/2,(tempdrawable.height-newlayer2.height)/2)

 pdb.gimp_buffer_delete(buffer)
 pdb.gimp_edit_clear(temp)    
 pdb.gimp_floating_sel_anchor(sel)

 #load up old selection
 if hassel:
   pdb.gimp_selection_load(savedsel)
   image.remove_channel(savedsel)
 
 # cleanup
 os.remove(tempfilename)  # delete the temporary file
 gimp.delete(tempimage)   # delete the temporary image
 os.remove( darktablefile )     # delete the locally created LZ file
 # Note the new image is dirty in Gimp and the user will be asked to save before closing.
 pdb.gimp_image_undo_group_end(image)
 gimp.displays_flush()


register(
       "python_fu_Darktable",
       "Call Darktable",
       "Call Darktable",
       "Rob Antonishen",
       "Copyright 2011 Rob Antonishen",
       "2011",
       "<Image>/Filters/Photo Editors/Darktable",
       "RGB*, GRAY*",
       [ (PF_RADIO, "visible", "Layer:", 1, (("new from visible", 1),("current layer",0))),
       ],
       [],
       plugin_main,
       )

main()


Original code of Lightzone.py
Code:
#!/usr/bin/env python

'''
Lightzone.py
call LightZone passing the active layer as a temp file.

Author:
Rob Antonishen
Modified by
Partha Bagchi
Modified by Martin Pohl
Modified by
Stefano Azzi
Modified by
Masahiro Kitagawa

Version:
0.8c Made it compatible with Windows, macOS, and Linux
0.8b Made it specific for LightZone
0.8 Made it specific to Nik Collection
0.7 fixed file save bug where all files were png regardless of extension
0.6 modified to allow for a returned layer that is a different size
    than the saved layer for
0.5 file extension parameter in program list.
0.4 modified to support many optional programs.

this script is modelled after the mm extern LabCurves trace plugin
by Michael Munzert http://www.mm-log.com/lab-curves-gimp

and thanks to the folds at gimp-chat has grown a bit ;)

License:

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 3 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

The GNU Public License is available at
http://www.gnu.org/copyleft/gpl.html

'''

from gimpfu import *
import shlex
import subprocess
import os, sys
import tempfile
from shutil import copyfile

def gimp_log( text ):
    pdb.gimp_message( text )

def plugin_main( image, drawable, visible ):
  pdb.gimp_image_undo_group_start(image)
  
  # Copy so the save operations doesn't affect the original
  if visible == 0:
    # Save in temporary.  Note: empty user entered file name
    temp = pdb.gimp_image_get_active_drawable(image)
  else:
    # Get the current visible
    temp = pdb.gimp_layer_new_from_visible(image, image, "LightZone")
    image.add_layer(temp, 0)

  buffer = pdb.gimp_edit_named_copy(temp, "ShellOutTemp")

  #save selection if one exists
  hassel = pdb.gimp_selection_is_empty(image) == 0
  if hassel:
    savedsel = pdb.gimp_selection_save(image)

  tempimage = pdb.gimp_edit_named_paste_as_new(buffer)
  pdb.gimp_buffer_delete(buffer)
  if not tempimage:
    raise RuntimeError
  pdb.gimp_image_undo_disable(tempimage)

  tempdrawable = pdb.gimp_image_get_active_layer(tempimage)
  
  tempfilename = os.path.join(tempfile.gettempdir(), "ShellOutTempFile.tif" )
  

  # !!! Note no run-mode first parameter, and user entered filename is empty string
  pdb.gimp_progress_set_text ("Saving a copy")
  pdb.gimp_file_save(tempimage, tempdrawable, tempfilename, tempfilename)

  # Build command line call
  if sys.platform.startswith('win'):
    progtorun = "\"" + os.environ["ProgramW6432"] + "\\LightZone\\LightZone.exe\""
  elif sys.platform.startswith('darwin'):
    progtorun = "open -W -a \"LightZone.app\""
  elif sys.platform.startswith('linux'):
    progtorun = "\"lightzone\""
  command = progtorun + " \"" + tempfilename + "\""
  args = shlex.split(command)

  # Invoke external command
  pdb.gimp_progress_set_text ("calling LightZone...")
  pdb.gimp_progress_pulse()
  child = subprocess.Popen(args, shell=False)
  child.communicate()

  # put it as a new layer in the opened image
  try:
    lightzonefile = os.path.join(tempfile.gettempdir(), "ShellOutTempFile_lzn.jpg")    
    copyfile( lightzonefile, tempfilename )
    newlayer2 = pdb.gimp_file_load_layer(tempimage, tempfilename)
  except:
    RuntimeError
    
  tempimage.add_layer(newlayer2,-1)
  buffer = pdb.gimp_edit_named_copy(newlayer2, "ShellOutTemp")

  if visible == 0:
    drawable.resize(newlayer2.width,newlayer2.height,0,0)
    sel = pdb.gimp_edit_named_paste(drawable, buffer, 1)
    drawable.translate((tempdrawable.width-newlayer2.width)/2,(tempdrawable.height-newlayer2.height)/2)
  else:
    temp.resize(newlayer2.width,newlayer2.height,0,0)
    sel = pdb.gimp_edit_named_paste(temp, buffer, 1)
    temp.translate((tempdrawable.width-newlayer2.width)/2,(tempdrawable.height-newlayer2.height)/2)

  pdb.gimp_buffer_delete(buffer)
  pdb.gimp_edit_clear(temp)    
  pdb.gimp_floating_sel_anchor(sel)

  #load up old selection
  if hassel:
    pdb.gimp_selection_load(savedsel)
    image.remove_channel(savedsel)
  
  # cleanup
  os.remove(tempfilename)  # delete the temporary file
  gimp.delete(tempimage)   # delete the temporary image
  os.remove( lightzonefile )     # delete the locally created LZ file
  # Note the new image is dirty in Gimp and the user will be asked to save before closing.
  pdb.gimp_image_undo_group_end(image)
  gimp.displays_flush()


register(
        "python_fu_Lightzone",
        "Call LightZone",
        "Call LightZone",
        "Rob Antonishen",
        "Copyright 2011 Rob Antonishen",
        "2011",
        "<Image>/Filters/Photo Editors/LightZone",
        "RGB*, GRAY*",
        [ (PF_RADIO, "visible", "Layer:", 1, (("new from visible", 1),("current layer",0))),
        ],
        [],
        plugin_main,
        )

main()
Patrice
Reply
#2
The culprit code is like this:
Code:
 # put it as a new layer in the opened image
 try:
    darktablefile = os.path.join(tempfile.gettempdir(), "ShellOutDTTempFile.tif")    
    copyfile( darktablefile, tempfilename )
    newlayer2 = pdb.gimp_file_load_layer(tempimage, tempfilename)
 except:
   RuntimeError
    
 tempimage.add_layer(newlayer2,-1)


The message you have is clear: variable newlayer2 doesn't exist, and in Python this means it has never been assigned.

Since it seems to have been assigned in the few lines above, WTF?

Notice that the lines above are bracketed by a try/except, so if anything bad happens in the first three lines, instead of stopping with an exception, the Python code jumps to the code after the except. So, something bad happened in the three lines, but where?

  • first line not too likely (join() is mostly about splicing string, it doesn't check if the name is valid)
  • copyfile is a candidate if the path created in the previous line is bad, or there are other systems problems (no enough space, write permissions)
  • pdb.gimp_file_load_layer is another candidate if the tempimage doesn't exist or if the file cannot be loaded for some reason (not an image file...)
And why does the code continue executing? It turns out that the code after the except does nothing. RuntimeException is the name of a class, if it is executed by the code, nothing happens (in the console if just prints information on the class). I believe it should have been raise RuntimeException.

But even then... this code has a major flaw: it replaces the exception raised by the copy or load steps (that could have explanatory messages), by an anonymous exception (that doesn't tell you anything). So  it is pointless... you can just as well remove it, this won't prevent errors but at least you will have a better idea of where and what they are.

Code:
 # put it as a new layer in the opened image
 darktablefile = os.path.join(tempfile.gettempdir(), "ShellOutDTTempFile.tif")    
 copyfile( darktablefile, tempfilename )
 newlayer2 = pdb.gimp_file_load_layer(tempimage, tempfilename)
 tempimage.add_layer(newlayer2,-1)

(although why you need to copy the file to a new file to load it is beyond me)

Also do yourself a favor and replace line #81 by
Code:
   raise RuntimeError('Cannot create temporary image')

And there are still a few weird bits left Big Grin
Reply
#3
(01-31-2024, 09:08 AM)Ofnuts Wrote: The culprit code is like this:
Code:
 # put it as a new layer in the opened image
 try:
    darktablefile = os.path.join(tempfile.gettempdir(), "ShellOutDTTempFile.tif")    
    copyfile( darktablefile, tempfilename )
    newlayer2 = pdb.gimp_file_load_layer(tempimage, tempfilename)
 except:
   RuntimeError
    
 tempimage.add_layer(newlayer2,-1)


The message you have is clear: variable newlayer2 doesn't exist, and in Python this means it has never been assigned.

Since it seems to have been assigned in the few lines above, WTF?

Ok, I don't feel alone anymore to have thought "WTF it's here" Big Grin

(01-31-2024, 09:08 AM)Ofnuts Wrote: Notice that the lines above are bracketed by a try/except, so if anything bad happens in the first three lines, instead of stopping with an exception, the Python code jumps to the code after the except. So, something bad happened in the three lines, but where?

  • first line not too likely (join() is mostly about splicing string, it doesn't check if the name is valid)
  • copyfile is a candidate if the path created in the previous line is bad, or there are other systems problems (no enough space, write permissions)
  • pdb.gimp_file_load_layer is another candidate if the tempimage doesn't exist or if the file cannot be loaded for some reason (not an image file...)
And why does the code continue executing? It turns out that the code after the except does nothing. RuntimeException is the name of a class, if it is executed by the code, nothing happens (in the console if just prints information on the class). I believe it should have been raise RuntimeException.

But even then... this code has a major flaw: it replaces the exception raised by the copy or load steps (that could have explanatory messages), by an anonymous exception (that doesn't tell you anything). So  it is pointless... you can just as well remove it, this won't prevent errors but at least you will have a better idea of where and what they are.

Code:
 # put it as a new layer in the opened image
 darktablefile = os.path.join(tempfile.gettempdir(), "ShellOutDTTempFile.tif")    
 copyfile( darktablefile, tempfilename )
 newlayer2 = pdb.gimp_file_load_layer(tempimage, tempfilename)
 tempimage.add_layer(newlayer2,-1)

(although why you need to copy the file to a new file to load it is beyond me)

Also do yourself a favor and replace line #81 by
Code:
   raise RuntimeError('Cannot create temporary image')

And there are still a few weird bits left Big Grin

Thanks a lot Ofnuts to have take time to read that code, and help.
I'm gonna do the changes you suggest and see further what's going on,

Also maybe I need to search for the 3 previous version (0.7 to 0.8c) for comparison... I'll see if I get stuck (maybe it's too buggy)
Again thank you very much.
Patrice
Reply


Forum Jump: