Changing Object Colours with Applescript

Hi guys,

First of all I just wanted to say thank you in helping me on some previous questions I had.
I feel like ive learnt alot since using this site.

I have a question regarding XCode and Applescript.
I basically have a bit of code that generates me an RGB value as 3 separate variables (rValue, gValue, bValue)
What im trying to figure out is how/if I can apply that RGB value to the fill colour of the below:

TextField Fill Colour
TextField Text Colour (Black or White)
Box Fill Colour

I have tried using code such as:



property rgbBoxPreview : missing value
property rgbTextPreview : missing value

set rValue to 86.41
set gValue to 136.98
set bValue to 90.36

rgbBoxPreview's setFillColor_(rValue, gValue, bValue)
rgbTextPreview's setFillColor_(rValue, gValue, bValue)
--I realise this is completely wrong but this is what im trying to achieve

-- Id like to also if possible be able to get the fill colour of an object
-- Ive got a bit of a script that looks at an RGB colour and figures out what the best colour is for the text out of White/Black to aid readability

-- something like this
rgbBoxPreview's getFillColor_(rValue, gValue, bValue)
rgbBoxPreview's getFillColor_(rValue, gValue, bValue)

set textColour to my setTextColour_(rValue, gValue, bValue)
rgbBoxPreview's setTextColor_(textColour)

Again, sorry that this code is completely wrong but Im still learning.
Im struggling to find any documentation on what properties I can specify for objects (such as ‘setStringValue’) etc.
Not ones specific to Applescript anyhow.

Any direction would be greatly appreciated as always :slight_smile:

(Software Versions etc at the bottom)

Also, on a side note:
How do you indent code on this site like you would normally do in coding?
I see loads of people doing it this site but never figured out how to do it.

Many Thanks

Model: Macbook Pro
AppleScript: Xcode 8.2.1
Browser: Firefox 51.0
Operating System: Mac OS X (10.10)

You need to create an instance of NSColor, and use that:

set theColor to current application's NSColor's colorWithDeviceRed:rValue green:gValue blue:bValue alpha:1.0
rgbBoxPreview's setFillColor:theColor

Thanks for the help again Shane.
I was having a little trouble with your code but it seemed to work once a fiddled about with it :slight_smile:

Final code for anyone with a similar issue:



set rValue to rValue / 255 --> 0.338892687611
        set gValue to gValue / 255 --> 0.537187721592
        set bValue to  bValue / 255 --> 0.354363711208
        
        set theColor to current application's NSColor's colorWithDeviceRed_green_blue_alpha_(rValue, gValue, bValue, 1.0)
        rgbBoxPreview's setFillColor_(theColor)


I think the issue wasnt with Shanes code but the fact that my RGB values were formatted in 0-255 wheras it will only accept values in the form of 0-1 - Dividing by 255 gets you the value you need

Thanks!

On the back of this Shane,

How would I go about setting the cell colour of a table to the RGB values?
Is is a similar principle but stating the column and row index?

Thanks

it’s considerably more complex with tables. If it’s a cell-based table, you need to implement -tableView:willDisplayCell:forTableColumn:row: in a table delegate, and color the cell within it.

Hi Shane,
Thanks for point me in the right direction.
Im having a hard time however implementing it.

Heres my code:

on bTableColor_(Sender)

        set theColumn1 to aTableView's columnWithIdentifier_("Header1")
        set theColumn2 to aTableView's columnWithIdentifier_("Header2")
        set theColumn3 to aTableView's columnWithIdentifier_("Header3")
        set theColumn4 to aTableView's columnWithIdentifier_("Header4")
        set theColumn5 to aTableView's columnWithIdentifier_("Header5")
        set theColumn6 to aTableView's columnWithIdentifier_("Header6")
        set theColumn7 to aTableView's columnWithIdentifier_("Header7")
        set theColumn8 to aTableView's columnWithIdentifier_("Header8")
        set theColumn9 to aTableView's columnWithIdentifier_("Header9")
        
        aTableView's beginUpdates()

        --set rowCount to numberOfRowsInTableView_(aTableView)
        repeat with i from 1 to 2 -- Loops through row by row to do the calculation
           -- The number 2 should be repalced with rowCount but I cant seem to get the row count.

            set theCell1 to aTableView's preparedCellAtColumn_row_(theColumn1,i-1)
            set theCell2 to aTableView's preparedCellAtColumn_row_(theColumn2,i-1)
            set theCell3 to aTableView's preparedCellAtColumn_row_(theColumn3,i-1)
            set theCell4 to aTableView's preparedCellAtColumn_row_(theColumn4,i-1)
            set theCell5 to aTableView's preparedCellAtColumn_row_(theColumn5,i-1)
            set theCell6 to aTableView's preparedCellAtColumn_row_(theColumn6,i-1)
            set theCell7 to aTableView's preparedCellAtColumn_row_(theColumn7,i-1)
            set theCell8 to aTableView's preparedCellAtColumn_row_(theColumn8,i-1)
            set theCell9 to aTableView's preparedCellAtColumn_row_(theColumn9,i-1)
            
            set cellContent1 to theCell1's stringValue()
            set cellContent2 to theCell2's stringValue()
            set cellContent3 to theCell3's stringValue()
            set cellContent4 to theCell4's stringValue()
            set cellContent5 to theCell5's stringValue()
            set cellContent6 to theCell6's stringValue()

-- This sends 3 numbers off to another sub routine that does some calculations and returns a single number.
-- The single number will go into cell8 on each row (each row will be different) depending on the values of the other columns in the row
set returnValue to my CalculationSub_(cellContent4, cellContent5, cellContent6)

--sets up the RGB values
-- This is setup by another sub routine but Im just declaring them here to shorten the code on here.
set rValue to 0.338892687611
        set gValue to 0.537187721592
        set bValue to 0.354363711208
        
        set theColor to current application's NSColor's colorWithDeviceRed_green_blue_alpha_(rValue, gValue, bValue, 1.0)
        
--This area doesnt work.
-- I cant to seem to set a string value of a specific cell
            theCell8's setStringValue_(returnValue)
            theCell7's setStringValue_(returnValue * 2)
            
            theCell7's setTextColor_(theColor)
            theCell8's setFillColor_(theColor)
            
        end repeat

        aTableView's endUpdates()
        aTableView's reloadData()

    end bTableColor_

Thing is:
My code doesnt bring up any errors.
Almost like its doing it… but not at the same time
Is it somthing simple or is my completelty wrong haha!

Thanks again

Slow down…

First, if you’re using 10.10, you should probably be using interleaved terminology rather than underscore terminology. The fact that you’re using the latter suggests you’re editing in Xcode rather than an external editor, which is a bad idea.

Next, you need to decide whether you are using a cell-based table or a view-based table. They work quite differently, and I can’t remember off-hand when Xcode changed which it defaults to. Cell-based tables are deprecated, although they are still commonly used. Some of your code only applies to cell-based tables.

Then you need to explain exactly what you want to do. Are you wanting to color individual cells, or just rows?

Finally, you need to read up on table views and delegates. You don’t act on cells directly: you implement delegate methods that get a chance to modify the cells/views as the table view requests them.

Yeah ive seen various bits of code using both interleaved and underscore and wasnt sure what the difference was - I assumed it was just 2 ways of doing it. Is it just bad practice or could/will it cause problems later down the line?

I am using cell based tables - I want to be able to enter data straight into the table but also later on down the line pull data in from a SQL database. By default XCode sets the cells to View Based tables. I have changed the table to cell based which you can do by either dragging ‘Text Cells’ onto the table or changing it to cell based from the table views attributes

The end goal is to have a table that I can enter a colour into (Lets say a Pantone reference) - Ive got a working bit of code that turns a ‘Lab’ value into an RGB breakdown - I want to set Cell 1 of the row selected to the RGB breakdown returned from the subroutine. Ive also got another piece of code that looks at the RGB value and gives me a best possible text colour for that cell so that the text is viewable (Returns Black or White)

So in Summary:
Enter a row of data (Done)
When entered passes the colours Lab value to a subroutine and returns the RGB (Done)
Passes the RGB onto the textColour subroutine to return Black or White (Done)
Populates the same row with a few variables (Done)
Sets the Cell Fill Colour to the RGB value (Not Done)
Sets the Cell Text Colour to White or Black (Not Done)

Currently I am using an Array Controller to first populate the table with empty rows:

set myData to {{Colour_Ref:""},{Colour_Ref:""},{Colour_Ref:""}} -- Sets the table to 3 Rows
        set my tableController to NSArray's arrayWithArray:myData

So I know how to populate a full table (which I can use later on to pull from SQL)
But I dont know how I can use an Array Controller to populate Cell 3 of Row 9 for example without overwriting all the Rows above - Or if I can even populate it using this method?

Ive tried looking all of this up online - and ive found alot of answers on this site to alot of questions.
Im still learning XCode - Ive only been using it for a month or so now and already have picked up quite a bit. Can you recommend any bed time reading for XCode/Applescript? Theres alot of info online but it mainly covers XCode Swift etc.
I cant seem to find much on ASOC

Anyway ive rambled on for too long now.
Thanks Again

So you need to make your script a delegate of the table view, and implement the tableView:willDisplayCell:forTableColumn:row: delegate handler like this:

on tableView:theTable willDisplayCell:theCell forTableColumn:theColumn row:rowNum
  -- check the column and row, calculate and make color
  theCell's setBackgroundColor:newColor
  ...

Hi Shane,

Thanks for the fast reply.
Thats great! Makes sense how that works.

Is there a way I can prevent it from the infinitie loop?
I understand it works by when I click, double click on any point it logs the cell, row and column - Which is great!
Im wanting to set variables within this code so currently whenever I do anything on the table it will trigger the code to run.

can I modify it so that the code whill only run lets say if columns 1-6 are filled in on the selected row?
Can it also only trigger when the cell is filled in rather than selected (ie when I hit enter?)

Thanks again

There is no infinite loop. It’s called whenever the table needs to draw a cell.

The code runs when it runs; you just control what it does.

That’s something separate – that requires an action handler that you connect the cell to in the interface.

Apologies, I prob didnt use the correct terminolgy - Its not an infinite loop per say. Its more of a wall of text when it runs.
As im wanting to set the cells as variables so I can pass them onto other routines to do the calculations it hits me with a continuous wall of text.

I understand that - I know i should be able to write some IF statments to capture everything I need and to minimise the code running. However, its the accessing/extracting the data im struggling with. (This comes down to a terminolgy thing on XCode)

Ive tried something like this that worked before - So I can check if null etc:

set theCell1 to aTableView's preparedCellAtColumn_row_(0,rowNum)
        set theCell2 to aTableView's preparedCellAtColumn_row_(1,rowNum)
        set theCell3 to aTableView's preparedCellAtColumn_row_(2,rowNum)
        set theCell4 to aTableView's preparedCellAtColumn_row_(3,rowNum)
        set theCell5 to aTableView's preparedCellAtColumn_row_(4,rowNum)
        set theCell6 to aTableView's preparedCellAtColumn_row_(5,rowNum)
        
        set cellContent1 to theCell1's stringValue()
        set cellContent2 to theCell2's stringValue()
        set cellContent3 to theCell3's stringValue()
        set cellContent4 to theCell4's stringValue()
        set cellContent5 to theCell5's stringValue()
        set cellContent6 to theCell6's stringValue()

This just gives me a wall of ‘Stack overflow. (error -2706)’ text - Which is never good :stuck_out_tongue:

This is fine - I have no problem with other methods.

Ive tried adding a button to the table which adds one to all rows.
Assigning the Action to a routine that sets the variables
the text field assigned to the button.

Problems are:
the button is ‘clicked’ when the cell is clicked so the code runs before theyre any values in the cell
when it does run I dont know how to tell the routine where the button has come from (ie row 4 etc)

Sorry for the questions.
As I said im still learning (as we all are I suppose) - Hence why im on this site almost everyday.
Ive even bought your book today which has got some good info in but sadly nothing about tables :frowning:

Thanks again

All you want to do in that handler is change the background color. Do all the calculations in an action handler called when contents of the cell changes, and store the result for the delegate method to pick up when it does the actual coloring.

You don’t want to talk to cells – you get the contents from either the array controller or the variable that holds your data. Cells are about presentation of data.

OK So I think I might have a way of possibly doing it.
If you can help me figure how to parse the info from an Array Controller.

Heres what Ive done:
Setup 2 tables with 2 separate Array Controllers (Im wanting Table1 to basically hold all the info then pass it onto a smaller filtered Table2 - This will show the colour and the calcs etc.
When I run log importController it shows something like this:


--> Log importController
        {
        A = 1;
        B = 1;
        "Colour_Ref_I" = "";
    },
        {
        A = 2;
        B = 2;
        "Colour_Ref_I" = 2;
    }

--This shows me all the rows and columns

-->Log item 1 of importController
{
        A = 1;
        B = 1;
        "Colour_Ref_I" = "";
    }

So… Im able to extract a single line.
Meaning I can create a loop to run through line by line.

Is there an easy way of me setting the variables from the item x of importController?
Ie:

set theValueA to item 1 of importController
set theValueB to item 2 of importController
set theValueCol to item 3 of importController

item 1 of course wont work as the delimter is a semicolon - I could convert the semicolon but would need a way of removing the identifier name and equals sign - Im sure theres an easier way

The other thing is if there is a way of using ‘item 1 of…’ to an Array Controller it wont always work as if a column is not filled in (lets say column A is empty) it will show like this:


-->Log item 1 of importController
{
        B = 1;
        "Colour_Ref_I" = "";
    }

Many Thanks

Two things:

  • You should store your data in records, not lists. That solves the problem of empty cells.

  • You access the array controller’s contents using arrangedObjects()

Two things:

Correct me if Im wrong but the difference being the ‘title’?
Ie a List: {Var1, Var2, Var3, Var4}
a Record {Title:Var1, Product:Var2, Name:Var3, Age:Var4}

Log item 1 of importController returns:
{A = 1; B = 1; “Colour_Ref_I” = “”;}
(Which looks to be in record form?

I was unable to make any progress using the above code.
I did however figure another way around it:

set myData to importController --> {A = 1;B = 2;"Colour_Ref_I" = "Test";},{A = 2;B = 2;"Colour_Ref_I" = 2;}
set processData to item 1 of myData --> {A = 1;B = 1;"Colour_Ref_I" = "";}
set aValue to A of myData --> 1
set bValue to B of myData --> 2
set colValue to Colour_Ref_I of myData --> Test

So I will write a repeat function to loop through how many filled rows there are.

With regards to adding a new line/populating the table:

Currently in using this bit of code:

set myData to {{Colour_Ref_I:""}{Colour_Ref_I:""}{Colour_Ref_I:""}{Colour_Ref_I:""}}
        set my importController to NSArray's arrayWithArray:myData
--> Adds 4 rows with Colour_Ref_I as ""

is there a way I can populate the table line by line rather than in one big chunk?
Something like this?

Repeat with i from 1 to 4
set myData to {{Colour_Ref_I:"",A:1,B:2}} -- Always 1 line only
set line i of my importController to NSArray's arrayWithArray:myData -- I know this is completelty wrong, Just to show you an idea
end repeat

Thanks

Yes, sorry, you are using records/dictionaries. But you should complete ones, so:

set myData to {{Colour_Ref_I:"", A:0, B:0},...}

You can add a new row using addObject: or addObjects:, like this:

importController's addObject:{Colour_Ref_I:"",A:1,B:2}

There are also insertion methods.

I knew it was something like that - I wasnt far off with my code I had (I tried using addObject before)
Problem Im getting is this error:

-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x608000020680 (error -10000)

Am I right in thinking that these errors occur when im trying to pass a number to what XCode is expecting a string for example? I know the Applescript classes dont mix with XCode? Is that right?

Thanks

That suggests your datasource is an NSArray rather than an NSMutableArray.

In fact, it suggests your data is an array if integers. That makes no sense, so you probably have a problem with your bindings or connections for the array controller.