johnrellis
LEGEND
johnrellis
LEGEND
Activity
‎Aug 04, 2010
04:52 PM
Yeah, the documentation for LrTasks.execute doesn't specify how the command line is interpreted -- does LR parse it (using what syntactic rules), or does it hand to the platform shell (cmd on Windows, sh on Mac) to parse and execute? I use the following workaround: Write the command line to a temp bat file ending in ".bat". Then on Windows, execute the command line: cmd /c "tempfile" and on Mac, execute the command line with: sh "tempfile" The code would look like: local commandLine = string.format ('%s "%s"', WIN_ENV and "cmd /c" or "sh ", tempFilename) LrTasks.execute (commandLine) This ensures you have absolute control over how the command is parsed and interpreted.
... View more
‎Aug 04, 2010
08:25 AM
Right, thanks.
... View more
‎Aug 02, 2010
10:01 AM
I think it would be simpler for everyone if Adobe were to provide a method catalog:getAllKeywords () that returned an array of all the keywords. There's no engineering challenge in doing that -- it would surely execute faster than the 15 msecs it currently takes to invoke any one of the LrKeyword methods. You can get a feel for how fast it should be by invoking Metadata > Export Keywords.
... View more
‎Jul 28, 2010
03:52 PM
1 Upvote
I capture the output of some command-line programs by redirecting their output to a temp file and then reading the temp file. That seems to be portable between Windows and Mac (though I haven't tested it yet with Mac). Exiftool uses standard error as well as standard out, so to capture both in the same file, you'll need to do something like: exiftool ... > output.log 2>&1 The "2>&1" supposedly has to go last on the command line on Windows. I looked into capturing the output of Exiftool using the -X option, which writes XML output, and then using the SDK's LrXml to read it. But I ended up not needing to go that route and don't have any meaninful experience to report.
... View more
‎Jul 25, 2010
09:01 AM
If you want lr-plugin-init to be called when lightroom first starts, you have to have a custom metadata item (if you dont want one - declare a dummy hidden one). Excellent discovery, thanks.
... View more
‎Jul 20, 2010
03:07 PM
1 Upvote
The original poster was interested in computing checksums of large files, which doesn't require the entire file in memory. Out of curiosity, I timed reading a very large file with various chunk sizes, ranging from 8K to 128M. This confirms Rob's quickie timings -- there is little difference in performance using chunk sizes from 8K through 2M. But larger than 2M and performance starts degrading seriously, as I suspected: Chunk size Seconds Ratio 8K 93 1.00 32K 90 0.97 128K 95 1.02 512K 91 0.98 2M 98 1.05 8M 103 1.11 32M 119 1.28 128M 176 1.89 These times are an average of 3 runs, each run reading a 6 GB file, ensuring that Windows Vista 64 (with 6 GB of memory) wouldn't be able to cache the file in memory. What I suspect is going on: At the OS level, Windows is reading from the disk into its cache in a uniformly large chunk size, regardless of the size passed to Lua's file:read(). But at the larger chunk sizes, the program incurs higher overhead allocating strings of the given chunk size, most likely because Lua memory allocation is optimized for small objects.
... View more
‎Jul 20, 2010
04:51 AM
Until you run out of memory, big is better. I strongly suspect that isn't true of Lua in Lightroom, though running actual tests would be the best way to determine that. Allocating huge contiguous chunks of private memory can strain both the OS's virtual memory and the allocator of the garbage collector. Here's what Programming in Lua, 2nd Ed by Roberto Ierusalimschy says: Usually, in Lua, it is faster to read a file as a whole than to read it line by line. However, sometimes we must face a big file (say, tens or hundreds megabytes) for which it is not reasonable to read it all at once. If you want to handle such big files with maximum performance, the fastest way is to read them in reasonably large chunks (e.g., 8 Kbytes each).
... View more
‎Jul 15, 2010
02:31 AM
[Oops, hit send too soon.] Or use LrShutdownPlugin? The LrShutdownPlugin would set a global variable "stopAsyncTasks" to true, and the background task polls that variable.
... View more
‎Jul 15, 2010
02:29 AM
Or use LrShutdownPlugin?
... View more
‎Jul 15, 2010
02:23 AM
A different solution: In the plugin's global environment, define a global variable "sentinel" whose value is a dummy table: sentinel = {value = "dummy table"} In the background task, create a Lua weak table that references the value of "sentinel". The background task periodically checks the value of the weak reference, and if it is nil, then you know the that the dummy table has been garbage collected, indicating that the global environment of the plugin has gonne away, indicating the plugin has been reloaded. A potential problem with this solution: You don't know when the garbage collector will actually run. It may be that in practice, it runs very frequently, but I don't know that.
... View more
‎Jul 15, 2010
02:08 AM
A hack workaround: When the persistent background task first starts, it writes the current time into a temp file and remembers that time. It reads the contents of the file every n seconds, and if it changes, it exits.
... View more
‎Jul 15, 2010
01:50 AM
In terms of performance, you'll most likely get the best performance reading in chunks much smaller than 100 MB, e.g. 1 MB or even smaller. You might see severely degraded performance trying to read an entire 500 MB file into memory. In terms of programming convenience, of course, it's nice to read an entire file all at once into a string.
... View more
‎Jul 09, 2010
03:04 PM
Yeah, there are many advantages to a dynamic language like Lua, but a big disadvantage is that pretty much any string of garbage can be executed, which makes PUI (programming under the influence) dangerous 😞
... View more
‎Jul 09, 2010
08:33 AM
To build on John's reply, double-check that the metadata definition declares the field browsable and searchable, e.g. metadataFieldsForPhotos = { {id = 'type', title = "Type", dataType = 'string', browsable = true, searchable = true},
... View more
‎Jul 08, 2010
08:18 PM
Jeff, thanks for the details -- all makes sense. I too am hoping that Adobe will invest in the performance of the SDK APIs. Even doing simple things like enumerating all the keywords takes two orders of magnitude too long.
... View more
‎Jul 08, 2010
12:01 PM
I have around 15k images and it takes only about 5 seconds to run this task on all images. That's much better performance than I've observed, so perhaps you could help me understand how to do better? Is that 5 seconds to run an incremental update, verifying that the shadow and actual values are identical? Does it also take 5 seconds to set the initial value of the shadow field, or does that take much longer? I assume you're calling the batch operations to get the metadata values? In my tests, batchGetRawMetadata() and batchGetFormattedMetadata() can do about 700 photos/sec on dateTimeOriginal, while you're oberving about 3000 photos/second. My computer is a little slow (dual-core 1.9 GHz, 4 GB memory, 7200 RPM disk, Windows 7 32-bit), but that shouldn't account for a 4x difference. Perhaps batchGetRawMetadata ("path") is reading from LR's in-memory data structures, while batchGetRawMetadata ("dateTimeOriginal") is doing a SQL query to the database?
... View more
‎Jul 08, 2010
09:51 AM
I have a couple of plugins that get and set view filters using catalog:getCurrentViewFilter() and setViewFilter(). You can reverse engineer the undocumented details (especially for the column browser) by saving filter presets and looking at their definitions in C:\Users\user\AppData\Roaming\Adobe\Lightroom\Filter Presets. It all seems to work pretty well. Sometimes however, the column browser doesn't appear to update itself properly when you set a view filter with multiple columns and multiple items selected -- but the filter appears to work correctly and show the right photos. I haven't discovered any mechanism to define new column types (e.g. process verson). The workaround of adding a plugin metadata field and then filtering on that is slow when you want to filter a large number of photos -- my tests indicate Lightroom can only set plugin metadata at the rate of a hundred or so photos a second.
... View more
‎Jul 07, 2010
02:56 PM
It seems like a basic design misfeature in the catalog (independent of the SDK), and it's biting users, as evidenced by the posts in the parent forum. I believe both Mac (by default) and Windows (always) ignore case when comparing paths. To cope, I guess we need to pretend that the following invariants are true: - The path recorded in the catalog has the same case as recorded in the file system. - Users and other tools won't change the case of paths by renaming a file. - The OS always reports the same case for a path (not true in Windows for network shares). Then, to accept as input paths from other tools or directly typed by users that may have case different than what's in the catalog, there should be a utility function that takes the path of an existing file and normalizes it to the case currently recorded in the file system. Looking at LrFileUtils, there's a slow way of doing that, using directoryEntries() to normalize the case of directories in the path starting at the root; but that will require platform-dependent parsing of the path components. Absent such a utility function, a plugin should never accept paths input by the user or another tool except via LrDialogs.runOpenPanel().
... View more
‎Jul 07, 2010
01:44 PM
The methods catalog:addPhoto () and catalog:findPhotoByPath () don't ignore case on WIndows 7 32-bit. If you invoke catalog:addPhoto () twice on the same path but using different case, LR 3 gets very confused, importing the photo a second time and adding a second C hierarchy to the Folders panel: In the parent forum, there are a number of posts observing that LR has added duplicated drive or folder entries, and some of them mention that the folders have different case. Here's code illustrating the problem. Beware that this could corrupt the catalog, so invoke it in a test catalog only: local LrDialogs = import 'LrDialogs' local LrTasks = import 'LrTasks' local catalog = import 'LrApplication'.activeCatalog () LrTasks.startAsyncTask (function () catalog:withWriteAccessDo ("Test add photo", function (context) local path = [ ] local photo = catalog:addPhoto (path) LrDialogs.message ("Path: " .. photo:getRawMetadata ("path")) LrDialogs.message ("Found by original case: " .. tostring (catalog:findPhotoByPath (path))) LrDialogs.message ("Found by lower case: " .. tostring (catalog:findPhotoByPath (string.lower (path)))) local photo = catalog:addPhoto (string.lower (path)) LrDialogs.message ("Path: " .. photo:getRawMetadata ("path")) end) end)
... View more
‎Jul 07, 2010
01:34 PM
Hmm, the combo boxes in DevAdjust don't work on my config. Here's what I did: 1. File > Plug-in Extras > DevAdjust 2. Adjustments | New Presets | Combos 3. In the first combo box, type "Tone Curve - Darks", enter 5.00 as the value 4. Save As Preset, use the filename "Test1" 5. Examine the contents of Test1.lua -- it's empty. 6. Repeat, but instead of typing, use the drop-down to select "Tone Curve - Darks". 7. The contents of Test1.lua are no longer empty. I'm stuck. I wonder if this is specific to my config or to Windows 7 32-bit? Anyone else with Windows 7 32-bit who could run the simple test code above?
... View more
‎Jul 07, 2010
10:50 AM
Probably you need to bind the value to a property instead of specifying a fixed value. Hmm, here's a version binding to a property, and it still doesnt work: local LrBinding = import 'LrBinding' local LrDialogs = import 'LrDialogs' local LrFunctionContext = import 'LrFunctionContext' local LrView = import 'LrView' local f = LrView.osFactory() LrFunctionContext.callWithContext ("test", function (context) local prop = LrBinding.makePropertyTable (context) LrDialogs.presentModalDialog {title = "Test", contents = f:combo_box {bind_to_object = prop, value = LrView.bind ("value"), items = {"1", "2", "3"}}} LrDialogs.message (prop.value) end) When you select an item from the menu, that gets displayed in the message dialog, but any value you type in isn't. I'm not having any such problems. I wonder what's different. Does the code above work on your machine? Which platform? I'm on Windows 7 32-bit. Note that the example on page 101 of the SDK Guide doesn't work for me either. Does one of your published plugins use combo boxes within presentModalDialog ()? If so, I could download it and see if it works properly on my config.
... View more
‎Jul 07, 2010
12:36 AM
See also http://forums.adobe.com/thread/675142.
... View more
‎Jul 07, 2010
12:34 AM
It appears that combo boxes don't work at all, ignoring any input the user types: local LrDialogs = import 'LrDialogs' local LrView = import 'LrView' local f = LrView.osFactory() local combo = f:combo_box {value = 1, items = {1, 2, 3}} LrDialogs.presentModalDialog {title = "Test", contents = combo} LrDialogs.message (combo.value) Am I missing something obvious? I tried the example in the SDK Guide on page 101, and that doesn't seem to work either. This would explain why validation of combo boxes doesn't work: http://forums.adobe.com/thread/673170?tstart=0
... View more
‎Jul 06, 2010
04:37 PM
1 Upvote
I haven't found any documented method for adding controls to a dialog that's being displayed. But there are techniques you could use to solve your problem: 1. After you get the data and know how many controls you need, close the first dialog and open a second one with the right number of controls. 2. Create the maximum number of controls, setting .visible = false to hide them. Unhide as many as necessary after you've received the data.
... View more
‎Jul 04, 2010
02:33 PM
In SDK 3.0, a numeric edit_field control can easily get into an infinite loop of popping up an "Invalid numeric entry" message, and the user is forced to kill LR using the task manager. To reproduce: 1. Run the test plugin below. 2. Click in another (non-Lightroom) window. Depending on the arrangement of windows, the "Invalid numeric entry" message pops up below the main LR window, invisible to the user. 3. Click back in the edit_field of the plugin. At this point, the "Invalid numeric entry" message is visible, and clicking OK just brings it up again. You've got to use the task manager to kill Lightroom. The plugin code: local LrDialogs = import 'LrDialogs' local f = import 'LrView'.osFactory() LrDialogs.presentModalDialog { title = "Test", contents = f:edit_field {min = 1}} I've submitted this via the Web form but posted it here in case others encounter similar symptoms.
... View more
‎Jul 03, 2010
01:06 PM
Combo boxes don't support the min, max, precision, or validate properties, contrary to what the documentation states in "Lightroom SDK 3.0\API Reference\modules\LrView edit view properties.html". Here's a test program that shows the problems: local LrDialogs = import 'LrDialogs' local LrView = import 'LrView' local f = LrView.osFactory() local text = f:static_text {} local function validate (view, value) text.title = "validate called from " .. view.label return true, value, nil end LrDialogs.presentModalDialog { title = "Test", contents = f:column { text, f:row { f:static_text {title = "numeric:"}, f:edit_field {min = 1, max = 10, precision = 0}}, f:row { f:static_text {title = "numeric:"}, f:combo_box {min = 1, max = 10, precision = 0, items = {1, 2, 3}}}, f:row { f:static_text {title = "validate:"}, f:edit_field {validate = validate, label = "edit_field"}}, f:row { f:static_text {title = "validate:"}, f:combo_box {validate = validate, label = "combo_box", items = {1, 2, 3}}}}} I've reported this via the Web form.
... View more
‎Jun 28, 2010
02:26 PM
Either have targetPhotos just return 1 photo, Doesn't catalog:getTargetPhoto () return just one photo, the active photo?
... View more
‎Jun 28, 2010
02:16 PM
Am I missing something obvious, or is there indeed no way to set the capture time of a photo? Invoking photo:setRawMetadata ("dateTimeOriginal", LrDate.currentTime ()) results in the error: LrPhoto:setRawMetadata: unknown metadata key "dateTimeOriginal" (The documentation doesn't list "dateTimeOriginal", but I tried it anyway.) If true, this seems like a pretty big omission?
... View more
‎Jun 26, 2010
05:05 PM
Here's an undocumented way to create a dialog with just a single "Done" button. Not sure you want to rely on this though :-; local LrDialogs = import 'LrDialogs' local LrFunctionContext = import 'LrFunctionContext' local LrTasks = import 'LrTasks' local LrView = import 'LrView' local function findButton (x, label, visited) if visited == nil then visited = {} end if type (x) ~= "table" or visited then return nil end visited = true if x._WinClassName == "AgViewWinPushButton" and x.title == label then return x; end for k, v in pairs (x) do local result = findButton (v, label, visited) if result then return result end end return nil end local f = LrView.osFactory() local controls = f:column { f:static_text {title = "Look Ma! No OK or Cancel!"}, f:edit_field {value = "fields"}, f:push_button {title = "Do It"}} LrTasks.startAsyncTask (function () while true do local okButton = findButton (controls, "OK") if okButton then okButton.enabled = false okButton.visible = false return end LrTasks.sleep (0.1) end end) LrDialogs.presentModalDialog { title = "Test", contents = controls, cancelVerb = "Done"}
... View more
‎Jun 25, 2010
10:59 PM
Oh, silly me, I gave you a code fragment that didn't actually make sense -- very sorry about that. The problem with this: local topLevelCollections = LrTasks.startAsyncTask ( function (context) return catalog:getChildCollections() end) is that startAsyncTask does exactly that -- it creates an asynchronous task (i.e. a thread) that executes the function in parallel with the task that called startAsyncTask. It always returns nil, which is why topLevelCollections is always nil. You'll need to put all of the work manipulating collections (and many other methods on catalogs) into a function that's invoked by startAsyncTask. E.g. your plugin could have the structure: local function main () ...access the catalog and collections... ...show the main dialog... end LrTask.startAsyncTask (main)
... View more