Copy link to clipboard
Copied
Hi. I write a script for PS and want to save user Preset choices so i dont have to click all my favorite settings again when i re-run the script. I couldnt find anything, standard JS window.localStorage is not working. Anyone? Thanks and best regards. Wolfgang
Extendscript doesn't have features which rely on a web browser such as cookies. Some apps (Bridge for example) allow you to save settings in application preferences. Others, such as Photoshop, do not.
Since Extendscript has support for XML, you can save settings to a file and read them back later. (Either XML or just plain text.)
More documentation is here:
https://developer.adobe.com/photoshop/
And below is a demo of using XML preferences.
/*
Utility Pack Scripts created by David M. Converse ©20
...
This is a sample script that I use to save preferences. The key to making this work is to declare all your UI variables with a name. Then you have to give the UI control a name that is the same as the variable. What's nice about this, is that you can add control items and it will automatically update.
///////////Preference and XML vars
var presetList = new Array( );//Blank array to be populated with the users preferences stored in an XML file
var run = true
var xmlFile = new File('~/De
...
At my level of scripting, I found it easier to work from a plain text file.
The following example writes 3 separate lines which will be used as variables to a file on the desktop:
/* WRITE TO PREF FILE */
// Pref file platform specific LF options
var os = $.os.toLowerCase().indexOf("mac") >= 0 ? "mac" : "windows";
if (os === "mac") {
prefFileOutLF = "Unix"; // Legacy = "Macintosh"
} else {
prefFileOutLF = "Windows";
}
// Create the preference file
var prefFileOut = new File('~/Desk
...
Copy link to clipboard
Copied
Extendscript doesn't have features which rely on a web browser such as cookies. Some apps (Bridge for example) allow you to save settings in application preferences. Others, such as Photoshop, do not.
Since Extendscript has support for XML, you can save settings to a file and read them back later. (Either XML or just plain text.)
More documentation is here:
https://developer.adobe.com/photoshop/
And below is a demo of using XML preferences.
/*
Utility Pack Scripts created by David M. Converse ©2018-24
This script is a sample of how to read/write settings to an XML file
Will work with any application that supports ExtendScript
Last modified 1/9/2024
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
main();
function main(){
var prefsXmlStr = '''<preferences> <creator>Test Script</creator> <setting_1> <name>Default</name> </setting_1> <setting_2> <value_1>100</value_1> <value_2>Maximum</value_2> <value_3>true</value_3> </setting_2> <data key="Version" value="1.0"/><data key="Last_Date" value="1-1-1"/></preferences>'''; //default settings, hardcoded for first run or if XML file is unusable
var prefsXML = new XML(prefsXmlStr); //new XML object
var prefsPath = '~/Desktop'; //this can be set as desired
prefsPath = prefsPath + '/Test Script Prefs.xml'; //name as desired
var prefsFile = File(prefsPath); //file onject
//we use variables to abstract the values from the XML
//XML values must be converted to String
var Tname = prefsXML.setting_1.name.toString(); //read from XML object
var slider_value = eval(prefsXML.setting_2.value_1.toString()); //number 0 to 100
var checkbox_value = eval(prefsXML.setting_2.value_3.toString()); //boolean
var ddl_value = prefsXML.setting_2.value_2.toString();
var vers = prefsXML.data.(@key == 'Version').@value.toString();
var Tdate = Date().toString(); //current date
var lastDate = prefsXML.data.(@key == 'Last_Date').@value.toString(); //last run date
var testWindow = new Window('dialog', 'Test', undefined, {closeButton:true}); //new window, supports Photoshop, can have additional properties
function prefsRead(prefsFile){ //read existing XML settings file and load
if(prefsFile.exists){ //file found on disk
try{
prefsFile.open('r'); //open for read
prefsXML = new XML(prefsFile.read()); //replace hardcoded XML object with XML from file
prefsFile.close();
}
catch(e){
alert('Could not read preferences file. Default settings will be loaded.');
prefsFile.remove(); //delete existing file on error
return;
}
}
else{ //can't find file
try{
var prefsFile = new File(prefsPath); //create new file
prefsFile.open('w'); //open for write
prefsFile.write(prefsXML.toXMLString()); //XML object to file
prefsFile.close();
}
catch(e){
alert('No preferences file found. Could not create new file.');
return;
}
}
}
function prefsWrite(){ //write updated values to file
if(! prefsFile.exists){ //prefs file not found
try{
prefsFile = new File(prefsPath); //create file
}
catch(e){
alert('Could not create preferences file.');
return;
}
}
try{ //save settings to prefs file
prefsFile.open('w'); //open for write
prefsFile.write(prefsXML.toXMLString()); //XML object write to file
prefsFile.close();
}
catch(e){
alert('Could not save preferences file.');
}
return;
}
function createWindow(){
try{
testWindow.panel = testWindow.add('panel', undefined, ''); //master panel
testWindow.panel.orientation = 'column'; //could use resource format instead
testWindow.panel.alignChildren = 'fill';
testWindow.panel.preferredSize = [250, 200];
testWindow.panel.textbox1 = testWindow.panel.add('edittext', undefined, ''); //add controls
testWindow.panel.textbox1.preferredSize = [200, 25];
testWindow.panel.textbox1.text = Tname;
testWindow.panel.slider1 = testWindow.panel.add('slider', undefined);
testWindow.panel.slider1.value = slider_value;
testWindow.panel.cb1 = testWindow.panel.add('checkbox', undefined, ' Checkbox 1');
testWindow.panel.cb1.value = checkbox_value;
testWindow.panel.ddl1 = testWindow.panel.add('dropdownlist', undefined, ['Maximum', 'Very High', 'High', 'Medium', 'Low']);
testWindow.panel.ddl1.selection = testWindow.panel.ddl1.find(ddl_value); //set list value
testWindow.panel.textbox2 = testWindow.panel.add('statictext', undefined, '', {multiline:true});
testWindow.panel.textbox2.preferredSize = [225, 25];
testWindow.panel.textbox2.text = 'Version ' + vers;
testWindow.panel.textbox3 = testWindow.panel.add('statictext', undefined, '', {multiline:true});
testWindow.panel.textbox3.preferredSize = [225, 75];
testWindow.panel.textbox3.text = 'Current Date:\r' + Tdate + '\rLast Run:\r' + lastDate;
testWindow.panel.button0 = testWindow.panel.add('button', undefined, 'Apply');
testWindow.panel.button1 = testWindow.panel.add('button', undefined, 'Ok');
testWindow.panel.button2 = testWindow.panel.add('button', undefined, 'Cancel');
//replace values with values from file
Tname = prefsXML.setting_1.name.toString(); //read from XML object
testWindow.panel.textbox1.text = Tname;
slider_value = eval(prefsXML.setting_2.value_1.toString()); //evaluate to change string to number
testWindow.panel.slider1.value = slider_value;
checkbox_value = eval(prefsXML.setting_2.value_3.toString()); //evaluate to change string to boolean
testWindow.panel.cb1.value = checkbox_value;
ddl_value = testWindow.panel.ddl1.find(prefsXML.setting_2.value_2.toString()); //listItem
testWindow.panel.ddl1.selection = ddl_value; //list box can also use indexed listitems
vers = prefsXML.data.(@key == 'Version').@value.toString(); //read key/value pair
testWindow.panel.textbox2.text = 'Version ' + vers;
lastDate = prefsXML.data.(@key == 'Last_Date').@value.toString();
testWindow.panel.textbox3.text = 'Current Date:\r' + Tdate + '\rLast Run:\r' + lastDate;
lastDate = Tdate;
prefsXML.data.(@key == 'Last_Date').@value = lastDate; //write value back to XML object
//update XML object with changed values, will be written on close.
testWindow.panel.textbox1.onChanging = function(){
Tname = testWindow.panel.textbox1.text;
prefsXML.setting_1.name = Tname;
}
testWindow.panel.slider1.onChange = function(){
slider_value = testWindow.panel.slider1.value;
prefsXML.setting_2.value_1 = slider_value.toString();
}
testWindow.panel.cb1.onClick = function(){
checkbox_value = testWindow.panel.cb1.value;
prefsXML.setting_2.value_3 = checkbox_value.toString();
}
testWindow.panel.ddl1.onChange = function(){
ddl_value = testWindow.panel.ddl1.selection.text;
prefsXML.setting_2.value_2 = ddl_value;
}
testWindow.panel.button0.onClick = function(){
prefsWrite();
}
testWindow.panel.button1.onClick = function(){
testWindow.close();
prefsWrite();
}
testWindow.layout.layout(true);
testWindow.show();
testWindow.active = true;
}
catch(e){
alert(e + ' ' + e.line);
}
}
prefsRead(prefsFile); //read file
createWindow(); //make window
}
Copy link to clipboard
Copied
In addition to TXT and XML mentioned by @Lumigraphics I believe that JSON is another viable option.
Photoshop does have application environment variables for $.setenv and $.getenv that persist for that session, however they are lost when exiting the software. I have not had much success in consistently using them though.
Copy link to clipboard
Copied
This is a sample script that I use to save preferences. The key to making this work is to declare all your UI variables with a name. Then you have to give the UI control a name that is the same as the variable. What's nice about this, is that you can add control items and it will automatically update.
///////////Preference and XML vars
var presetList = new Array( );//Blank array to be populated with the users preferences stored in an XML file
var run = true
var xmlFile = new File('~/Desktop/XYZ/xmlTest.xml');
if(xmlFile.exists){
prefXML = new XML(readXMLFile(xmlFile))
};// see if a preference file exists and load it.
else {
run = false
alert('There is no preset file')}
var dlg = new Window('dialog','XML Test')
/////////All elements separated in a separate window
///////////////////////////////////////////////////////////////////////////////////////////
var dlg = new Window('dialog','XML Test')
var myCheckBox = dlg.add('checkbox',undefined,'Check Box'); myCheckBox.name='myCheckBox';
var myRadio1 = dlg.add('radiobutton',undefined,'Radio 1'); myRadio1.name='myRadio1';
var myRadio2 = dlg.add('radiobutton',undefined,'Radio 2'); myRadio2.name='myRadio2';
myRadio1.value = true;
var myEditT = dlg.add('edittext',undefined,'Edit Test '); myEditT.name='myEditT';
var myStaticT = dlg.add('statictext',undefined,'Static Test '); myStaticT.name='myStaticT';
var mySlider = dlg.add('slider',undefined,25,0,100); mySlider.name='mySlider';
var myDropList = dlg.add('dropdownlist',undefined,['one','two','three']); myDropList.name='myDropList';
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
/////////////////////////////////// This needs to be in any dialog box for the presets to work///////////////////////////////////////////////////////////
/////////////////////////////////// The name of the dialog has to match whatever is in the current UI///////////////////////////////////////////
dlg.gp = dlg.add('group');
dlg.gp.orientation = 'row';
var presetListDrop = dlg.gp.add('dropdownlist',undefined, ['No Presets']); //presetListDrop.name = 'presetListDrop';
presetListDrop.size = [200,16]
presetListDrop.selection = 0;
dlg.show()
try{
if(prefXML.presets.children().length()>0){//loads presets into UI
setPresetList()
setUIvar(prefXML,0,dlg)
presetListDrop.selection = 0
}
}
catch (e){}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
dlg.hide();
////////////////////////////////////////////////////
//alert (myCheckBox.value + '\n' + myEditT.text + '\n' + myDropList.selection.text)
////////////////////////////////Presets//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
//function loops through the ui object and if a control item has been assigned a name uses that name to look up the preset value in the XML.
function setUIvar(x,n,d){//x= xml file; n = node number (0 is default node), d = UI dialog
var currentXMLVal;//used to store values from XML file. When this value is assigned, it checks to see if value from XML exist
var noMatch = false
for(var i = 0;i<d.children.length;i++){
noMatch = false;
if(d.children[i].type == 'panel' || d.children[i].type == 'group' || d.children[i].type == 'tab' || d.children[i].type == 'tabbedpanel'){setUIvar(x,n,d.children[i])};//reruns function if child is container and not control item.
else{
if(d.children[i].name){//Checks to see if child has a name assigned so only will reset those control items that have a name will be stored.
try{
//Assigns all variables from presets node. The "n" tells which preset to select.
currentXMLVal = x.presets.preset[n].child(d.children[i].name);
if(currentXMLVal == 'null'){currentXMLVal = null};
}//end try
//catch assigns 'no_good' to current XMLVal so that if there is no value in the XML file, it will not try to assign a bad value to the UI controls.
catch(e){currentXMLVal = 'no_good'};//end catch
//switch makes sure proper type of value is reassigned back to UI controls.
if(x.presets.preset[n].child(d.children[i].name).length() > 0 || d.children[i].type == 'button'){
switch(d.children[i].type){
case 'radiobutton':
d.children[i].value = returnBoolean(currentXMLVal);
break;
case 'checkbox':
d.children[i].value = returnBoolean(currentXMLVal);
break;
case 'edittext':
d.children[i].text = currentXMLVal;
break;
case 'slider':
d.children[i].value = parseFloat(currentXMLVal);
break;
case 'dropdownlist':
varHold = false;
if(x.presets.preset[n].child(d.children[i].name).@selecIndex.toString() == 'null'){d.children[i].selection = null}
else{d.children[i].selection = parseInt(x.presets.preset[n].child(d.children[i].name).@selecIndex)};
break;
};//end switch else
};//end if to see if there is a good value from the XML
};//end if for UI control having name
};//end else for if child is container or control
};//end for loop
};//end function setUIvar
//function returns a boolean value. Values stored in XML are returned as strings, so they need to be converted back to boolean values.
function returnBoolean(b){
if(b == 'true'){return true}
else{return false}
};
//Function resets the values of the preset list when items are added or deleted.
function setPresetList(){
presetList = new Array();
presetListDrop.removeAll();
for(var i=0;i<prefXML.presets.children().length();i++){presetListDrop.add('item',prefXML.presets.children()[i].@presetName)}
};//end function setPresetList
function readXMLFile(file) {
if (!file.exists) {
alert( "Cannot find file: " + deodeURI(file.absoluteURI));
}
else{
file.encoding = "UTF8";
file.lineFeed = "unix";
file.open("r", "TEXT", "????");
var str = file.read();
file.close();
return new XML(str);
};
};
Copy link to clipboard
Copied
At my level of scripting, I found it easier to work from a plain text file.
The following example writes 3 separate lines which will be used as variables to a file on the desktop:
/* WRITE TO PREF FILE */
// Pref file platform specific LF options
var os = $.os.toLowerCase().indexOf("mac") >= 0 ? "mac" : "windows";
if (os === "mac") {
prefFileOutLF = "Unix"; // Legacy = "Macintosh"
} else {
prefFileOutLF = "Windows";
}
// Create the preference file
var prefFileOut = new File('~/Desktop/My_Preference_File.txt');
if (prefFileOut.exists)
prefFileOut.remove();
prefFileOut.open("w");
prefFileOut.encoding = "UTF-8";
prefFileOut.lineFeed = prefFileOutLF;
var dateTime = new Date().toLocaleString();
prefFileOut.writeln(dateTime + ", " + "Source Doc: " + app.activeDocument.name);
prefFileOut.writeln(activeDocument.width.value);
prefFileOut.writeln(activeDocument.height.value);
prefFileOut.close();
The following script will read the 3 separate lines as variables from the text file and transform two of the variables from strings to numbers:
/* READ FROM PREF FILE */
var prefFileIn = File('~/Desktop/My_Preference_File.txt');
if (File(prefFileIn).exists && File(prefFileIn).length > 0) {
// Read the preference file from the user's desktop
prefFileIn.open('r');
// Read the 1st line from the log file, a means to an end...
var logInfo = prefFileIn.readln(1);
// Read the 2nd line from the log file & convert the string to a number/integer
var prefFileWidthValue = Math.floor(prefFileIn.readln(2));
//alert(prefFileWidthValue.toSource());
// Read the 3rd line from the log file & convert the string to a number/integer
var prefFileHeightValue = ~~prefFileIn.readln(3);
//alert(prefFileHeightValue.toSource());
prefFileIn.close();
// Demo - alert on the variables
alert(logInfo);
alert(prefFileWidthValue);
alert(prefFileHeightValue);
// Debugging
$.writeln(logInfo);
$.writeln(prefFileWidthValue);
$.writeln(prefFileHeightValue);
} else {
app.beep();
alert('There is no valid file named "My_Preference_File.txt" on the desktop!');
}
Copy link to clipboard
Copied
Thanks for all the answers. I ended up with a textfile on my local machine. a little sad though ,) thanks!
Copy link to clipboard
Copied
because sad will he have to write the data somewhere or not???
Copy link to clipboard
Copied
Unless you have a strictly memory-resident app that doesn't store state, it has to go into a file somewhere. A cookie is just a text file managed by a web browser.