• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Weird behaviour with dialog box and a variable in Lua script for Lightroom Plugin

Explorer ,
Jun 13, 2017 Jun 13, 2017

Copy link to clipboard

Copied

I'm writing a Lightroom plugin using the Lightroom SDK/API in the Lua language. I'm new to Lua. I've found a situation where my script only works if a Lightroom dialog box (LrDialogs.message("random message")) is present in one function.  If the first dialog isn't included then the program flow never steps into the for line in io.lines(outputFilePath) block, despite outputFilePath being populated with the correct path string.  Any ideas what's going on?

Here's the code:

------ read output file for exif and write to LR metadata ------
function parseOutput(outputFilePath)

   LrDialogs
.message("random message")

  
local tblOutput = {}   --to hold the output exif (1 column table, i.e. an array)
  
local tblImages = {}   --to hold the images and their relevant metadata

  
for line in io.lines(outputFilePath) do
       line
= removeWhitespaces(line)
       table
.insert(tblOutput, line)
  
end

  
local str = table.remove(tblOutput) --remove last line in table/file (it's log info, not exif)
   tblImages
= extractExif(tblOutput)  --pick out the exif key/value pairs and add to Image objects

end

[Moved out of the general (and dead) Coding Corner and into a product-specific SDK forum - moderator]

TOPICS
SDK

Views

858

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

LEGEND , Jun 14, 2017 Jun 14, 2017

The call to parseOutput() must occur only after Exiftool has finished.  So they should be executed sequentially in the same task.

Votes

Translate

Translate
LEGEND ,
Jun 13, 2017 Jun 13, 2017

Copy link to clipboard

Copied

I don't see anything obviously wrong with the code fragment you posted. But because LR often (usually) doesn't report errors that occur in all but the main task, it's easy to get fooled about what's really going with your plugin.  I strongly recommend you use a debugger.  Another thread in this forum describes how to connect LR to a particular IDE; alternatively, you can use by Debugging Toolkit, which provides a more limited but lighter-weight debugger specifically designed for LR's Lua environment.  Either one will require an investment of an hour or two, but it will quickly pay off. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jun 13, 2017 Jun 13, 2017

Copy link to clipboard

Copied

Thanks John.  I'm using ZeroBraneStudio IDE which allows me to debug.  I'm not sure how to dig deeper than just stepping through the program and watching the relevant variable.  outputFilePath is populated with the correct value, but io.lines(outputFilePath) doesn't trigger.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jun 13, 2017 Jun 13, 2017

Copy link to clipboard

Copied

io.lines(outputFilePath) doesn't trigger.

Do you mean that as you single step, the debugger never reaches that line?  But when you insert the call to message() and single-step, the debugger does reach that line? 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jun 13, 2017 Jun 13, 2017

Copy link to clipboard

Copied

Some thoughts:

- Perhaps io.lines() is raising an error and ZeroBrane isn't trapping the error, or perhaps you've got an enclosing pcall() or error handling in some caller that is silently catching the error.   Try adding this line before the "for":

local success, value = LrTasks.pcall (io.lines, outputFilePath)

if not success then

    LrDialogs.message ("io.lines error: " .. value)

    end

- When I revised my debugging toolkit last winter, I looked at the module tha ZeroBrane pretty closely.  I wasn't convinced that it properly handles LR's Lua execution environment. In particular, I wonder if it always catches errors in tasks other than the main task and in callbacks from LR API calls, and I wonder if it properly handles single-stepping and breakpoints in LR's tasks (which I'm pretty sure is not the standard Lua tasking / coroutines).   If my suspicions are right (and I'm not confident they are), try using logging ("print statements") to trace the execution. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jun 14, 2017 Jun 14, 2017

Copy link to clipboard

Copied

Thanks John.  I added that code and it returned success = true.  So it didn't invoke the error LrDialog.message.

The code reaches the for io.lines... block, but it never steps into the block (unless I add that random LrDialog before it).

Additional info:  The parseOutput() function is called from a LrTasks.startAsyncTask() function with a LrTasks.execute() call in it.   

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jun 14, 2017 Jun 14, 2017

Copy link to clipboard

Copied

Here's the code of the function that calls parseOutput().

exifToolAPI = {}

local LrMobdebug = import 'LrMobdebug'

local LrApplication = import 'LrApplication'

local LrDialogs = import 'LrDialogs'

local LrTasks = import 'LrTasks'

local LrPathUtils = import 'LrPathUtils'

local LrFileUtils = import 'LrFileUtils'

local LrDate = import 'LrDate'

local strErrorLogPath = ""

  if WIN_ENV then

  strErrorLogPath = "C:\Users\Foo\Bar\FUJI_LR_error.log"

  elseif MAC_ENV then

  strErrorLogPath = "/Users/foo/bar/FUJI_LR_error.log"

  end

local arrImage = {} --array of Image objects holding requested exif data

function exifToolAPI.open()

  LrMobdebug.start()

  local photos = {} -- array to hold LrPhoto objects

  local cat = LrApplication.activeCatalog() --active catalogue object

  local objET = {} --handle object to hold various ExifTool related properties

  local tmpdir = LrPathUtils.getStandardFilePath("temp") --path to system temp directory

  --create path to a unique txt file to hold commands to pass to ExifTool

  objET.commandFilePath = LrPathUtils.child(tmpdir, "ExiftoolCmds-" .. tostring(LrDate.currentTime()) .. ".txt")

  objET.outputFilePath = LrPathUtils.child(tmpdir, "ExiftoolOutput-" .. tostring(LrDate.currentTime()) .. ".txt")

  objET.errorLogFilePath = strErrorLogPath

  --create empty file

  local objFile = io.open(objET.outputFilePath, "w")

  objFile:close()

  local objFile = io.open(objET.errorLogFilePath, "w")

  objFile:close()

  --get image list

  photos = cat:getMultipleSelectedOrAllPhotos()

  LrTasks.startAsyncTask (

       function() -- Start exiftool in listen mode

           LrMobdebug.on()

            strPath = nil

            strCommand = cmdlineQuote() .. "/usr/local/bin/exiftool -iso -FilmMode -DynamicRangeSetting -AutoDynamicRange -DevelopmentDynamicRange -@ " .. objET.commandFilePath

            strParameters = ""

            for i,v in ipairs(photos) do

                 strPath = photos:getRawMetadata("path") .. "\n" --don't need quotes when using -@ file

                 strParameters = strParameters .. strPath

            end

       objFile = io.open(objET.commandFilePath, "w")

       objFile:write(strParameters)

       objFile:close()

       strCommand = strCommand .. " > " .. objET.outputFilePath .. " 2> " .. objET.errorLogFilePath ..  cmdlineQuote()

       local exitStatus = LrTasks.execute(strCommand)

       LrFileUtils.delete(objET.commandFilePath)

       end

       )

  parseOutput(objET.outputFilePath)

  LrFileUtils.delete(objET.outputFilePath)

end

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jun 14, 2017 Jun 14, 2017

Copy link to clipboard

Copied

One issue is that the task running Exiftool may not run at all before parseOutput() is called, or it may only run partially.  If that happens, then the file whose name is in objET.outputFilePath hasn't been created yet.   But when you insert the call to message() in parseOutput(), the dialog will pause its calling task, allowing the Exiftool task to run to completion, creating the output file before parseOutput() attempts to read it.

Why are you calling Exiftool in a task separate from the one calling parseOutput()?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jun 14, 2017 Jun 14, 2017

Copy link to clipboard

Copied

No reason other than just separating code.  Should it be within the async call?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Jun 14, 2017 Jun 14, 2017

Copy link to clipboard

Copied

The call to parseOutput() must occur only after Exiftool has finished.  So they should be executed sequentially in the same task.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jun 14, 2017 Jun 14, 2017

Copy link to clipboard

Copied

LATEST

That's fixed it.  I moved the call to parseOutput() into the async task block and it runs correctly.  Thanks for your help with this, John.  I'm now trying to write the fuji exif data to LR metadata and have encountered another problem.  I'll start a new thread for that issue.  Cheers.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines