Multi-Experiment Process, ask if I want to save change to experiment "Untitled" all the time.

Hello,
I have done a batch function based on the explanation to sequentially open experiment file find in: Choose File->Example Experiments->Programming->Multi-Experiment Process.

I manage to make a nice code using this example. Everything works well when I follow the tutorial but when I want to implement my own function I have a problem. It comes from a Hook function in my experiment:

static Function IgorStartOrNewHook(igorApplicationNameStr)
String igorApplicationNameStr

...

End

If I use this function, igor will ask before every new experiment:
Do you want to save change to experiment "Untitled"?

somebody have any idea why? Thanks again

Chris
Post the entire function, please.

Your function may be setting globals you don't expect (like V_Flag).
You can use the ExperimentModified operation to tell Igor you don't consider the current experiment in need of saving.

--Jim Prouty
Software Engineer, WaveMetrics, Inc.
JimProuty wrote:
Post the entire function, please.

Your function may be setting globals you don't expect (like V_Flag).
You can use the ExperimentModified operation to tell Igor you don't consider the current experiment in need of saving.

--Jim Prouty
Software Engineer, WaveMetrics, Inc.


Hello Jim,
Thank you for your reply, I already try ExperimentModified without success but maybe I called it at the wrong point, so I will try other combinations. The whole function is bellow, it is the function taken from Igor: Choose File->Example Experiments->Programming->Multi-Experiment Process. The modification is only a simple callback. It mimics the real modification that I have in my code but it may be easier for you to understand this code provided directly by Igor developers.
#pragma rtGlobals=1     // Use modern global access method.

#include "IgorTools"

// ABOUT MULTI-EXPERIMENT PROCESS

// This procedure file demonstrates a process that runs across multiple experiments.

// The challenge was to open a series of packed experiment files (with ".pxp" extensions) in a particular folder.
// Each experiment file contained waves named wave_lif1 and wave_lif2. We were asked to graph these waves
// and then export the graph as a PNG file and then to repeat the process for the next experiment.

// This file contains the procedures that cycle through experiments and execute a function for each experiment.

// When a file is opened, Igor calls the AfterFileOpenHook hook in this file. The hook checks to see if
// the file being opened is a packed experiment file and if it contains the expected waves. If so,
// the hook function calls a subroutine that graphs the waves and exports the graph as a PNG file.
// It then opens the next experiment file and repeats the process.

// This procedure file uses Igor's LoadPackagePreferences and SavePackagePreferences to store
// information used between invocations of AfterFileOpenHook. It uses Execute/P to post commands
// to Igor's operation queue for opening the next experiment.

// TO TRY THE DEMO

// Choose File->Example Experiments->Programming->Multi-Experiment Process to open
// the demo experiment and follow the instructions therein.

Menu "Macros"
    "Start Multi-Experiment Process", /Q, StartMultiExperimentProcess()
End

// NOTE: If you use these procedures for your own purposes, change the package name
// to a distinctive name so that you don't clash with other people's preferences.
static StrConstant kPackageName = "Multi-experiment Process"
static StrConstant kPreferencesFileName = "ProcessPrefs.bin"
static Constant kPrefsRecordID = 0      // The recordID is a unique number identifying a record within the preference file.
                                        // In this example we store only one record in the preference file.

// The structure stored in preferences to keep track of what experiment to load next.
// If you add, remove or change fields you must delete your old prefs file. See the help
// topic "Saving Package Preferences" for details.
Structure MultiExperimentProcessPrefs
    uint32 version                      // Prefs version
    uint32 processRunning               // Truth that we are running the mult-experiment process
    char folderPath[256]                // Path to folder containing experiments
    uint32 index                        // Zero-based index of next experiment in folder to load
    uint32 experimentsProcessed
    uint32 experimentsSkipped  
EndStructure

// In version 101 of the prefs structure we increased folderPath from 100 to 256 bytes
static Constant kPrefsVersionNumber = 101

//  Loads preferences into our structure.
static Function LoadPackagePrefs(prefs)
    STRUCT MultiExperimentProcessPrefs &prefs

    Variable currentPrefsVersion = kPrefsVersionNumber

    // This loads preferences from disk if they exist on disk.
    LoadPackagePreferences /MIS=1 kPackageName, kPreferencesFileName, kPrefsRecordID, prefs
    // Printf "%d byte loaded\r", V_bytesRead

    // If error or prefs not found or not valid, initialize them.
    if (V_flag!=0 || V_bytesRead==0 || prefs.version!=currentPrefsVersion)
        prefs.version = currentPrefsVersion

        prefs.processRunning = 0
        prefs.folderPath = ""
        prefs.index = 0
        prefs.experimentsProcessed = 0
        prefs.experimentsSkipped = 0

        SavePackagePrefs(prefs)     // Create default prefs file.
    endif
End

//  Saves our structure to preferences.
static Function SavePackagePrefs(prefs)
    STRUCT MultiExperimentProcessPrefs &prefs

    SavePackagePreferences kPackageName, kPreferencesFileName, kPrefsRecordID, prefs
End

//  This is the routine that you would need to change to use this procedure file for your own purposes.
//  See comments about labeled "TO USE FOR YOUR OWN PURPOSES".
static Function ProcessCurrentExperiment(prefs)
    STRUCT MultiExperimentProcessPrefs &prefs


    String folderPath = prefs.folderPath
   
    String experimentName = IgorInfo(1)
    String tmp = RemoveEnding(experimentName, ".pxp")
    String fullFilePath = folderPath + tmp + ".png"

    Display wave_lif1 wave_lif2
    ModifyGraph rgb(wave_lif1)=(0,0,65280),rgb(wave_lif2)=(0,52224,0)
    ModifyGraph mirror=2
   
    // Add annotation
    String text
    sprintf text, "Experiment %d, \"%s\"", prefs.index, experimentName
    TextBox/C/N=text0/M/H=36/A=LT/X=5.00/Y=0.00 text

    SavePICT/E=-5/RES=600/I/W=(0,0,4,3)/O as fullFilePath
End

static Function IsAppropriateExperiment()
    if (WaveExists(wave_lif1) && WaveExists(wave_lif2))
        print "We have found an experiment"
        return 1    // This looks like the kind of function we want to process.
    else
        print "Not appropriate experiment"
   
    endif
   
    return 0        // This does not appear to be the kind of experiment we want to process.
End

// Returns full path to the next experiment file to be loaded or "" if we are finished.
static Function/S FindNextExperiment(prefs)
    STRUCT MultiExperimentProcessPrefs &prefs
   
    String folderPath = prefs.folderPath
    NewPath/O/Q MultiExperimentPath, folderPath
   
    String nextExpName = IndexedFile(MultiExperimentPath, prefs.index, ".pxp")
   
    if (strlen(nextExpName) == 0)
        return ""
    endif
   
    String fullPath = prefs.folderPath + nextExpName
    return fullPath
End

// Posts commands to Igor's operation queue to close the current experiment and open the next one.
// Igor executes operation queue commands when it is idling - that is, when it is not running a
// function or operation.
static Function PostLoadNextExperiment(nextExperimentFullPath)
    String nextExperimentFullPath
   
    Execute/P "NEWEXPERIMENT "              // Post command to close this experiment.
   
    // Post command to open next experiment.
    String cmd
    sprintf cmd "Execute/P \"LOADFILE %s\"", nextExperimentFullPath
    Execute cmd
End


static Function IgorStartOrNewHook(igorApplicationNameStr)  //Christophe Nell: This is the new hook function that I need for my own experiment
    String igorApplicationNameStr
   
    print "IgorStartOrNewHook" //With a message or any other action, it always asks me 'Do you want to save "untitled" experiment?'
End
// This is the hook function that Igor calls whenever a file is opened. We use it to
// detect the opening of an experiment and to call our ProcessCurrentExperiment function.
static Function AfterFileOpenHook(refNum,file,pathName,type,creator,kind)

    Variable refNum,kind
    String file,pathName,type,creator

   
    STRUCT MultiExperimentProcessPrefs prefs
   
    LoadPackagePrefs(prefs)                     // Load our prefs into our structure
    if (prefs.processRunning == 0)
        return 0                                    // Process not yet started.
    endif
   
    // Check file type
    if (CmpStr(type,"IGsU") != 0)
        return 0        // This is not a packed experiment file
    endif
   
    // Check for expected waves
    if (IsAppropriateExperiment())
        ProcessCurrentExperiment(prefs)
        prefs.index += 1                            // Index tracks next experiment to be processed.
        prefs.experimentsProcessed += 1
    else
        DoAlert 0, "This experiment is not suitable. Skipping to next experiment."
        prefs.experimentsSkipped += 1
    endif
   
    // See if there are more experiments to process.
    String nextExperimentFullPath = FindNextExperiment(prefs)
    if (strlen(nextExperimentFullPath) == 0)
        // Process is finished
        prefs.processRunning = 0        // Flag process is finished.
        Execute/P "NEWEXPERIMENT "                          // Post command to close this experiment.
        String message
        sprintf message, "Multi-experiment process is finished. %d experiments processed, %d skipped.", prefs.experimentsProcessed, prefs.experimentsSkipped
        DoAlert 0, message
    else
        // Load the next experiment in the designated folder, if any.
        PostLoadNextExperiment(nextExperimentFullPath)      // Post operation queue commands to load next experiment
    endif

    SavePackagePrefs(prefs)
   
    return 0    // Tell Igor to handle file in default fashion.
End

static Function PossiblySaveCurrentExperiment()
    DoIgorMenu/C "File", "Save Experiment"      // Check if current experiment can be saved.
    if (V_flag == 0)                                // Experiment is not modified.
        return 0
    endif

    DoAlert 2, "Save current experiment before starting?"
    if (V_flag == 1)            // Yes
        SaveExperiment
    endif
    if (V_flag == 3)            // Cancel
        return -1
    endif

    return 0
End

// Allow user to choose the folder containing the experiment files and start the process.
Function StartMultiExperimentProcess()
print "StartMultiExperment"
    STRUCT MultiExperimentProcessPrefs prefs

    // First save current experiment if necessary.
    if (PossiblySaveCurrentExperiment())
        return -1       // User cancelled
    endif

    // Ask user to choose the folder containing experiment files to be processed.
    String message = "Choose folder containing experiment files"
    NewPath/O/Q/M=message MultiExperimentPath           // Display dialog asking for folder
    if (V_flag != 0)
        return -1                                           // User canceled from New Path dialog
    endif
    PathInfo MultiExperimentPath

    LoadPackagePrefs(prefs)                             // This initializes prefs if they don't yet exist

    prefs.processRunning = 1                                // Flag process is started.
    prefs.folderPath = S_path                               // S_path is set by PathInfo
    prefs.index = 0
    prefs.experimentsProcessed = 0
    prefs.experimentsSkipped = 0

    // Start the process off by loading the first experiment.
    String nextExperimentFullPath = FindNextExperiment(prefs)
    print "PostLoadNextExp"
    PostLoadNextExperiment(nextExperimentFullPath)      // Start the process off

    SavePackagePrefs(prefs)
   
    return 0
End
Here is another version of my function, much shorter:
Function CN_BatchSelectedExperiment()

    getfilefolderinfo/D
    newpath/O cgms S_path  
    String dirList = IndexedDir(cgms, -1, 0), cmd
    Variable numDirs = ItemsInList(dirList)

    // Store directory list in a free text wave.
    // The free wave is automatically killed when the function returns.
    Make/N=(numDirs)/T/FREE dirs = StringFromList(p, dirList)

    Variable i
    for( i=0; i<numDirs; i+=1)

        NewPath/O myPath S_path + dirs[i]
   
        String ExperimentFile = IndexedFile(myPath,-1,".pxp")  
        if(cmpstr(ExperimentFile, "") == 1)
        string directory = S_path + dirs[i] +  ":" + StringFromList(0, ExperimentFile, ";")
        Execute/P "NEWEXPERIMENT "
        sprintf cmd "Execute/P \"LOADFILE %s\"", directory
        Execute cmd
        Execute/P "CN_testExperiment()"

        endif
       
    endfor

End

Function CN_testExperiment()

        String experimentName = IgorInfo(1)
        PathInfo/s home
        String tmp = RemoveEnding(S_path, ".pxp")
        String fullFilePath = tmp + ".png"
        Display wave_lif1 wave_lif2
        ModifyGraph rgb(wave_lif1)=(0,0,65280),rgb(wave_lif2)=(0,52224,0)
        ModifyGraph mirror=2
   
        // Add annotation
        String text
        sprintf text, "Experiment \"%s\"", experimentName
        TextBox/C/N=text0/M/H=36/A=LT/X=5.00/Y=0.00 text
        SavePICT/E=-5/RES=600/I/W=(0,0,4,3)/O as fullFilePath
End

and if I had the following hook function, the batch function ask me all the time if I want to save the untitled experiment:

static Function IgorStartOrNewHook(igorApplicationNameStr)
    String igorApplicationNameStr
   
    Newdatafolder/o root:packages
    Newdatafolder/o/s root:packages:PatchModeOli
    Make/t/o/n=(4,3) GainDAC
    Make/t/o/n=(4,3) GainADC0
    Make/t/o/n=(4,3) GainADC1
        ...
End

Hello,
I have solved my problem by adding
"ExperimentModified 0"
at the end of the hook function that was responsible of my problem! Thanks a lot for your reply, best

Christophe