Copy link to clipboard
Copied
I am new to lua and am trying to create a plugin to assist my use of keywords in Lightroom Classic. I am creating my own UI for this tool and intend to include buttons to activate certain functions. However I am having problems getting a keyword variable to be visible inside a function attached as an action to a button. I can get a string variable passed in no problem, but not a keyword variable.
Here is the code, the variable KeywordIn is one of the top level kewwords in my catalog -
local function show_Dialog(KeywordIn)
local kw_In_Name = KeywordIn:getName()
outputToLog("in show_Dialog, input keyword - " .. kw_In_Name )
LrFunctionContext.callWithContext( "show_Dialog", function( context )
local props = LrBinding.makePropertyTable( context )
props.myObservedString = "Birds (Aves)"
local f = LrView.osFactory()
local staticTextValue = f:static_text {
title = props.myObservedString,
}
local updateField = f:edit_field {
immediate = true,
value = props.myObservedString -- "Birds (Aves)"
}
local c = f:column {
spacing = f:dialog_spacing(),
f:row {
f:static_text {
alignment = "right",
width = LrView.share "label_width",
title = "Class to be Searched: "
},
updateField,
f:push_button {
title = "Run",
-- When the 'Run' button is clicked.
action = function()
outputToLog( "Run button clicked." )
staticTextValue.text_color = LrColor ( 0, 0, 0)
props.myObservedString = updateField.value
local kwClassName = updateField.value
outputToLog("Data from Field - " .. kwClassName)
outputToLog("keyword Inputted, name passed as string - " .. kw_In_Name)
outputToLog("keyword Inputted, name from getName - " .. KeywordIn:getName() )
end
},
}, -- end of row
} -- end of column
LrDialogs.presentModalDialog {
title = "Trial Dialog",
contents = c
}
end)
end
When I run this plugin it works fine until I click on the run button. All works apart from the line outputting the keyword name using getName, which is not printed to the debug viewer.
Can anyone tell me what I am doing wrong please?
Pete Rowland
1 Correct answer
Sorry, thinko on my part. The task should be created inside the action function:
action = function() LrTasks.startAsyncTask (function ()
outputToLog( "Run button clicked." )
staticTextValue.text_color = LrColor ( 0, 0, 0)
props.myObservedString = updateField.value
local kwClassName = updateField.value
outputToLog("Data from Field - " .. kwClassName)
outputToLog("keyword Inputted, name passed as string - " .. kw_In_Name)
outputToLog("keyword Inputted, name from getName
...
Copy link to clipboard
Copied
[This post contains formatting and embedded images that don't appear in email. View the post in your Web browser.]
The callback functions passed to LrView methods are generally called from the main task, including the "action" function of an f:push_button(). But most of the methods of LrKeyword (and many other SDK objects) must be called from what the API documentation calls an "asynchronous" task"
An "asynchronous" task is simply one created by LrTask.
When one of these methods is executed on the main task, you'll get this mysterious runtime error:
You're not seeing that because LR silently stops execution of the task and doesn't display an error message. If you want errors to be trapped and an error message to be display, you'll need to use this idiom to wrap the body of every tasks and callback function:
LrFunctionContext.callWithContext ("", function (context)
LrDialogs.attachErrorDialogToFunctionContext (context)
...your code...
end)
Pretty tedious. Or you can use my lightweight debugging toolkit, which provides the more concise Debug.showErrors() that does the same thing. For example:
action = showErrors (function()
outputToLog( "Run button clicked." )
...
end)}}}
In general, it's very difficult to develop LR plugins with just print statements. I recommend you either use my toolkit or the Zerobrane IDE. My toolkit better understands LR's particular task architecture and will give better stack traces, error handling, and pretty-printing of nested tables. But the Zerobrane is a full-fledged IDE used with many apps that include Lua.
To get back to the original problem, the easiest way to implement that action function is run it as a separate task:
action = LrTasks.startAsyncTask (showErrors (function()
outputToLog( "Run button clicked." )
...
end))
Copy link to clipboard
Copied
Note that with both my toolkit and Zerobrane, you'll need to annotate your code with showErrors() or Zerobrane's equivalent.
Copy link to clipboard
Copied
Many thanks John - understanding improved and getName function now working within the action code. I've downloaded your debugger but as yet have not had time to get to grips with it. I will soon, many thanks again.
However, I now have a different problem - the action function now operates immediately on opening the dialog but not on clicking the run button - any thoughts?
Copy link to clipboard
Copied
"The action function now operates immediately on opening the dialog but not on clicking the run button - any thoughts?"
Post the code?
Copy link to clipboard
Copied
local LrApplication = import 'LrApplication' -- Import LR namespace which provides access to active catalog
local LrDialogs = import 'LrDialogs' -- Import LR namespace for user dialog functions
local LrProgressScope = import 'LrProgressScope'
local LrTasks = import 'LrTasks' -- Import functions for starting async tasks
local LrFileUtils = import "LrFileUtils"
local LrPathUtils = import 'LrPathUtils'
local LrPrefs = import 'LrPrefs'
-- Imports for dialog
local LrFunctionContext = import 'LrFunctionContext'
local LrBinding = import 'LrBinding'
local LrView = import 'LrView'
local LrLogger = import 'LrLogger'
local LrColor = import 'LrColor'
-- Preferences:
local prefs = LrPrefs.prefsForPlugin(_PLUGIN.id)
local topLevelKeywords = {}
-- Create the logger and enable the print function.
local myLogger = LrLogger( 'libraryLogger' )
myLogger:enable( "print" ) -- Pass either a string or a table of actions.
local function outputToLog( message )
myLogger:trace( message )
end
local function show_Dialog(KeywordIn)
local kw_In_Name = KeywordIn:getName()
outputToLog("in show_Dialog, input keyword - " .. kw_In_Name )
LrFunctionContext.callWithContext( "show_Dialog", function( context )
local props = LrBinding.makePropertyTable( context )
props.myObservedString = "Birds (Aves)"
local f = LrView.osFactory()
local staticTextValue = f:static_text {
title = props.myObservedString,
}
local updateField = f:edit_field {
immediate = true,
value = props.myObservedString -- "Birds (Aves)"
}
local c = f:column {
spacing = f:dialog_spacing(),
f:row {
f:static_text {
alignment = "right",
width = LrView.share "label_width",
title = "Class to be Searched: "
},
updateField,
f:push_button {
title = "Run",
-- When the 'Run' button is clicked.
action = LrTasks.startAsyncTask (function()
outputToLog( "Run button clicked." )
staticTextValue.text_color = LrColor ( 0, 0, 0)
props.myObservedString = updateField.value
local kwClassName = updateField.value
outputToLog("Data from Field - " .. kwClassName)
outputToLog("keyword Inputted, name passed as string - " .. kw_In_Name)
outputToLog("keyword Inputted, name from getName - " .. KeywordIn:getName() )
end)
},
}, -- end of row
} -- end of column
LrDialogs.presentModalDialog {
title = "Trial Dialog",
contents = c
}
end)
end
LrTasks.startAsyncTask (function()
catalog = LrApplication.activeCatalog()
local topLevelKeywords = catalog:getKeywords()
local kw
local kwFound
for _, kw in pairs(topLevelKeywords) do
if kw:getName() == "NATURAL_WORLD" then
kwFound = kw
break
end
end
outputToLog("First Keyword in Main Routine - " .. kwFound:getName())
show_Dialog(kwFound)
end)
Copy link to clipboard
Copied
This code is just a trial to get the button code working, I'm using your debugger on other codes to get my head around it. I'm working around the original problem by using the dialog to just return string data and then running the active code in the main task, using LrTasks.startAsyncTask.
Copy link to clipboard
Copied
Sorry, thinko on my part. The task should be created inside the action function:
action = function() LrTasks.startAsyncTask (function ()
outputToLog( "Run button clicked." )
staticTextValue.text_color = LrColor ( 0, 0, 0)
props.myObservedString = updateField.value
local kwClassName = updateField.value
outputToLog("Data from Field - " .. kwClassName)
outputToLog("keyword Inputted, name passed as string - " .. kw_In_Name)
outputToLog("keyword Inputted, name from getName - " .. KeywordIn:getName() )
end) end
Copy link to clipboard
Copied
Many thanks John, that did the trick.
Copy link to clipboard
Copied
If you ever start doing Lua programming tutorials, sign me up. While I've made a lot of plugins, they're mostly Web SDK and metadata based so not hugely divergent from the SDK samples. I'd love to do more.

