Skip navigation
Currently Being Moderated

Writing a plugin to create Smart Collections

Jun 4, 2012 7:30 AM

Tags: #plugin #collection #folder #smart

Hi folks

 


Is it possible to use the SDK to create a plugin that creates a Collection Set that contains a number of Smart collections.

 

I'd also like to be able to select a Folder and have the plugin use the folder name as one of the parameters in the Smart collections and the title of the Collection Set

 

Cheers in advance

 
Replies
  • Currently Being Moderated
    Jun 4, 2012 10:06 AM   in reply to John Spacey

    It should be straightforward.  In the LrCatalog class, see the methods catalog:createSmartCollection() and catalog:createCollectionSet().

     

    To find out which folder or folders are currently selected, see the method catalog:getActiveSources().

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 4, 2012 1:18 PM   in reply to John Spacey

    You may be interested in:

     

    Folder Collections:

     

    http://www.robcole.com/Rob/ProductsAndServices/FolderCollectionsLrPlug in

     

    Download is source code format.

     

    It uses dumb collections because there is no way to have smart collection content match folder content - I got close using smart collections, but ultimately had to rely on dumb collections and a background task.

     

    Still, the original version used smart collections, and the code for it is still mostly in there...

     

    Cheers,

    Rob

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 4, 2012 6:54 PM   in reply to John Spacey

    The "closures" feature of Lua, among other things, makes it a delightful language to program with, in my opinion. - very simple, but very powerful - a shining example of good programming language design, in my opinion.

     

    Enjoy!

    Rob

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 6, 2012 12:11 PM   in reply to John Spacey

    Hi John,

     

    Try something like this:

     

    local sources = catalog:getActiveSources()

     

    for _notUsed, source in ipairs(sources) do

     

        if source:type() == 'LrCollection' then

           -- do something

        elseif source:type() == 'LrCollectionSet' then

           -- do something

        elseif source:type() == 'LrPublishedCollection' then

           -- do something

        elseif source:type() == 'LrPublishedCollectionSet' then

           -- do something

        elseif source:type() == 'LrFolder' then

           -- do something

        end

     

    end

     

    Does this help?

     

    -Don

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 6, 2012 1:08 PM   in reply to John Spacey

    Hi John,

     

    The array is just a Lua array (e.g. sources[1], sources[2], ...) that, according to the Lightroom SDK doc contains one of the listed objects.  Also referring to the SDK doc for the objects, they all have a "getName()" method.  Is that what you're looking for?

     

    -Don

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 6, 2012 2:27 PM   in reply to John Spacey

    What object were you looking at?  For example, the LrPublishedCollection's getName() method documentation includes:

     

    This function must be called from within an asynchronous task started using LrTasks.

     

    Did you do that?

     

    -Don

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 6, 2012 3:33 PM   in reply to John Spacey

    Lots of stuff only works in an asynchronous task.

     

    The first thing I do in response to a menu or button is create an asynchronous task to process it, with an error handler attached to the processing context (LrFunctionContext).

     

    That way, I never have to worry about something needing to be done from an asynchronous task, because *everything* is being done from an asynchronous task.

     

    If you want to make sure another instance of the task doesn't get started before the other is finished, you can use recursion guarding.

     

    I don't use the LrRecursionGuard, but it's one possibility.

     

    Rob

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 6, 2012 4:18 PM   in reply to John Spacey

    LrFunctionContext.postAsyncTaskWithContext is my personal favorite, since you can create the task and the processing context in one swipe that way.

     

    Put that at the outermost level.

     

    Then assign a cleanup, or error handler to the context, so errors don't just silently abort, or at least that's what used to happen to button handlers, unless Adobe changed that in Lr4 (I haven't checked).

     

    I simply check/set a variable upon entry, then clear it in the cleanup handler to implement recursion guarding, which I have rolled into a set of classes which I use in my plugin framework.

     

    You can see said framework (elare plugin framework) in any of my plugins (e.g. http://www.robcole.com/Rob/ProductsAndServices), all of which include source code, and you can use the framework by going here:

     

    https://www.assembla.com/spaces/lrdevplugin/

     

    R

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 6, 2012 4:21 PM   in reply to John Spacey

    > Where can I learn about how to create an asyncronous task?

     

    From the sample plug-ins included with the SDK, and the SDK doc.  But, basically,

     

    LrTasks.startAsyncTask(function()

     

         MyHWLibraryItem.showCustomDialog()

     

    end)

     

    would run all of your code in a task.

     

    -Don

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 6, 2012 4:23 PM   in reply to Don McKee

    Oh, and what Rob said. 

     

    -Don

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 6, 2012 4:48 PM   in reply to Don McKee

    Yeah, - what Don said too.

     

    Just remember that recursion guarding can become an issue when everything is done asynchronously, since impatient users or those with twitchy fingers may get 3 or 4 of the same things going at once if not careful. Obviously, if there isn't enough time to click twice before the first/next (modal) dialog box comes up..., it's a non-problem. But if processing takes more than a fraction of a second...  (Next up: LrProgressScope...).

     

    Also, tasks don't abort when reloading the plugin, until they're finished, *if* they finish. It's possible to have multiple instances of the same task running with different environments...

     

    I use LrShutdownPlugin to set a global shutdown flag and check it in async loops, so plugin reloads happen gracefully. (I wish LrTasks.sleep would automatically return a code upon a plugin reload / shutdown (like java...), but since it doesn't, I implemented my own sleeper in the framework).

     

    PS - There is some new shutdown handling which I've yet to investigate. Since it would be ignored in Lr3, it's hardly worth using unless plugin is Lr4 only.

     

    My apology in advance if this is too much too soon - maybe return to this thread and re-read if you start having problems...

     

    Also, as Obewankanobee said: read the docs, Luke...

     

    Rob

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 6, 2012 4:56 PM   in reply to John Spacey

    John Spacey wrote:

     

    HURRAH! You guys Rock! Thanks ever so much.

     

    ******************************************

    Rob, errors seem pretty verbose in LR4.1 when something has gone wrong an error dialog appears everytime on the cide I've been mangling

     

    I've bookmarked your site and the assembla site

     

    Thanks again peeps

     

    Congrats, and you're welcome.

     

    In some cases, Lightroom provides the error handling, in other cases - it doesn't.

     

    I no longer remember which cases it does, and which cases it doesn't, since I always provide my own.

     

    One case I do remember is button handlers. You *must* handle errors in button handlers or they will just silently malfunction, or at least that's true for Lr3-.

     

    Oh yeah, change handlers (ui property observers) and ui validator functions are 2 more cases that need error handlers (unless you don't mind silent failure upon error).

     

    R

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 6, 2012 5:10 PM   in reply to John Spacey

    John Spacey wrote:

     

    Cheers Rob

     

    I am reading the docs as much as possible. I'm not finding them that intuitive to be honest, but I am trying to source the answers myself before coming here and shouting 'Help!      I hadn't even heard of LUA a few days ago so it is a bit of a jump in the deep end

     

    Hi John,

     

    Yeah, the docs are perhaps best digested along with a big dose of experience.

     

    Enjoy (I think Lua is great).

     

    Rob

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 7, 2012 5:13 PM   in reply to John Spacey

    I have a number of plugins that deal with collections which may have some good example source code:

     

    FolderCollections

    CollectionAgent

    Stacker

    SQLiteroom

    LrFourB

    PublishServiceAssistant

     

    http://www.robcole.com/Rob/ProductsAndServices

     

    R

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 8, 2012 2:07 AM   in reply to John Spacey

    Looks good John.

     

    A couple lua-y things to note:

     

    You can assign nil to an array, but:

     

    * ipairs stops on the first nil.

    * #tbl counts items (at positive integer indexes) up unitl the last nil.

     

    I mean, assigning non-nil values (e.g. false) is probably the preferred approach in this case, but just so ya know:

     

    #{ x=1 } == 0 -- is true

    #{ 1, nil, 3 } == 3 -- true too

    #{ 1, nil } == 1 -- also true

    #{ x=1, 2 } == 1 -- true

    { x=1, 2 }[1] == 2 -- true

    local x = {}

    x[0] = 1

    #x == 0 -- true

     

    for i,v in ipairs{ 1, nil, 3 } do

         local nothing = 1 -- executes only once

    end

     

    R

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 9, 2012 3:12 PM   in reply to John Spacey

    Standing by... (thanks).

    R

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 11, 2012 1:49 AM   in reply to John Spacey

    Looks good, but I wrapped it in a plugin and tried it, and I can't tell what happened (couldn't find any new collections, nor was there a UI message presented (after I approved the initial prompt I mean), nor any logged messages). I'm not sure what was supposed to happen.

     

    ?

    R

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 11, 2012 4:08 PM   in reply to John Spacey

    Thanks,

     

    I tried it using a folder source and it worked. Previous test was using a collection as source.

     

    Unfortunately, for me, the smart collection folder criteria casts too wide a net (sometimes includes photos from other folders). Same will be true for other users. This is why I changed FolderCollections to use dumb collections instead - there is no way to reliably define smart collection criteria to limit photos to those of a single corresponding folder, or at least no way that John Ellis or I could figure.

     

    Otherwise, nice job!

     

    Cheers,

    Rob

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 11, 2012 4:50 PM   in reply to John Spacey

    Right-o: unique folder names will do it. And, I may resort to unique folder names at some point, just for this reason (assuming Adobe does not robusten the SDK enough come Lr5). Just thought you should know, in case you plan on making this plugin available to others...

     

    Cheers,

    Rob

     
    |
    Mark as:
  • Currently Being Moderated
    Jun 12, 2012 1:49 PM   in reply to John Spacey
     
    |
    Mark as:

More Like This

  • Retrieving data ...

Bookmarked By (1)

Answers + Points = Status

  • 10 points awarded for Correct Answers
  • 5 points awarded for Helpful Answers
  • 10,000+ points
  • 1,001-10,000 points
  • 501-1,000 points
  • 5-500 points