Drag-and-drop loading of zipped ibw files
This procedure loads ibw files from a zipped file by dropping the zip onto the Igor window. The function LoadZippedIBW(filePath, fileName) can also be called from the command line or other functions. The zip is expanded into the current OS temp folder and the ibw files are deleted after loading. The code is 'dumb' and only works correctly for zip files with nothing else than ibw files in them, i.e., no support for other file types or a folder-structure. In pre-Igor 9 environments the zip is expanded via Tony's unzip script. Any suggestions for improvements are welcome. Supply your own PostProcessData() function if the waves need to be processed in some way (I use this for renaming and normalization).
Static Function BeforeFileOpenHook(refNum, fileNameStr, pathNameStr, fileTypeStr, fileCreatorStr, fileKind)
Variable refNum, fileKind
String fileNameStr, pathNameStr, fileTypeStr, fileCreatorStr
If (StringMatch(fileNameStr,"*.zip"))
PathInfo $pathNameStr
LoadZippedIBW(S_path, fileNameStr)
return 1
endif
return 0
End
//#################################
Function LoadZippedIBW(filePath, fileName)
String filePath, fileName
Variable unzipError, i
String tempFolder = SpecialDirPath("Temporary", 0, 0, 0)
NewPath/O/Q tmpPath, tempFolder
unzipError = unzipArchive(filePath+fileName, tempFolder)
if (unzipError)
Print "Could not unzip file " + fileName
return -1
endif
String tempFileList = IndexedFile(tmpPath, -1, "IGBW")
if (strlen(tempFileList) > 0)
for (i = 0; i < ItemsInList(tempFileList); i += 1)
String currFile = StringFromList(i,tempFileList)
LoadWave/O/Q/P=tmpPath currFile
DeleteFile/Z/P=tmpPath currFile
if (strlen(S_waveNames) > 0)
Wave work = $StringFromList(0, S_waveNames)
//PostProcessData(work, currFile) // post-process loaded wave
endif
endfor
endif
KillPath/Z tmpPath
return 0
End
//#################################
// stripped down unzip procedure written by Tony Withers + Igor 9 support
// https://www.wavemetrics.com/code-snippet/expand-zip-archive-platform-ag…
Static Function unzipArchive(archivePathStr, unzippedPathStr)
String archivePathStr, unzippedPathStr
GetFileFolderInfo /Q/Z archivePathStr
if(V_Flag || V_isFile==0)
printf "Could not find file %s\r", archivePathStr
return -1
endif
if(findlistItem(ParseFilePath(4, archivePathStr, ":", 0, 0), "zip;", ";", 0, 0) == -1)
printf "%s doesn't appear to be a zip archive\r", ParseFilePath(0, archivePathStr, ":", 1, 0)
return -1
endif
#if(IgorVersion() >= 9) // in Igor 9 just use unZip and quit
unzippedPathStr = RemoveEnding(unzippedPathStr,":")
unzipFile/O/Z archivePathStr, unzippedPathStr
return V_flag
#endif
String cmd
if(stringmatch(StringByKey("OS", igorinfo(3))[0,2],"Win")) // Windows
Variable WinVersion = str2num(StringByKey("OSVERSION", igorinfo(3)))
if (WinVersion<6.2) // 6.2 = windows 8.0
Print "unzipArchive requires Windows 8 or later"
return 0
endif
archivePathStr = ParseFilePath(5, archivePathStr, "\\", 0, 0)
unzippedPathStr = ParseFilePath(5, unzippedPathStr, "\\", 0, 0)
cmd = "powershell.exe -nologo -noprofile -command \"& { Add-Type -A 'System.IO.Compression.FileSystem';"
sprintf cmd "%s [IO.Compression.ZipFile]::ExtractToDirectory('%s', '%s'); }\"", cmd, archivePathStr, unzippedPathStr
else // Mac
archivePathStr = ParseFilePath(5, archivePathStr, "/", 0, 0)
unzippedPathStr = ParseFilePath(5, unzippedPathStr, "/", 0, 0)
sprintf cmd, "unzip %s -d %s", archivePathStr, unzippedPathStr
sprintf cmd, "do shell script \"%s\"", cmd
endif
ExecuteScriptText /B/UNQ/Z cmd
if (kVerbose)
Print S_value // output from ExecuteScriptText
endif
return V_flag
End
Forum
Support
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More
I recommend that you create a new directory within the temp directory and unzip into that directory. Otherwise, if there happen to be any .ibw files in tempFolder those will be loaded as well.
If you made this change, you could then use LoadData instead of LoadWave to load the entire contents of that temporary directory at once. Though for your purposes, that might complicate things because you need to post-process the data one wave at a time.
January 22, 2021 at 07:06 am - Permalink
Thank you for the suggestion. Yes, I thought about that, but the reason I decided against working in a separate folder was the following: I was under the impression that deleting a folder is kind of a big deal (judging from the warning message in the help), and I can't imagine that there are ibw files in the system's temporary folder. I went for the (seemingly) safer and slightly less robust method here. If you say that I should not worry too much then I might give it a go.
Edit: I tried to get the unzip to work inside a folder, and was thinking about supporting a folder structure inside zip files, but there were quite a few roadblocks. First, deleting a folder really is a big deal with forced user interaction. To keep this process smooth one would need to leave the folder in place, but then what to do with the contents? Is there some way to delete disk folders silently?
Also, while LoadData would make recursive loading easy, I really need to post-process each wave. The main reason is that the data from the measurement software contains the exact same internal wave name in every file, so I cannot load multiple files without renaming each wave before the next file comes in. If LoadData had some way to rename each wave according to the file name, it would work for my case. But I understand the general purpose of LoadData, and the problematic implications of such a behavior (what to do with packet experiments etc.).
So in conclusion, I don't see a way to improve the functionality here without lots of compromises. If there are some neat tricks I would be happy to learn about them.
January 22, 2021 at 07:45 pm - Permalink
> Is there some way to delete disk folders silently?
If you only need for yourself you should be able to tweak that in the settings.
The documentation has
> The default behavior is to display a dialog asking for permission. The user can alter this behavior via the Miscellaneous Settings > dialog's Misc category.
January 24, 2021 at 07:50 am - Permalink
You can use this function to delete a folder and its contents without prompting the user or enabling that setting in the Miscellaneous Settings category:
// Forcefully delete a folder. DeleteFolder also
// works but if the user hasn't told Igor not to prompt
// them before doing so then the tests won't finish running.
//
// BE CAREFUL USING THIS.
//*
static Function ForceDeleteFolder(fullPathToFolder)
String fullPathToFolder
Variable error
String platform = IgorInfo(2)
String errorMessage
String envInfoString
StrSwitch (platform)
Case "Windows":
try
// Do some setup.
String tmpDir = SpecialDirPath("Temporary", 0, 0, 0)
AbortOnValue (numtype(strlen(tmpDir)) != 0 || !(strlen(tmpDir) > 0)), 1
// Make sure that the directory we just got is, in fact, a directory.
GetFileFolderInfo/Q tmpDir
AbortOnValue (V_Flag >= 0 && !V_isFolder), 3
// Set an Igor symbolic path to the temporary directory.
String tmpDirectoryPath = UniqueName("tmpPath", 12, 0)
NewPath/Q $(tmpDirectoryPath), tmpDir
AbortOnValue (V_flag), 5 // Setting of the new path failed.
// Write a temporary batch file to the temporary directory that will
// call the "rmdir" command from the command shell and save the
// output of the command to a temporary file.
String tempBatFileName, tempResultsFileName
String tempBatFileFullPath, tempResultsFileFullPath
Variable tempBatFileRefNum, tempResultsFileRefNum
sprintf tempBatFileName, "IgorCommandScript_%.4u.bat", abs(trunc(StopMsTimer(-2)))
Open/P=$(tmpDirectoryPath)/T=".bat"/Z=1 tempBatFileRefNum as tempBatFileName
AbortOnValue (V_flag != 0), 7
// Add a path separator character to the end of the path, if necessary, and add on the file name.
tempBatFileFullPath = ParseFilePath(2, tmpDir, ":", 0, 0) + tempBatFileName
// Convert the path into a windows path that uses "\" as the path separator.
tempBatFileFullPath = ParseFilePath(5, tempBatFileFullPath, "\\", 0, 0)
// /s deletes all contents of directory and then the directory
// /q prevents DOS from asking us if we're sure.
String windowsFilePath = ParseFilePath(5, fullPathToFolder, "\\", 0, 0)
if (strlen(windowsFilePath) <= 0)
AbortOnValue 1, 8
endif
fprintf tempBatFileRefNum, "rmdir \"%s\"/s /q \r\n", windowsFilePath
Close tempBatFileRefNum
// Call the batch file we just created. Timeout after 2 seconds if this doesn't succeed.
String scriptText
sprintf scriptText, "cmd.exe /C \"%s\"", tempBatFileFullPath
ExecuteScriptText/W=2/Z scriptText
// Check for an error.
AbortOnValue (V_flag != 0), 9
// Delete the temporary batch file and temporary results file.
DeleteFile/P=$(tmpDirectoryPath)/Z=1 tempBatFileName
// If we get an error here we don't really care. We've already got
// the goods, so just run.
break
catch
error = -1
endtry
Case "Macintosh":
// This is a lot easier on the Macintosh because we can just use AppleScript.
try
// Figure out the POSIX path of the folder to delete.
String posixPath = ParseFilePath(5, fullPathToFolder, "/", 0, 0)
if (strlen(posixPath) > 0)
String appleScriptCommand
sprintf appleScriptCommand, "tell application \"Finder\" to delete ((POSIX file \"%s\") as alias)", posixPath
ExecuteScriptText/Z appleScriptCommand
AbortOnValue V_flag, 1
endif
envInfoString = S_Value
catch
errorMessage = "Could not execute AppleScript command."
print errorMessage
error = -1
endtry
break
EndSwitch
if (numtype(strlen(tmpDirectoryPath)) == 0) // Check for a null string
KillPath/Z $(tmpDirectoryPath)
endif
return error
End
Note: On macOS (10.14 and later at least), the user will be prompted to allow Igor to use AppleScript the first time this is run.
January 24, 2021 at 11:41 am - Permalink
Thank you very much for the script. Very interesting, also as an example how to run things on a system level. A bit overkill for just extracting a few zip files, though. So for my purposes I will stay with my 'dumb' approach.
January 24, 2021 at 07:11 pm - Permalink