9 Replies Latest reply on Aug 30, 2013 4:39 AM by dhmc05

    Need help with connected / linked pop-up menus

    dhmc05 Level 1

      I want to select a preset.

      What I did is create 2 pop-up menus. The first with the preset folders and the second with the presets from that folder.

      Each pop-up group has a pop-up and a static field with the current choise.

       

      After selecting a different value from the first pop-up menu I want to have the second popup menu filled with the new, right presets of that folder.

      The first time everything works correct.

      However when selecting a different folder I see that the "preset table"  is filled with the correct new preset of that folder. Also the static field below the second pop-up menu shows the first value of the correct new presets. But the second pop-up menu itself still contains the presets of the first folder and not the new folder.

      Yet in the Observer I see that the preset table contains the right and new presets.

       

      My code is below.

      I based it on the sample in the SDK manual.

      Question: How can I update the second pop-up menu so that the pop-up menu shows the right presets.

       

      Code below

       

      local LrApplication = import 'LrApplication'
      local LrPathUtils = import 'LrPathUtils'
      local LrFileUtils = import 'LrFileUtils'
      
      local LrBinding = import "LrBinding"
      local LrDialogs = import "LrDialogs"local LrFunctionContext = import "LrFunctionContext"
      local LrView = import "LrView"
      local bind = LrView.bind -- shortcut for bind() method
      
      --     https://github.com/kikito/inspect.lua
      local inspect = require 'inspect'
      
      local logFilename = 'PresetList'
      local myLogger = import 'LrLogger'( logFilename )
      myLogger:enable( "logfile" )
      local logPath
      
      --[[--------------------------------------------------------------------------
      Name          emptyLogFile
      Purpose          Clears the existing log file.
      
      From cookbook: http://cookbooks.adobe.com/post_Clearing_your_logfile_automatically-19677.html
      ----------------------------------------------------------------------------]]
      function emptyLogFile()
       --local myLogger = import 'LrLogger'( 'Stash' )
       myLogger:disable() 
      
       logPath = LrPathUtils.child(LrPathUtils.getStandardFilePath('documents'), logFilename .. ".log")
       if LrFileUtils.exists( logPath ) then
       local success, reason = LrFileUtils.delete( logPath )
       if not success then
       logger:error("Error deleting existing logfile!" .. reason)
       end
       end
       myLogger:enable( "logfile" )
      end
      
      --[[--------------------------------------------------------------------------
      Name          getLocationLogFile.
      Purpose          Returns the full path of the current log file.
      ----------------------------------------------------------------------------]]
      function getLocationLogFile()
       return logPath
      end
      
      local function getPresetFolders()
       -- Get all folders with presets
          local lrPresetFolders = LrApplication.developPresetFolders()
          local presetFolderCache = {}
       local record = {}
      
          for i, fo in ipairs( lrPresetFolders ) do
       record = {title = fo:getName(), value = fo }
      
       --record = {title = fo:getName(), value = fo:getName() }
       --myLogger:info( "folder name = " .. fo:getName() )
       table.insert ( presetFolderCache, record )
       end
      
       --     myLogger:info("Voor de listing")
       --     myLogger:info("preset Folders : " .. inspect (  presetFolderCache ) )
       return presetFolderCache
      end
      
      local function getPresets ( folderObject )
       local presets = folderObject:getDevelopPresets()
       local presetCache = {}
       local record = {}
      
       for j, p in ipairs( presets ) do
       record = {title = p:getName(), value = p:getName() }
      
       --record = {title = p:getName(), value = p:getName() }
       myLogger:info( "getPresets = " .. p:getName() )
       table.insert ( presetCache, record )
       end
      
       myLogger:info("presets : " .. inspect (  presetCache ) )
       return presetCache
      end
      
      local function ChoosePreset()
      
       emptyLogFile()
      
       local presetFolders = getPresetFolders()
       local currentPresetFolder = presetFolders[1]["value"]
       local presets = getPresets ( currentPresetFolder )
       myLogger:info(presets)
      
       --     myLogger:info("preset Folders : " .. inspect (  presetFolders ) )
       LrFunctionContext.callWithContext( 'Pop-up example', function( context )
      
       local f = LrView.osFactory() -- obtain view factory
       local properties = LrBinding.makePropertyTable( context ) -- make prop table
      
       -- create some keys with initial values
       properties.presetFolder = presetFolders[1]["value"] -- for radio buttons and pop-up menu
       properties.preset = presets[1]["value"]
      
       properties:addObserver( 'presetFolder', function( properties, key, newValue )
       myLogger:info("Observer - get preset list" )
       currentPresetFolder = properties.presetFolder
       presets = getPresets ( currentPresetFolder )
       myLogger:info(presets)
      
       myLogger:info("Observer - Preset table: " .. inspect ( presets ) )
      
       --     Set the new value
       properties.preset = presets[1]["value"]
       myLogger:info("Observer: " .. presets[1]["title"] )
       end )
       --     myLogger:info("presetFolder = " .. properties.presetFolder )
       --     myLogger:info("preset = " .. properties.preset )
      
       local contents = f:column { -- create view hierarchy
       fill_horizontal = 1,
       spacing = f:control_spacing(),
       bind_to_object = properties, -- default bound table is the one we made
      
       f:group_box {
       title = "Preset folders",
       fill_horizontal = 1,
       spacing = f:control_spacing(),
       f:popup_menu {
       value = bind 'presetFolder', -- current value bound to same key as static text
       items = presetFolders
       },
       f:static_text {
       fill_horizontal = 1,
       title = bind 'presetFolder', -- bound to same key as current selection
       },
       },
       f:group_box {
       title = "Presets",
       fill_horizontal = 1,
       spacing = f:control_spacing(),
       f:popup_menu {
       value = bind 'preset', -- current value bound to same key as static text
       items = presets
       --     items = getPresets ( currentPresetFolder )
       },
       f:static_text {
       fill_horizontal = 1,
       title = bind 'preset', -- bound to same key as current selection
       },
       },
       }
      
       local result = LrDialogs.presentModalDialog( -- invoke the dialog
       {
       title = "Select preset",
       contents = contents,
       }
       )
       end
       )
      end
      
      -- Now display the dialogs
      ChoosePreset()
      
      
        • 1. Re: Need help with connected / linked pop-up menus
          areohbee Level 5

          if the popup items is not fixed (and in your case, it's not), you can bind it to a property.

           

          Rob

          • 2. Re: Need help with connected / linked pop-up menus
            dhmc05 Level 1

            Hi Rob (and others),

             

            Thank you for your reply, however I do not understand what you are trying to tell me.

             

            I did bind both the pop-ups to a property:

            properties.presetFolder = presetFolders[1]["value"] -- for radio buttons and pop-up menu

            properties.preset = presets[1]["value"]

             

             

            Probably you mean something different.

             

            Could you please explain in more detail what the problem is and how I can fix it.

            • 3. Re: Need help with connected / linked pop-up menus
              areohbee Level 5

              in preset popup view:

               

              bind_to_object = properties,

              items = LrView.bind( 'myPresetItems' )

               

              then, elsewhere:

               

              properties['myPresetItems'] = computeNewItems() -- compute array of tables with title and value members and assign to bound member of property table.

              • 4. Re: Need help with connected / linked pop-up menus
                DawMatt Level 3

                That should work because you are likely to be creating a new table each time computeNewItems() executes..

                 

                Sometime the bind function doesn't recognise whether the contents of a table bound to a property changes. So if you use something like this:

                 

                local myItems = computeNewItems() -- compute array of tables with title and value members

                properties['myPresetItems'] = myItems --  assign to bound member of property table.

                 

                it will work the first time. If you then do this:

                 

                myItems = computeNewItems() -- compute array of tables with title and value members but using existing variable

                properties['myPresetItems'] = myItems --  assign to bound member of property table. Change might not be noticed by bind.


                In this instance you would need to reassign the same value again. i.e.

                 

                properties['myPresetItems'] = myItems --  assign to bound member of property table again to force bind to check for table values.


                Setting a property with the same value twice seems to trick bind into checking to see whether you have bound a table. I'm not sure whether this trick is only required on some versions of LR (e.g. LR 2) or only on one of the platforms, but even if it isn't required in LR 5 it will help improve your backwards compatibility and it shouldn't cause any harm to continue using it in LR 5.

                 

                Matt

                • 5. Re: Need help with connected / linked pop-up menus
                  areohbee Level 5

                  Here is my understanding.

                   

                  this will not work:

                   

                  local myItems = { ... }

                  properties.myItems = myItems

                  items = LrView.bind( 'myItems' )

                  myItems[1].title = "You won't see this"

                   

                  because Lr is not checking all table members for changes.

                   

                  But if you do

                  properties.myItems = { ... } -- new array

                   

                  it will always work.

                   

                  Put another way, Lr will always check if a new value has been assigned to a bound property. What it won't check is if a member of a bound table has changed, when the table itself hasn't. Reminder: tables are "by-reference", so in the case of tables, what you are actually binding to is a "pointer". So, if the pointer changes, it will be caught, if the contents pointed to changes, the change will not be caught.

                   

                  Do I not understand correctly?

                   

                  Even if not, I have succeeded in making lots of dynamic preset popups based on such "understanding" (i.e. assigning a wholly new table *always* works).

                   

                  Rob

                  • 6. Re: Need help with connected / linked pop-up menus
                    DawMatt Level 3

                    Rob Cole wrote:

                     

                    Here is my understanding.

                     

                    this will not work:

                     

                    local myItems = { ... }

                    properties.myItems = myItems

                    items = LrView.bind( 'myItems' )

                    myItems[1].title = "You won't see this"

                     

                    because Lr is not checking all table members for changes.

                     

                     

                    Correct. bind does not check for table member changes so that will not work.

                     

                    But bind does seem to have implemented a work around for this issue. It is a bit of a kludge but it does seem to work. If you change the above code to look like this it should work:

                     

                    local myItems = { ... }

                    properties.myItems = myItems

                    items = LrView.bind( 'myItems' )

                    myItems[1].title = "You won't see this"


                    -- Set this twice, which seems to trick bind into re-evaluating the table. Setting it once will often work.

                    properties.myItems = myItems

                    properties.myItems = myItems

                     

                     

                    So even though you are setting the same value to the property (the table's "by-reference" identifier aka pointer) it is still enough to force bind to re-evaluate the bound value and thus the table contents.

                     

                     

                    Rob Cole wrote:

                     

                    Do I not understand correctly?

                     

                     

                    Yes, you understand it correctly. There is just a long-standing quirk here (I believe it is an intentional feature) that gives you a way to achieve the same result without having to create new tables each time.

                     

                    Matt

                    • 7. Re: Need help with connected / linked pop-up menus
                      areohbee Level 5

                      Matt Dawson wrote:

                       

                      There is just a long-standing quirk here (I believe it is an intentional feature) that gives you a way to achieve the same result without having to create new tables each time.

                      Thanks for the tip - I'd heard it before but have been relying on the "larger hammer" approach for so long that I'd forgotten - I appreciate the reminder .

                       

                      Rob

                      • 8. Re: Need help with connected / linked pop-up menus
                        jarnoh Level 1

                        I'm assuming that you indeed have static popup content, the discussion above seems to be for dynamic content, which gets hairy very quickly.

                         

                        First, build a "Presets" table with title,value for every folder.  Value should contain another table with title,value for each preset.  Then define your popups like this:

                         

                        f:popup_menu {

                          items = LrView.bind("Presets"),

                          value = LrView.bind("SelectedFolder"),

                        },

                        f:popup_menu {

                          items = LrView.bind("SelectedFolder"),

                          value = LrView.bind("SelectedPreset"),

                        },

                         

                        This is what I use in http://capturemonkey.com/thefader

                        • 9. Re: Need help with connected / linked pop-up menus
                          dhmc05 Level 1

                          Just back from holidays I want to thank Rob for his suggestion above! It worked!

                           

                          A little checking had to be added, because when choosing the the preset and selecting it, the preset folder was somehow changed to and that fired the computeNewItems() again setting the preset back to the first item in that table.

                          With a check if the previous preset folder is the current preset folder I solved this.

                           

                          The interesting discussion below and the suggestion of  jarnoh will I test.

                           

                          Thank you all!