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

Writing a plugin to create Smart Collections

Guest
Jun 04, 2012 Jun 04, 2012

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

TOPICS
SDK
12.4K
Translate
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 04, 2012 Jun 04, 2012

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().

Translate
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 04, 2012 Jun 04, 2012

You may be interested in:

Folder Collections:

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

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

Translate
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
Guest
Jun 04, 2012 Jun 04, 2012

thanks guys

Yes it looks like it shouldn't be too difficult to get something up and running that meets my needs. I'm pretty experienced in PHP programming, but do need to learn LUA.

Translate
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 04, 2012 Jun 04, 2012

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

Translate
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
Guest
Jun 06, 2012 Jun 06, 2012

Ok, I'm admitting defeat at an early stage ...

I think I've managed to return the array of selected sources from catalog:getActiveSources()

However I can't find any documentation on the actual structure of this array or how to access it

The docs for catalog:getActiveSources() say

Returned:

(array of LrCollection, LrCollectionSet, LrPublishedCollection, LrPublishedCollectionSet or LrFolder)

Translate
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 06, 2012 Jun 06, 2012

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

Translate
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
Guest
Jun 06, 2012 Jun 06, 2012

Thanks Don, that helps enormously.   I'm still having trouble extracting the source names though.  Guessing from source:type() I tried getting the names by trying source:name() or source:title() but it didn't work.   Sorry about these possibly dumb questions, but I can't find any documentation on the structure of the array.

Translate
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 06, 2012 Jun 06, 2012

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

Translate
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
Guest
Jun 06, 2012 Jun 06, 2012

Ahh getName()

I  tried this by inserting  sourceName = source:getName() in your code abvoe @  -- do something and I got the followimg error

"An internal error has occoured: We can only wait from within a task"

Translate
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 06, 2012 Jun 06, 2012

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

Translate
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
Guest
Jun 06, 2012 Jun 06, 2012

I was looking at the object LrFolder in the returned array

I tried to get the name of the selected folder like this:

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

       sourceName = source:getName()

    end

/Adobe Lightroom 4 SDK/API Reference/modules/LrFolder.html#folder:getName

which says:

------------------------------------------------------------------------------------------

folder:getName() Retrieves the name of this folder.

First supported in version 3.0 of the Lightroom SDK.

Return value

(string) The name.

------------------------------------------------------------------------------------------

and mentions nothing about 'an asynchronous task started using LrTasks.' whatever that is

Stumped

Translate
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 06, 2012 Jun 06, 2012

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

Translate
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
Guest
Jun 06, 2012 Jun 06, 2012

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

This is where I am at so far. I'm able to show a dialog box that lists the types of active sources

https://fbcdn-sphotos-a.akamaihd.net/hphotos-ak-ash3/543743_424084167624887_174437144_n.jpg

I modified one of the SDK programmers guide tutorials thus:

ShowSources.lua

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

local LrFunctionContext = import 'LrFunctionContext'

local LrBinding = import 'LrBinding'

local LrDialogs = import 'LrDialogs'

local LrView = import 'LrView'

local LrColor = import 'LrColor'

local LrTasks = import 'LrTasks'

local LrApplication = import 'LrApplication'

MyHWLibraryItem = {}

-- function to interrogate active sources array

function read_activeSources_array( sources )

    source_type = "Active Sources:\n"

        for i, source in ipairs( sources ) do

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

                source_type = source_type .. i .. " is a Collection\n"

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

                source_type = source_type .. i .. " is a folder\n"                   

            end           

            --local sourceName = source:getName()    -- this produces ***ERROR***

        end

        return source_type

end

-- ***ERROR*** "An internal error has occoured: We can only wait from within a task"

function MyHWLibraryItem.showCustomDialog()

-- body of show-dialog function

LrFunctionContext.callWithContext( "ShowSources",

    function( context )

        catalog = LrApplication.activeCatalog()

        local path = catalog:getPath()

        local sources = catalog:getActiveSources()

        --string = LrTasks.startAsyncTask( read_activeSources_array( sources ) )

        display_string = read_activeSources_array( sources )

    -- create view hierarchy for dialog box

    local f = LrView.osFactory() -- get the view factory object

    local c = f:column { -- the root node

        f:row { -- Display contents of active sources Array

            f:static_text {

                text_color = LrColor( 0, 0, 1 ),

                alignment = "Left",

                -- add title with binding later

                title = display_string

            },

        },

    }

    local result = LrDialogs.presentModalDialog(

        {

        title = "Show Sources",

        contents = c, -- the view hierarchy we defined

        }

    )

    end )

end

MyHWLibraryItem.showCustomDialog()

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

Translate
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 06, 2012 Jun 06, 2012

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

Translate
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 06, 2012 Jun 06, 2012

> 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

Translate
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 06, 2012 Jun 06, 2012

Oh, and what Rob said. 

-Don

Translate
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
Guest
Jun 06, 2012 Jun 06, 2012

HURRAH! You guys Rock! Thanks ever so much. https://fbcdn-sphotos-a.akamaihd.net/hphotos-ak-ash4/481157_424096430956994_427450149_n.jpg

I did

LrTasks.startAsyncTask(function()

    MyHWLibraryItem.showCustomDialog()

end)

And then modified my function to

-- function to interrogate active sources array

function read_activeSources_array( sources )

    source_type = "Active Sources:\n"

        for i, source in ipairs( sources ) do

            local sourceName = source:getName()

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

                source_type = source_type .. i .. " is a Collection called " .. sourceName .. "\n"

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

                source_type = source_type .. i .. " is a folder called " .. sourceName .. "\n"   

                --source_type = source_type .. sourceName .."\n"           

            end           

           

        end

           

        return source_type

end

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

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

Translate
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 06, 2012 Jun 06, 2012

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

Translate
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 06, 2012 Jun 06, 2012

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

Translate
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
Guest
Jun 06, 2012 Jun 06, 2012

Rob Cole wrote:

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

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

Translate
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 06, 2012 Jun 06, 2012

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

Translate
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
Guest
Jun 07, 2012 Jun 07, 2012

Right tonight's takst is to come up with a routine to create collection sets and collections based upon those fodler names. I shall go and digest as much documentionation as possible before shouting for help

Translate
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
Guest
Jun 07, 2012 Jun 07, 2012

Excellent! On a roll tonight. Managed to create a collection set containing a number of Smart collections!   Only issue I have now is finding an existing collection set of a known name and using that as a parent to the newly created collection set.

Translate
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 07, 2012 Jun 07, 2012

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

Translate
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