Search code examples
applescriptmetadatapreviewautomator

Create an image annotation tool with AppleScript and Automator


On Mac, is it possible to use AppleScript/Automator to show a preview of an image in the background, while the Automator app is running?

I have hundreds of images to which I want to add meta data so that they can be displayed on a web site. My plan is to create a *.meta file with the same name as the original image, so that a PHP script can read in the meta data at the same time that it generates the URL for the image.

I have created an AppleScript file (see below), which I have embedded in an Automator app. When you drop a selection of files on the app, it first shows you the image, and then shows 3 dialogs where you can enter the required data.

The problem is that the AppleScript is blocked while the qlmanage preview window is open. Before you can enter the required data, you need to close the window, so you can no longer see the image.

Is there a way to send the qlmanage process into the background so that the dialog windows can appear while the image is open?

Or perhaps there is already a free tool for Mac OS that already allows you to do exactly what I hope to do.

tell application "Finder"
    set the_selection to the selection
end tell

set file_types to {"jpg", "png", "gif", "mp4"}
set copyright to "© 2015 CompanyName"
set signature to "Signature"

repeat with ii from 1 to the count of the_selection

    set {posix_path, file_path, file_name, file_ext} to splitPath(item ii of the_selection)

    if file_types contains file_ext then
        set meta_path to file_path & file_name & ".meta"
        tell application "Finder"
            if not (exists file (meta_path)) then
                do shell script "qlmanage -t -s 640 " & posix_path

                set alt_text to the text returned of (display dialog "alt" default answer file_name)
                set copyright to the text returned of (display dialog "©" default answer copyright)
                set signature to the text returned of (display dialog "signature" default answer signature)

                set meta_text to "alt " & alt_text & return & "copyright " & copyright & return & "signature " & signature

                set meta_file to open for access meta_path with write permission
                write meta_text to meta_file
                close access meta_file

            end if
        end tell
    end if
end repeat

on splitPath(selected_item)
    tell application "System Events"
        set posix_path to POSIX path of (selected_item as alias)
        set posix_path to quoted form of posix_path
    end tell

    set file_path to selected_item as string

    set file_ext to ""
    set file_name to ""

    set text item delimiters to "."
    if (count text items of file_path) > 1 then
        set file_ext to last text item of file_path
        set file_path to (text items 1 through -2 of file_path) as string
    end if

    set text item delimiters to ":"
    if (count text items of file_path) > 1 then
        set file_name to last text item of file_path
        set file_path to ((text items 1 through -2 of file_path) as string) & ":"
    end if

    return {posix_path, file_path, file_name, file_ext}
end splitPath

Solution

  • I wrote an Application that does most of what you do using ApplescriptOBJc in Script Editor.app ( mainly to see how easy it was ( It was ))

    • Gets a selection of images.
    • Populates text fields with info.
    • shows each image.
    • writes to a meta file.

    ApplescriptOBJc is the syntax bridging language between Applescript and Objective - c.

    This language allows you to access the power of Objective -c and use it alongside Applescript in the same code.

    The only thing I have not added is a check for video files.

    But as this is a example/get you started code you should be able to do that easily enough.

    I also would normally use ApplescriptOBJc to write the files out but used your code for familiarity

    The code should be easy enough to follow, change , add or move UI objects and also change or update its logic.

    Past the code into a new Script Editor document. Then Save it as a stay open application.

    The Code for the App:


        -- Copyright 2015 {Mark Hunte}. All rights reserved.
    
        use scripting additions
        use framework "Foundation"
        use framework "cocoa"
        use framework "AppKit"
    
        --- set up window
        property buttonWindow : class "NSWindow"
        property theImage : class "NSImage"
        property theImageView : class "NSImageView"
    
        property altLabelField : class "NSTextField"
        property altField : class "NSTextField"
        property copyrightField : class "NSTextField"
        property sigField : class "NSTextField"
    
        property countLabelField : class "NSTextField"
    
        property the_selection : {}
        property imageList : {} --  holds NSImage instances
        property imageListSplit : {} -- to use for file when needing an alias 
        property thisImage : 0
        property imageCount : 0
    
        property copyright : "© 2015 CompanyName"
        property signature : "Signature"
    
    
        set height to 700
        set width to 1000
        set winRect to current application's NSMakeRect(0, 0, width, height)
        set buttonWindow to current application's NSWindow's alloc()'s initWithContentRect:winRect styleMask:7 backing:2 defer:false
        buttonWindow's setFrameAutosaveName:"buttonWindow"
    
        --set up buttons
    
        set writeButtonFrame to current application's NSMakeRect(105, (height - 230), 100, 25) -- button rect origin ,x,y ,size width,hieght
        set writeBtn to current application's NSButton's alloc's initWithFrame:writeButtonFrame -- init button
    
        writeBtn's setTitle:"write file"
        set writeBtn's bezelStyle to 12 --NSRoundedBezelStyle
        writeBtn's setButtonType:0 --NSMomentaryLightButton
        writeBtn's setTarget:me
        writeBtn's setAction:"writeFile:"
    
    
    
        set nextButtonFrame to current application's NSMakeRect(105, (height - 675), 50, 25) -- button rect origin ,x,y ,size width,hieght
        set nextBtn to current application's NSButton's alloc's initWithFrame:nextButtonFrame -- init button
    
        nextBtn's setTitle:"Next"
        set nextBtn's bezelStyle to 12 --NSRoundedBezelStyle
        nextBtn's setButtonType:0 --NSMomentaryLightButton
        nextBtn's setTarget:me
        nextBtn's setAction:"nextImage:"
    
    
        --
    
    
        set prevButtonFrame to current application's NSMakeRect(25, (height - 675), 50, 25)
        set prevBtn to current application's NSButton's alloc's initWithFrame:prevButtonFrame
    
        prevBtn's setTitle:"Prev"
        set prevBtn's bezelStyle to 12 --NSRoundedBezelStyle
        prevBtn's setButtonType:0 --NSMomentaryLightButton
        prevBtn's setTarget:me
        prevBtn's setAction:"previousImage:"
    
        ---
    
    
        set selectionButtonFrame to current application's NSMakeRect((width - 715), (height - 690), 150, 25)
        set selectionBtn to current application's NSButton's alloc's initWithFrame:selectionButtonFrame
    
        selectionBtn's setTitle:"Select new Images"
        set selectionBtn's bezelStyle to 12 --NSRoundedBezelStyle
        selectionBtn's setButtonType:0 --NSMomentaryLightButton
        selectionBtn's setTarget:me
        selectionBtn's setAction:"finderSelection:"
        --
    
        set terminatButtonFrame to current application's NSMakeRect((width - 90), (height - 690), 75, 25)
        set terminateBtn to current application's NSButton's alloc's initWithFrame:terminatButtonFrame
    
        terminateBtn's setTitle:"Quit"
        set terminateBtn's bezelStyle to 12 --NSRoundedBezelStyle
        terminateBtn's setButtonType:0 --NSMomentaryLightButton
        terminateBtn's setTarget:me
        terminateBtn's setAction:"terminateMe"
        --
    
        -- add buttons to the window
    
        buttonWindow's contentView's addSubview:nextBtn
        buttonWindow's contentView's addSubview:prevBtn
        buttonWindow's contentView's addSubview:selectionBtn
        buttonWindow's contentView's addSubview:terminateBtn
        buttonWindow's contentView's addSubview:writeBtn
    
        --
    
        -- set up image view
    
        set imageViewFrame to current application's NSMakeRect(300, (height - 660), 640, 640) --origin ,x,y ,size width,hieght
        set theImageView to current application's NSImageView's alloc()'s initWithFrame:imageViewFrame
        theImageView's setImageFrameStyle:1
    
        -- add image view to the window
        buttonWindow's contentView's addSubview:theImageView
    
    
        -- activate the window
        buttonWindow's makeKeyAndOrderFront:buttonWindow
    
    
    
        --- set alt label
    
        set altLabelFrame to current application's NSMakeRect(25, (height - 60), 100, 25) --origin ,x,y ,size width,hieght
        set altLabelField to current application's NSTextField's alloc()'s initWithFrame:altLabelFrame
    
    
        altLabelField's setStringValue:"alt"
        altLabelField's setBezeled:false
        altLabelField's setDrawsBackground:false
        altLabelField's setEditable:false
        altLabelField's setSelectable:false
    
        -- set up alt textField 
    
        set altFieldFrame to current application's NSMakeRect(25, (height - 80), 240, 25) --origin ,x,y ,size width,hiegh
        set altField to current application's NSTextField's alloc()'s initWithFrame:altFieldFrame
    
        ---
    
    
        --- set copyright label
    
        set copyrightLabelFrame to current application's NSMakeRect(25, (height - 110), 100, 25) --origin ,x,y ,size width,hieght
        set copyrightLabelField to current application's NSTextField's alloc()'s initWithFrame:copyrightLabelFrame
    
        copyrightLabelField's setStringValue:"©opyright"
        copyrightLabelField's setBezeled:false
        copyrightLabelField's setDrawsBackground:false
        copyrightLabelField's setEditable:false
        copyrightLabelField's setSelectable:false
    
    
        -- set up copyright textFields
    
        set copyrightFieldFrame to current application's NSMakeRect(25, (height - 130), 240, 25) --origin ,x,y ,size width,hieght
        set copyrightField to current application's NSTextField's alloc()'s initWithFrame:copyrightFieldFrame
    
    
        --- set sig label
    
        set sigLabelFrame to current application's NSMakeRect(25, (height - 160), 100, 25) --origin ,x,y ,size width,hieght
        set sigLabelField to current application's NSTextField's alloc()'s initWithFrame:sigLabelFrame
    
        sigLabelField's setStringValue:"signature"
        sigLabelField's setBezeled:false
        sigLabelField's setDrawsBackground:false
        sigLabelField's setEditable:false
        sigLabelField's setSelectable:false
        sigLabelField's setDelegate:me
    
    
        -- set up sig textFields
    
        set sigFieldFrame to current application's NSMakeRect(25, (height - 180), 240, 25) --origin ,x,y ,size width,hieght
        set sigField to current application's NSTextField's alloc()'s initWithFrame:sigFieldFrame
    
    
    
        --- set image count label
    
        set countLabelFrame to current application's NSMakeRect(500, (height - 25), 100, 25) --origin ,x,y ,size width,hieght
        set countLabelField to current application's NSTextField's alloc()'s initWithFrame:countLabelFrame
        countLabelField's setStringValue:"0"
        countLabelField's setBezeled:false
        countLabelField's setDrawsBackground:false
        countLabelField's setEditable:false
        countLabelField's setSelectable:false
    
    
        --
    
    
        buttonWindow's contentView's addSubview:altLabelField
        buttonWindow's contentView's addSubview:altField
    
        buttonWindow's contentView's addSubview:copyrightLabelField
        buttonWindow's contentView's addSubview:copyrightField
    
        buttonWindow's contentView's addSubview:sigLabelField
        buttonWindow's contentView's addSubview:sigField
    
        buttonWindow's contentView's addSubview:countLabelField
        ---
    
       my finderSelection:(missing value)
    
    
        ---
        on nextImage:sender
            buttonWindow's makeFirstResponder:(missing value)
            if thisImage is not greater than imageCount and thisImage is not equal to imageCount then
    
                set thisImage to thisImage + 1
    
                theImageView's setImage:(item thisImage of imageList)
    
            end if
            setUpTextFieldsStrings()
        end nextImage:
    
        on previousImage:sender
            buttonWindow's makeFirstResponder:(missing value)
            if thisImage is less than 1 or thisImage is not equal to 1 then
                set thisImage to thisImage - 1
    
                theImageView's setImage:(item thisImage of imageList)
            end if
            setUpTextFieldsStrings()
        end previousImage:
    
        on setUpTextFieldsStrings()
    
            tell application "Finder" to set file_name to displayed name of ((item thisImage of imageListSplit) as alias)
            set altField's stringValue to file_name
            set copyrightField's stringValue to copyright
            set sigField's stringValue to signature
    
    
            set countLabelField's stringValue to (thisImage & " of " & (count of imageListSplit) as string)
        end setUpTextFieldsStrings
    
        on writeFile:sender
            buttonWindow's makeFirstResponder:(missing value)
            set {posix_path, file_path, file_name, file_ext} to splitPath(item thisImage of imageListSplit)
            set meta_path to file_path & file_name & ".meta"
            tell application "Finder"
                if not (exists file (meta_path)) then
    
                    set alt_text to altField's stringValue
                    set copyrightText to copyrightField's stringValue
                    set signatureText to sigField's stringValue
    
                    set meta_text to "alt " & alt_text & return & "copyright " & copyrightText & return & "signature " & signatureText
    
                    set meta_file to open for access meta_path with write permission
                    write meta_text to meta_file
                    close access meta_file
    
                end if
            end tell
        end writeFile:
    
        on finderSelection:sender
            buttonWindow's makeFirstResponder:(missing value)
            set the_selection to {}
            set imageList to {}
            set imageListSplit to {}
    
            set imageCount to 0
    
            set thisImage to 0
    
            using terms from application "Finder"
                set the_selection to (choose file with prompt "Please select   images:" with multiple selections allowed without invisibles)
            end using terms from
    
            repeat with i from 1 to number of items in the_selection
    
                set this_item to (POSIX path of (item i of the_selection as alias))
    
                set workSpace to current application's NSWorkspace's sharedWorkspace
                set type to (workSpace's typeOfFile:this_item |error|:(missing value))
    
                if (workSpace's type:type conformsToType:"public.image") then
    
    
                    set end of imageList to (current application's NSImage's alloc()'s initWithContentsOfFile:this_item)
                    set end of imageListSplit to item i of the_selection
                end if
    
            end repeat
    
    
    
            if imageList is not {} then
                set imageCount to count of imageList
                theImageView's setImage:(item 1 of imageList)
    
                set thisImage to 1
                my setUpTextFieldsStrings()
            end if
    
        end finderSelection:
    
    
        on terminateMe()
    
            tell me to quit
        end terminateMe
    
    
        on splitPath(selected_item)
    
            tell application "System Events"
                set posix_path to POSIX path of (selected_item as alias)
                set posix_path to quoted form of posix_path
            end tell
    
            set file_path to selected_item as string
    
            set file_ext to ""
            set file_name to ""
    
            set text item delimiters to "."
            if (count text items of file_path) > 1 then
                set file_ext to last text item of file_path
                set file_path to (text items 1 through -2 of file_path) as string
            end if
    
            set text item delimiters to ":"
            if (count text items of file_path) > 1 then
                set file_name to last text item of file_path
                set file_path to ((text items 1 through -2 of file_path) as string) & ":"
            end if
    
            return {posix_path, file_path, file_name, file_ext}
        end splitPath
    

    Running the App:

    Once saved you run it as a normal application.

    You can also run it from within Script Editor but must Run it using either alt + the run button, alt + R or use the menu Script->Run Application.


    enter image description here

    enter image description here