Get index number of static text with a certain value (AppleScript) - user-interface

Fairly new to AS, but I haven't come across a similar question. I am using AppleScript to get information from the UI of an app window. The window has a static text x with value "Name", and I want to get the value of the next static text, which would be static text x + 1 ("John Smith"). The value of x differs with each case, however, so I cannot simply refer to static text 9 as it can be static text 47 next time.
My solution now is to get the value of every static text available in the window and add it to a list. Then I find the first item with value "Name", and refer to that item + 1 to get the actual name I want. However, as the window can contain quite a lot of static texts, this method takes some unnecessary time.
set MyList to {}
tell application "System Events" to tell process "cBK" to set y to count static text of scroll area 2 of splitter group 1 of window 1
repeat with x from 1 to y
tell application "System Events" to tell process "cBK" to set end of MyList to value of static text x of scroll area 2 of splitter group 1 of window 1
end repeat
on findFirst(lst, val)
local lst, val, i
try
if lst's class is not list then error "not a list." number -1704
if {val} is not in lst then return 0
script k
property l : lst
end script
repeat with i from 1 to count of k's l
if k's l's item i is val then return i
end repeat
on error eMsg number eNum
error "Can't findFirst: " & eMsg number eNum
end try
end findFirst
set Name to item (findFirst(MyList, "Name") + 1) of MyList
Is there a simple way to get x of static text x with a certain value, so I can get value of static text x + 1?

You can get, from your front window, all static text values in a list with single instruction. Then search in that list and when found, just get next item. Script bellow gives an example:
set myTarget to "Name"
tell application "System Events"
tell process "cBK" to set myValues to value of every static text of front window
repeat with i from 1 to count of myValues
if item i of myValues is myTarget then exit repeat
end repeat
if i < (count of myValues) then
set myNext to item (i + 1) of myValues
else
set myNext to "" -- not found
end if
end tell
The last test checks that value has been found in a static text or return "" if not.

Related

osascript: how to keep nested lists structure when output from command-line

I'm trying to code a script which can be able to extract the menu structure of a given app. This is a simplified working example:
tell application "System Events"
tell process "Terminal"
tell menu bar 1
set listA to name of every menu bar item
set listB to name of every menu of every menu bar item
set listC to name of every menu item of every menu of every menu bar item
set listD to name of every menu of every menu item of every menu of every menu bar item
set listE to name of every menu item of every menu of every menu item of every menu of every menu bar item
end tell
end tell
end tell
return {listA, listB, listC, listD, listE}
When I run this script on Script Editor, the result is a set of nested lists, like this (real result is too long, so I'm giving a sample):
{{{"Option1", "Option2", "Option3"}, {{"subOption1.1", "subOption1.2"}, {"subOption2.1", subOption2.2", "subOption2.3"}, {"subOption3.1"}}}
Thus, it's easy to know that menu Option1 has two items inside and so on...
But when I run this same script from python, using "osascript -e", the list structure and braces are gone, like this
{{"Option1", "Option2", "Option3"}, {"subOption1.1", "subOption1.2", "subOption2.1", subOption2.2", "subOption2.3", "subOption3.1"}}
So there is no way to know which sub-list corresponds to each other.
Is there a way to keep those braces or converting them into something different you can manage later on, or write this in a sort of "raw" data which keeps that nested structure?
Thanks in advance!
Following #red_menace advice, I finally managed to get all application menu structure:
itemList = []
def findit():
level = 0
while True:
part = ""
for lev in range(level):
if lev % 2 == 0:
part = " of every menu" + part
else:
part = " of every menu item" + part
subCmd = "set itemList to name" + part + " of every menu bar item"
if level % 2 == 0: # Grabbing items only (menus will have non-empty lists on the next level)
cmd = """
on run arg1
set procName to arg1 as string
tell application "System Events"
tell process procName
tell menu bar 1
%s
end tell
end tell
end tell
return itemList as list
end run
""" % subCmd
# https://stackoverflow.com/questions/69774133/how-to-use-global-variables-inside-of-an-applescript-function-for-a-python-code
# Didn't find a way to get the "injected code" working when passed as argument
proc = subprocess.Popen(['osascript', '-s', 's', '-', str(self._parent._app.localizedName())],
stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf8')
ret, err = proc.communicate(cmd)
ret = ret.replace("\n", "").replace('missing value', '"separator"').replace("{", "[").replace("}", "]")
item = ast.literal_eval(ret)
if err is None and not self._isListEmpty(item):
itemList.append(item)
else:
break
level += 1
return itemList != []
def fillit(subList):
# Pending: build hierarchy structure (dict format) from list
if findit():
print(itemList)
fillit(itemList)

How to find the number of items in a variable? (AppleScript)

I am trying to find the number of items in a variable, I tried to create somewhat of a while loop to test if the item of the variable exists, if it does then go to the next item and test if it exists, this repeats until the item does not exist, then it displays the current item number, which should be the final item of the variable.
Here is my code:
set stuff to "123456789"
set x to "1"
set num to item x of stuff
if exists item x of stuff then
repeat while exists item (x + 1) of stuff
if exists (item x of stuff) then
set x to (x + 1)
else
set num to x
end if
end repeat
end if
display dialog num
Currently when I run this code I get the error:
"Can’t get item 10 of "123456789"."
Which I understand tells me that 10 is the last item of this variable but the information does me no good in the form of an error message. Thanks in advance
How to find the number of items in a variable? (AppleScript)
The clue's in the question:
set variable to "123456789"
return the number of items in the variable --> 9
As already stated by red_menace, you can also quantify data objects using the length property (for list, record, or text objects), or by using the count command, which is superfluous in my view as it ends up accessing the length property anyway.
The AppleScript command count counts the number of elements in an object, for example count of stuff. In addition, classes such as list, record, and text also have a length property, for example length of stuff.
set stuff to "this is a test"
set x to 0
repeat with anItem in (get items of stuff)
set x to x + 1 -- just using x to count
log anItem
end repeat
display dialog "Number of items is " & x & return & "Count is " & (count stuff) & return & "Length is " & (length of stuff)
See the AppleScript Language Guide for more information.

How can I use apple script to step through my apple mailbox and see if addresses exist there

I'm very new to apple scripts, and honestly am not too much of a programmer. I want to do something very similar to this: How can I determine whether or not a contact with a given e-mail address exists in the Address Book?
Except I want to look through all my emails, and search for specific addresses (i have a list of thousands in csv/excel/numbers format), to see whether any email communication exists. I figured out how to run through my list and search, but not sure how to return whether mail exists. I often double email people with marketing material and would rather not do that. Here's my basic search script that pulls from numbers, just need to figure out how to ask the mail application if a mail item exists after running each email search through it.
set {eAddress} to getData()
repeat with i from 1 to 4
activate application "Mail"
tell application "System Events"
tell process "Mail"
tell window 1
keystroke "f" using {command down, option down}
keystroke {item i of eAddress} as string
end tell
end tell
end tell
end repeat
on getData()
set colA to {}
tell application "Numbers"
activate
tell table 1 of sheet 1 of document 1
#set lastRow to 4
set lastRow to row count
#first row index of (get end (last cell of column 1) direction toward the top)
repeat with i from 2 to lastRow
set end of colA to (value of cell i of column "A")
end repeat
end tell
end tell
return {colA}
end getData
I'm not sure if this will answer your question 100%, but if you just want to find whether or not you have messages from a particular address, you can do something like this:
set eAddresses to getData()
tell application "Mail"
repeat with thisAddress in eAddresses
if ((count of (messages of inbox whose sender contains thisAddress)) > 0) then
-- there are messages from this address, so do something
else
-- there are no messages from this address, so do something else
end if
end repeat
end tell
on getData()
set colA to {}
tell application "Numbers"
activate
tell table 1 of sheet 1 of document 1
#set lastRow to 4
set lastRow to row count
#first row index of (get end (last cell of column 1) direction toward the top)
repeat with i from 2 to lastRow
set end of colA to (value of cell i of column "A")
end repeat
end tell
end tell
return colA
end getData
The whose command in the fourth line — (count of (messages of inbox whose...)) — asks Mail to do an internal search for messages from that sender, which is more efficient than trying to loop through the messages on your own.

Setting a variable in AppleScript based on a list in a Numbers sheet

AppleScript beginner here. Searching high and low hasn't led me to the answer yet.
I'm using AppleScript to help run youth wrestling tournaments. Each division (based on age) is broken down into weight classes. For example: Novice 80 or Cadet 105.
Once a certain group of kids is put into a certain division/weight class, those kids get added to a new sheet that contains their bracket (think March Madness bracket but a small number of kids wrestling instead of playing basketball).
I've figured out how to get a group into a new sheet where they populate the bracket, but when this new sheet is created, I don't know how to make AppleScript change the name of the sheet to the correct division/weight class. I'm sure it has something to do with creating variables based on a list of the divisions/weight classes (that I have), but I can't figure out how to do it. Here's the relevant portion of the code:
tell document 1
set active sheet to the last sheet
set thisSheet to make new sheet
set the name of thisSheet to "[Division variable – Weight class variable]"
tell thisSheet
delete every table
end tell
Any ideas on how to make AppleScript name the sheet like I want?
To give you a small example that you're able to visualize what you're going after, heres a small snippet. I think its self explanatory, iterate through a list of titles, then apply the names to the sheets.
set divisionNames to {"Novice", "Cadet"} -- How You Grab These Values Matters
set weightClasses to {"80", "105"} -- These Values Too
tell application "Numbers"
activate
set thisDocument to make new document
tell thisDocument
repeat with i from 1 to count of divisionNames
make new sheet with properties {name:item i of divisionNames & space & item i of weightClasses}
end repeat
end tell
end tell
Alternately, if you're pulling the values from a list as a whole then you could
set sheetTitles to {"Novice 80", "Cadet 105"}
tell application "Numbers"
activate
set thisDocument to make new document
tell thisDocument
repeat with division in sheetTitles
make new sheet with properties {name:division}
end repeat
end tell
end tell
EDIT: In the spirit of helping a low to no budget school/organization.. here's another example answering the second question issued in comments. Again without knowing the structure of your data its hard to give you an exact answer on your specific case. Additionally, here's a link to a site they may help further the advancement on your project. https://iworkautomation.com/numbers/index.html
(*
set sampleKids to {"John Doe", "Jane Doe", "Johnny Foe", "Janie Foe", "Tommy Joe", "Tammy Joe"}
set sampleDivisions to {"Novice-80", "Novice-85", "Cadet-105", "Cadet-110", "Novice-80", "Cadet-105"}
tell application "Numbers"
activate
set thisDoc to make new document with properties {name:"Wrestling Sample"}
tell thisDoc
set the name of sheet 1 to "Sign In Sheet"
tell active sheet
delete every table
set newTable to make new table with properties {row count:(count of sampleKids) + 1, column count:2, name:"Sign In Sheet"}
tell newTable
set value of cell 1 of column "A" to "Name"
set value of cell 1 of column "B" to "Division"
set x to 2
repeat with eachName in sampleKids
set value of cell x of column "A" to eachName
set x to (x + 1)
end repeat
set x to 2
repeat with eachDivision in sampleDivisions
set value of cell x of column "B" to eachDivision
set x to (x + 1)
end repeat
end tell
end tell
end tell
end tell
*)
--********** IGNORE ABOVE THIS LINE IT'S ONLY BUILDING A SAMPLE TABLE **********--
--********** SAVE ABOVE TO ANOTHER SCRIPT FOR TESTING WITH NEW TABLE **********--
(*
ERROR HANDLING ISN'T PRESENT - AN EMPTY CELL IN FIRST COLUMN OR CHOSEN COLUMN WILL THROW AN
ERROR SINCE THEY ARE THE IMPORTANT PIECES OF DATA FOR GRABBING LISTS - SAVE SCRIPT
TO NUMBERS SCRIPT FOLDER OF YOUR CHOICE - ENTER ALL INFO ON FIRST TABLE OF FIRST SHEET OF
DOCUMENT THEN RUN SCRIPT - SCRIPT ACCEPTS ANY AMOUNT OF ROWS OR COLUMNS
*)
tell application "Numbers"
activate
-- Display A Simple Reminder That You're About To Lose Some Existing Data
display dialog "This Script Will Delete All Sheets Except The First Sheet Of This Document Before It Proceeds To Make New Sheets & Tables Based On The First Table Of The First Sheet." buttons {"Cancel", "Start"} default button 2 with icon 1
tell document 1
-- Get A List of the Sheet Names
set sheetNames to name of sheets
-- Start With A Fresh Slate, No Old Sheets
delete (every sheet whose name is not item 1 of sheetNames)
tell sheet 1
-- Grab and Set Future Header Values
set columnHeaders to value of cell of row 1 in table 1
-- Display A List Of Possible Choices To Create New Sheets With From The List We Make Above
set chosenColumn to choose from list columnHeaders with prompt "Which Column Do You Want To Use For New Sheets?" default items item 1 of columnHeaders
set chosenColumn to chosenColumn as text
tell table 1
-- Remove All Empty Rows to Help Prevent Error In Script
set {row_count, col_count} to {count rows, count columns}
set blank_row to {}
repeat with x from 1 to col_count
set blank_row to blank_row & missing value
end repeat
set x to 1
-- Delete Empty Rows In Reverse, It's Logical
repeat with y from row_count to 1 by -1
set row_values to value of cells of row y
if row_values = blank_row then delete row y
end repeat
-- Grab A List of All Divisions for Future Use Depending on Choice From Prompt, excluding the First Row Which Is A Header. If You Selected The First Column, We Have to Handle That Differently
if chosenColumn is item 1 of columnHeaders then
set theDivisions to the value of every cell of column "A" whose value is not chosenColumn
else
set theDivisions to the value of every cell of column named chosenColumn whose value is not chosenColumn
end if
end tell
end tell
-- Start the New "Sheet Making" Loop
repeat with division in theDivisions
-- Make An Empty Blank List At the Start of Every Loop
set matchingDivisions to {}
tell table 1 of sheet 1
-- Get All Rows Matching the Current Division of the Loop We Are On, to Make New Tables With Later
repeat with x from 1 to count of cells
if the value of cell x is division then
-- Put All Data About the Rows We Gathered Above Into the Empty List We Made
set the end of matchingDivisions to value of cells of row of cell x
end if
end repeat
-- Reset x, Because I'm Not Creative and Want to Use It Later
set x to 1
end tell
-- If The Sheet Of the Division We Are On, of the Loop, Doesn't Exist, Make It
if not (exists sheet division) then
make new sheet with properties {name:division}
tell sheet division
-- Start With A Fresh Slate On This New Sheet
delete every table
-- Make the Table With All Relevant Parameters
set currentDivisionTable to make new table with properties ¬
{row count:((count of matchingDivisions) + 1), column count:count of item 1 of matchingDivisions, name:division}
tell currentDivisionTable
set x to 1
-- Set The Header Values from A List We Created Earlier
repeat with theHeader in columnHeaders
set the value of cell x to theHeader
set x to (x + 1)
end repeat
-- Reset x Again, I'm Lazy
set x to 1
-- Set Starting Point to Start Filling The Table, Compensate For Our Headers
set rowIndex to 1
set columnIndex to 0
-- Start Filling The Table With Data, Which Comes From The List Earlier
repeat with x from 1 to count of the matchingDivisions
set rowData to item x of the matchingDivisions
tell row (rowIndex + x)
repeat with i from 1 to the count of rowData
tell cell (columnIndex + i)
set value to item i of rowData
end tell
end repeat
end tell
end repeat
end tell
end tell
end if
end repeat
-- Return To the First Sheet
set the active sheet to the first sheet
-- Display Notification That The Tables Are Done Being Made -- OPTIONAL
display notification "Processing is complete." with title "Numbers Table Converter" subtitle "All Tables Have Been Made." sound name "Hero"
end tell
end tell
Try
set thisSheet to make new sheet with properties {name:"yourname"}
Edit
Some explanation: if your struggling on how to adress third party apps try to load it's library and look up the methods you need. In applescript editor you will find it in the (I guess) menu. Then select the desired app to get the library
The full code modify at below:
set {begCol, endCol} to {2, 17}
set tgtCol to 1
tell application "Numbers"
tell front sheet of front document
tell active sheet
set getVal to rows's cells's value
set myOriginalTable to front table
set itemCode to {"CardNo", "SAL-DAY", "OT15-Money",
"OT2-Money", "OT3-Money", "OTHINC", "P0007", "DILIG", "P0004", "P0003",
"SEV_SPE", "P0011", "SI-SSF", "TI-TAXITEM", "P0022", "P0021", "P0025"} --
change to variable
set Amount to {"CardNo", "SAL-DAY", "OT15-Money",
"OT2-Money", "OT3-Money", "OTHINC", "P0007", "DILIG", "P0004", "P0003",
"SEV_SPE", "P0011", "SI-SSF", "TI-TAXITEM", "P0022", "P0021", "P0025"} --
change to variable
set setCols to 8
end tell
set myNewTable to make new table with properties ¬
{column count:setCols, row count:(count of itemCode) + 1,
header column count:0}
tell myNewTable
set value of cell 1 of column "A" to "cardNo"
set value of cell 1 of column "B" to "emCode"
set value of cell 1 of column "C" to "emName"
set value of cell 1 of column "D" to "itemCode"
set value of cell 1 of column "E" to "itemName"
set value of cell 1 of column "F" to "effDate"
set value of cell 1 of column "G" to "amt"
set value of cell 1 of column "H" to "remark"
set x to 2
repeat with eachAmount in Amount
set value of cell x of column "G" to eachAmount
set x to (x + 1)
end repeat
set x to 2
repeat with eachItemCode in itemCode
set value of cell x of column "D" to eachItemCode
set x to (x + 1)
end repeat
end tell
end tell
end tell
Thank you
According source code above. I found other way for use range select for determine data. But it still incorrect because the code pickup all data to one cell like this.
result after ran script
Could some one able to suggest me?
try
tell application "Numbers" to tell front document to tell active sheet
set delimiter to ","
set selected_table to first table whose class of selection range is range
tell selected_table
set my_selection to the selection range
set begCol to address of first column of my_selection
set endCol to address of last column of my_selection
set begRow to address of first row of my_selection
set endRow to address of last row of my_selection
set getVal to ""
repeat with j from begRow to endRow
repeat with i from begCol to endCol
set getVal to (getVal & (value of cell j of column i of selected_table) as text) & delimiter
set getVal to getVal & return
end repeat
end repeat
end tell
end tell
set AmountVal to {getVal}
tell application "Numbers"
activate
tell front sheet of front document
set myOriginalTable to front table
set setCols to 8
set myNewTable to make new table with properties ¬
{row count:(count of AmountVal) + 1, column count:setCols, header column count:0}
tell myNewTable
set value of cell 1 of column "A" to "cardNo"
set value of cell 1 of column "B" to "emCode"
set value of cell 1 of column "C" to "emName"
set value of cell 1 of column "D" to "itemCode"
set value of cell 1 of column "E" to "itemName"
set value of cell 1 of column "F" to "effDate"
set value of cell 1 of column "G" to "amt"
set value of cell 1 of column "H" to "remark"
set x to 2
repeat with eachAmount in AmountVal
set value of cell x of column "G" to eachAmount
set x to (x + 1)
end repeat
end tell
end tell
end tell
display notification "Already Done!" with title "Numbers"
on error
display dialog "Select a range first and then try again"
end try

Creating a new table in iWork numbers on a specific sheet using applescript

I am having trouble creating a new table on a specific sheet using Applescript unless the sheet I want insert the new table in is either new or current selected.
The general form of the code I am using is:
tell application "Numbers"
tell document 1
tell sheet "This is the sheet I want to use"
make new table with properties {name:"A new table"}
end tell
end tell
end tell
Has anyone had any more success at achieving this? This looks to me like a bit of a problem for some advanced spreadsheet scripts in Numbers.
OK. My first answer was accepted and could not be deleted, but was inadequate, so I have decided to edit it and add some code which really does solve the problem!
This code comes via Yvan Koenig, who credits Nigel Garvey. Both are active on the applescript-users list, which is excellent, by the way, not least because of the activity of these two gentlemen, and so many other great AppleScripters.
It relies on GUI scripting (which is always a horrible thing to have to fall back on, but it's what we've got).
The call to my activateGUIscripting() may cause a prompt for an admin password to appear. If you absolutely know that you have GUI scripting enabled (System Prefs->Universal Access->Enable Access for Assistive Devices) - then you can omit this line.
The next two lines are just example calls, so you need a sheet called "Sheet 1" for these example calls to work.
This will allow you to create a table in any sheet in any document, regardless of what is selected or frontmost.
--EXAMPLE CALLS
my activateGUIscripting()
my selectsheet(1, "Sheet 1")
my createNewTable(1, "Sheet 1", "myNewTable", 69, 13)
--NUMBERS UTILS
on createNewTable(dName, sName, newTable, nb_rows, nb_columns)
tell application "Numbers" to tell document dName to tell sheet sName
make new table with properties {name:newTable, row count:nb_rows, column count:nb_columns}
end tell
end createNewTable
--=====
on activateGUIscripting()
(* to be sure than GUI scripting will be active *)
tell application "System Events"
if not (UI elements enabled) then set (UI elements enabled) to true
end tell
end activateGUIscripting
--=====
(*
==== Uses GUIscripting ====
*)
on selectsheet(theDoc, theSheet)
script myScript
property listeObjets : {}
local maybe, targetSheetRow
--+++++
-- set log_report to "point 2 : " & (current date) & return
--+++++
tell application "Numbers"
activate
set theDoc to name of document theDoc (* useful if the passed value is a number *)
tell document theDoc to set my listeObjets to name of sheets
end tell -- "Numbers"…
set maybe to theSheet is in my listeObjets
set my listeObjets to {} -- So it will not be saved in the script *)
if not maybe then
error "The sheet “" & theSheet & "” is unavailable in the spreadsheet “" & theDoc & "” !"
end if -- not maybe
set maybe to 5 > (system attribute "sys2")
tell application "System Events" to tell application process "Numbers"
tell outline 1 of scroll area 1 of splitter group 1 of splitter group 1 of window theDoc
if maybe then (* macOS X 10.4.x
'(value of attributes contains 0)': '(value of attribute "AXDisclosureLevel" is 0)' sometimes works in Tiger, sometimes not.
The only possible instances of 0 amongst the attributes are the disclosure level of a sheet row and the index of the first row, which represents a sheet anyway.
Another possibility is '(value of attribute -1 is 0)', which makes me uneasy. *)
set targetSheetRow to first row where ((value of attributes contains 0) and (value of first static text is theSheet))
else (* macOS X 10.5.x or higher *)
set targetSheetRow to first row where ((value of attribute "AXDisclosureLevel" is 0) and ((groups is {}) and (value of first static text is theSheet)) or (value of first group's first static text is theSheet))
end if -- maybe…
(*
Handler modified to accomodate sheets requiring a lot of time to get the focus
*)
tell targetSheetRow to set value of attribute "AXSelected" to true
set cnt to 0
repeat (*
Must way that Numbers becomes ready to receive the value *)
try
tell targetSheetRow to set value of attribute "AXDisclosing" to true
exit repeat
on error
set cnt to cnt + 1
delay 0.5 -- half a second
end try
end repeat
end tell -- outline…
end tell -- "System Events"…
--+++++
-- set log_report to log_report & "point 3, cnt = " & cnt & return & (current date) & return
--+++++
tell application "Numbers" to tell document theDoc to tell sheet theSheet to tell table 1
with timeout of 20 * 60 seconds (*
WITH this setting, the script will be able to wait 20 minutes for the asked value.
I hope that the document will not be so huge that this delay prove to be too short. *)
value of cell "A1"
end timeout
end tell -- "Numbers"…
--+++++
-- set log_report to log_report & "point 4 : " & (current date) & return
--+++++
tell application "System Events" to tell application process "Numbers" (*
Do the trick one more time to be sure that the sheet is open *)
tell targetSheetRow to set value of attribute "AXDisclosing" to true
end tell -- "System Events"…
--+++++
-- return log_report & "point 5 : " & (current date) & return
--+++++
(*
End of the modified piece of code
*)
end script
run myScript
end selectsheet

Resources