Help with Image in Myriad Tables?

I was wondering if I could add an image to Myriad Tables.

I found this thread which contains the following:

set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:theFile
set theView to current application's NSImageView's alloc()'s initWithFrame:{{0, 0}, {64, 64}}
theView's setImage:theImage
theView's setImageScaling:(current application's NSImageScaleProportionallyUpOrDown)

I attempted to modify Shane’s “Accessory view sample” script included with Myriad Tables to show an image before attempting to integrate it with my script, but I can’t get that working.

use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"
use script "Myriad Tables Lib" version "1.0.9"
use scripting additions

property theCheckbox : missing value
property theAccessoryView : missing value
property theImageView : missing value

-- This sample uses the 'accessory view' parameter. This is a more complex process requiring some knowledge of AppleScriptObjC.

-- Manipulation of views should be done on the main thread
if current application's NSThread's isMainThread() as boolean then
	my buildAccViewWithCheckbox()
	my buildAccViewWithImage()
else
	my performSelectorOnMainThread:"buildAccViewWithCheckbox" withObject:(missing value) waitUntilDone:true
	my performSelectorOnMainThread:"buildAccViewWithImage" withObject:(missing value) waitUntilDone:true
end if

-- make a table and add the accessory view
set theTable to make new table with data {"One", "Two", "Three", "Four", "Five"}
modify table theTable accessory view theAccessoryView
modify table theTable accessory view theImageView
set theAccessoryView to missing value -- to avoid error messages when saving
set theImageView to missing value
display table theTable
-- get the state of the checkbox
set theState to theCheckbox's state() as boolean
set theCheckbox to missing value -- to avoid error messages when saving
display dialog theState as text

on buildAccViewWithCheckbox()
	-- build a checkbox
	set my theCheckbox to current application's NSButton's alloc()'s initWithFrame:{{10, 10}, {150, 18}}
	tell theCheckbox
		its setButtonType:(current application's NSSwitchButton)
		its setTitle:"This is a checkbox"
		its setTarget:me
		its setAction:"checkboxClicked:" -- a handler in this script
	end tell
	-- make a view and add the checkbox to it; maximum size is 600pts x 200pts
	set my theAccessoryView to current application's NSView's alloc()'s initWithFrame:{{0, 0}, {160, 28}}
	theAccessoryView's addSubview:theCheckbox
end buildAccViewWithCheckbox

on buildAccViewWithImage()
	set theFile to "/Users/work/Downloads/26ba7a0e-65bb-4a56-bd36-9a772ac5aa97.png"
	set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:theFile
	set my theImageView to current application's NSImageView's alloc()'s initWithFrame:{{0, 0}, {64, 64}}
	theImageView's setImage:theImage
	theImageView's setImageScaling:(current application's NSImageScaleProportionallyUpOrDown)
end buildAccViewWithImage

-- this is called when the checkbox is clicked
on checkboxClicked:sender
	-- make an alert and show it as a sheet over the table window
	set theWindow to sender's |window|()
	set theAlert to current application's NSAlert's alloc()'s init()
	theAlert's setMessageText:"You clicked the checkbox"
	theAlert's setInformativeText:"Now close this sheet"
	theAlert's beginSheetModalForWindow:theWindow completionHandler:(missing value)
end checkboxClicked:

Sorry, probably a bone-headed mistake. I keep meaning to learn ASObjC, but never find the time, so I have no idea what I’m doing.

Also, ideally, I’d like to have it switch what image is displayed there whenever the user selects a different row. (multiple selections are not allowed). I’ve been trying to figure out what the equivalent to
its setAction:"checkboxClicked:"
that could make the dialog run code to update the image whenever the selection is changed.

If that’s not easy/practical, I could just make a button near the image display that does the update when the user clicks.

Thanks in advance for any help.

The table can only have one accessory view. You need to put the button and image in the same view.

I thought that was a possibility, so I had tried commenting out everything for the checkbox. That’s actually how I left the test script saved on my computer. But I get the same result.

use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"
use script "Myriad Tables Lib" version "1.0.9"
use scripting additions

property theCheckbox : missing value
-- property theAccessoryView : missing value
property theImageView : missing value

-- This sample uses the 'accessory view' parameter. This is a more complex process requiring some knowledge of AppleScriptObjC.

-- Manipulation of views should be done on the main thread
if current application's NSThread's isMainThread() as boolean then
	--my buildAccViewWithCheckbox()
	my buildAccViewWithImage()
else
	--my performSelectorOnMainThread:"buildAccViewWithCheckbox" withObject:(missing value) waitUntilDone:true
	my performSelectorOnMainThread:"buildAccViewWithImage" withObject:(missing value) waitUntilDone:true
end if

-- make a table and add the accessory view
set theTable to make new table with data {"One", "Two", "Three", "Four", "Five"}
-- modify table theTable accessory view theAccessoryView
modify table theTable accessory view theImageView
-- set theAccessoryView to missing value -- to avoid error messages when saving
set theImageView to missing value
display table theTable
-- get the state of the checkbox
-- set theState to theCheckbox's state() as boolean
-- set theCheckbox to missing value -- to avoid error messages when saving
display dialog theState as text

on buildAccViewWithCheckbox()
	-- build a checkbox
	set my theCheckbox to current application's NSButton's alloc()'s initWithFrame:{{10, 10}, {150, 18}}
	tell theCheckbox
		its setButtonType:(current application's NSSwitchButton)
		its setTitle:"This is a checkbox"
		its setTarget:me
		its setAction:"checkboxClicked:" -- a handler in this script
	end tell
	-- make a view and add the checkbox to it; maximum size is 600pts x 200pts
	set my theAccessoryView to current application's NSView's alloc()'s initWithFrame:{{0, 0}, {160, 28}}
	theAccessoryView's addSubview:theCheckbox
end buildAccViewWithCheckbox

on buildAccViewWithImage()
	set theFile to "/Users/work/Downloads/26ba7a0e-65bb-4a56-bd36-9a772ac5aa97.png"
	set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:theFile
	set my theImageView to current application's NSImageView's alloc()'s initWithFrame:{{0, 0}, {64, 64}}
	theImageView's setImage:theImage
	theImageView's setImageScaling:(current application's NSImageScaleProportionallyUpOrDown)
end buildAccViewWithImage

-- this is called when the checkbox is clicked
on checkboxClicked:sender
	-- make an alert and show it as a sheet over the table window
	set theWindow to sender's |window|()
	set theAlert to current application's NSAlert's alloc()'s init()
	theAlert's setMessageText:"You clicked the checkbox"
	theAlert's setInformativeText:"Now close this sheet"
	theAlert's beginSheetModalForWindow:theWindow completionHandler:(missing value)
end checkboxClicked:

You’re passing a path where a URL is required. Either create an NSURL, or just append as «class furl» to the first of those lines.

(For troubleshooting performSelectorOnMainThread:, use log commands in the handlers called. In this case, you’ll quickly see theImage is missing value.)

Awesome, thank you! That worked.

And thanks for the tip on logging.

I’ve got the image and a button, but I haven’t been able to get my button to change my image. Another beginner mistake, I’m sure. I did try logging various things, but to no avail.

Also, any advice on if it’s possible / how to have the button read the currently selected row in Myriad Tables?

In my real table, each row represents an image file. My goal here is to have them be able to click the “Update Image” button and have it show them a preview of the image they have selected… but I don’t actually know if the accessory view can retrieve information from something outside the accessory view, in the standard table.

Any help is, as always, greatly appreciated.

use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"
use script "Myriad Tables Lib" version "1.0.9"
use scripting additions

property theButton : missing value
property theImageView : missing value

-- This sample uses the 'accessory view' parameter. This is a more complex process requiring some knowledge of AppleScriptObjC.

-- Manipulation of views should be done on the main thread
if current application's NSThread's isMainThread() as boolean then
	--my buildAccViewWithCheckbox()
	my buildAccViewWithImage()
else
	--my performSelectorOnMainThread:"buildAccViewWithCheckbox" withObject:(missing value) waitUntilDone:true
	my performSelectorOnMainThread:"buildAccViewWithImage" withObject:(missing value) waitUntilDone:true
end if

-- make a table and add the accessory view
set theTable to make new table with data {"One", "Two", "Three", "Four", "Five"}
modify table theTable accessory view theImageView
set theImageView to missing value
display table theTable
set theButton to missing value -- to avoid error messages when saving

on buildAccViewWithImage()
	set my theButton to current application's NSButton's alloc()'s initWithFrame:{{10, 0}, {150, 20}}
	tell theButton
		its setTitle:"Update Image"
		its setButtonType:(current application's NSMomentaryPushInButton)
		its setBezelStyle:(current application's NSRoundedBezelStyle)
		its setImagePosition:(current application's NSNoImage)
		its setTarget:me
		its setAction:"updateTheImage:"
	end tell
	set theFile to "/Users/work/Downloads/26ba7a0e-65bb-4a56-bd36-9a772ac5aa97.png" as «class furl»
	set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:theFile
	set my theImageView to current application's NSImageView's alloc()'s initWithFrame:{{0, 0}, {180, 200}}
	theImageView's setImage:theImage
	theImageView's setImageScaling:(current application's NSImageScaleProportionallyUpOrDown)
	theImageView's addSubview:theButton
end buildAccViewWithImage

on updateTheImage:sender
	set theFile to "/Users/work/Downloads/Mag glass.png" as «class furl»
	set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:theFile
	my (theImageView's setImage:theImage)
end updateTheImage:

This line probably isn’t helping your cause.

You can get a reference to the actual Cocoa table view using something like:

set theActualTableView to theTable's theController()'s dialogTableView()

Getting its selectedRow() should give you the row’s index (-1 for empty selection).

1 Like

As always, thanks so much. I did just need to delete that one line “set theImageView to missing value” and my image immediately started updating when I click the button.

On the second part, I’m confused on what “theController” is… I had assumed it was something specifically named within Myriad Tables that was already inside the table, but wherever I put that line in my code, I’m getting

(theTable doesn’t understand the “theController” message.)

Am I supposed to be updating that to be something in my code?

use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"
use script "Myriad Tables Lib" version "1.0.9"
use scripting additions

property theButton : missing value
property theImageView : missing value
property theActualTableView : missing value

-- This sample uses the 'accessory view' parameter. This is a more complex process requiring some knowledge of AppleScriptObjC.

-- Manipulation of views should be done on the main thread
if current application's NSThread's isMainThread() as boolean then
	--my buildAccViewWithCheckbox()
	my buildAccViewWithImage()
else
	--my performSelectorOnMainThread:"buildAccViewWithCheckbox" withObject:(missing value) waitUntilDone:true
	my performSelectorOnMainThread:"buildAccViewWithImage" withObject:(missing value) waitUntilDone:true
end if

-- make a table and add the accessory view
set theTable to make new table with data {"One", "Two", "Three", "Four", "Five"}
modify table theTable accessory view theImageView
display table theTable
set theButton to missing value -- to avoid error messages when saving

on buildAccViewWithImage()
	set my theButton to current application's NSButton's alloc()'s initWithFrame:{{10, 0}, {150, 20}}
	tell theButton
		its setTitle:"Update Image"
		its setButtonType:(current application's NSMomentaryPushInButton)
		its setBezelStyle:(current application's NSRoundedBezelStyle)
		its setImagePosition:(current application's NSNoImage)
		its setTarget:me
		its setAction:"updateTheImage:"
	end tell
	set theFile to "/Users/work/Downloads/26ba7a0e-65bb-4a56-bd36-9a772ac5aa97.png" as «class furl»
	set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:theFile
	set my theImageView to current application's NSImageView's alloc()'s initWithFrame:{{0, 0}, {180, 200}}
	theImageView's setImage:theImage
	theImageView's setImageScaling:(current application's NSImageScaleProportionallyUpOrDown)
	theImageView's addSubview:theButton
end buildAccViewWithImage

on updateTheImage:sender
	try
		set theActualTableView to my theTable's theController()'s dialogTableView()
		set theSelection to theActualTableView's selectedRow()
		set theFile to "/Users/work/Downloads/Mag glass.png" as «class furl»
		set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:theFile
		my (theImageView's setImage:theImage)
	on error theError
		log theError
	end try
end updateTheImage:

It is. When you make a table, the variable returned contains a script object, which contains a variable called theController that contains the actual Cocoa reference to the Objective-C table controller (using a script object is required for reasons not relevant here).

Two issues:

  • theController is an AS property, not a Cocoa method, so no parens:
		set theActualTableView to my theTable's theController's dialogTableView()

  • You haven’t declared theTable as a property of the script, so it doesn’t contain anything in your handler.

Bingo! Thanks a million.

All the complicated ASObjC bits are working in my test script now, so presumably I can port them to my real script without messing things up too badly. :pray:

Oh, and if you’d be kind enough to confirm (or deny) my hunch that trying to queue the image change based on the user selecting a row isn’t going to be easy, and I should stick with a user clicking a button to update the image?
I didn’t know if it’s possible to do a modification on the table to “setAction:” on selecting a row. Messed around with it to no avail. The button’s fine, just curious if it could be done the other way, which would be slightly friendlier.

Good hunch. It’s probably theoretically possible using notifications, but it would be a fair bit of code that I’m not sure would be totally reliable.

Ah, one other issue.

The table allows sorting, which can be handy. Once sorted, what’s returned when the table window is dismissed remains coherent, so I don’t operate on the wrong data. It doesn’t matter what order the rows are in when it’s returned.

set theActualTableView to my printCopyTable's theController's dialogTableView()
set theSelection to theActualTableView's selectedRow()

returns the index of the selected row, so I can use that index to get the original row out of the data I used to build the table, and then use that data to figure out what image I’m updating.

But once the user sorts the table, the index returned no longer corresponds to the order the data was in when I built it. So my “update image” button works perfectly, unless the table was sorted by the user after displaying. If it’s been sorted, when the “update image” button is clicked, then it will display the wrong image… the image for the row that was originally in the selected position.

Now, this isn’t a deal killer. I can just add “sorting style sort none” and stop them from sorting - sorting is only mildly useful in this scenario.

But thought I’d ask - is it possible to return the data of the selected row, rather than the index of the selected row? Or use the index to get the current data?

I did some googling and tried a couple things:

	set theActualTableView to my printCopyTable's theController's dialogTableView()
	set theSelection to theActualTableView's selectedRow()
	set rowValues to theActualTableView's ObjectValue:theSelection

and

	set theActualTableView to my printCopyTable's theController's dialogTableView()
	set theSelection to theActualTableView's selectedRow()
	set rowValues to theActualTableView's objectAtIndex:theSelection

but didn’t have any luck.

Like I said, if this is hard, I’ll just turn off the ability to sort.

Again, it could probably be done theoretically, but I doubt it’s practical. Tables are complex beasts programmatically.

OK, thanks. I just locked out sort, so a user can’t accidentally run the script on the wrong image by sorting, then choosing an image based on the (now inaccurate) preview.