hey guys just trying to create a simple import/export script in zscript, which is the most fucked up language i have ever seen in my life, but i cant find a way to access the %appdata% environment variable of windows.
anyone got any clues?
hey guys just trying to create a simple import/export script in zscript, which is the most fucked up language i have ever seen in my life, but i cant find a way to access the %appdata% environment variable of windows.
anyone got any clues?
Haha yeaaah. I’ve been learning zscript over the past few days and it’s been an impressively frustrating language to work with.
And no, you can’t access env vars through zscript. You would have to write a C++ DLL that you called with [FileExecute] which would return the value for you.
You could try this hacky approach:
[ul]
[li]Write a python script that gets the variable you want and writes it out to a file.[/li][li]Call the ZFileUtils function LaunchAppWithFile to launch that python script (ZBrush - The World's Leading Digital Sculpting Solution)[/li][li]Use [MemCreateFromFile] to load the contents of the file into a memory block[/li][li]Read the value using [MemReadString][/li][li]Cry a little[/li][/ul]
I realized this might actually be a useful tool (since I don’t know how to write DLLs). Here’s the hacky approach for getting an env var.
The python file:
# c:\dev\zbrush\python\getenv.py
import sys
import os
def write_env(env, outfile):
with open(outfile, 'w') as f:
f.write(os.getenv(env))
if __name__ == '__main__':
write_env(*sys.argv[1:])
And the zscript. Just make sure that dllPath points to the location of ZFileUtils. If you don’t have ZFileUtils grab it here: http://www.zbrushcentral.com/attachment.php?attachmentid=325518&d=1348839145
[VarDef, dllPath, "ZSTARTUP_ZPLUGS\MyPluginData\ZFileUtils.dll"]
[IButton,"Get Environment Variable", "Displays APPDATA value",
// The full path to the app to launch
[VarSet,appPath, "C:\Python26\python.exe"]
// The arguments to pass to the app being launched
[VarSet, outFile, "c:\dev\zbrush\python\envout.txt"]
[VarSet, envVar, "APPDATA"]
[VarSet, fullFilePath, [StrMerge, "C:\dev\zbrush\python\getenv.py ", #envVar, " ", #outFile]]
//create memblock for app path
[MemCreate, ZFileUTils_AppPath, 256, 0]
//write app path to memblock
[MemWriteString, ZFileUTils_AppPath, appPath,0]
// Call the app
[VarSet, err, [FileExecute,[Var,dllPath], "LaunchAppWithFile", #fullFilePath,, ZFileUTils_AppPath]]
//delete the memblock as we've done with it
[MemDelete, ZFileUTils_AppPath]
[If, err,
// An error
[Note,"An error occurred.",,2]
,
// Read the file
[MemCreateFromFile, envVarBlock, #outFile]
[VarSet, envVarVal, ""]
// Get the value from the memblock
[MemReadString, envVarBlock, envVarVal]
[MessageOK, envVarVal]
]
]
On the ZFileUtils doc page there is an example of a Routine that confirms that ZFileUtils exists. You may want to use that when distributing.
wow that is a lot of effort to do such a simple thing, but thanks for the code.
ya find it really stupid that zbrush has zscript instead of using a established languages like python or pearl.
also a method like that kinda introduces the same problem im trying to work around, since all im trying to do is make it so my script doesn’t have to be edited with the correct paths, by using environment variables to find what i need, but to set that up i would need to feed it my python path, and afileutils path.
Been messing with this for a bit. Here’s a script that will allow you to run a series of python commands and retrieve the output. I have a python file (runpycmd.py) that takes an outfilePath and python commandString to exec. The python file knows what data to return by exec’ing the command and looking for a variable named ‘zscriptOutput’ in the result. The contents of ‘zscriptOutput’ are then written out to outfile, which is then read in by the zscript. This means that whatever data you want to get back has to be assigned to a variable named zscriptOutput:
"import os; zscriptOutput = os.getenv('APPDATA', '')"
The zscript sets a pyCmdErr variable to 1 if the script errored, so you can deal with that accordingly. If it runs successfully, the return value is put into the variable pyCmdReturnValue. Because this is a first pass, the error handling is pretty primitive. Currently if the outfile doesn’t exist, the script assumes there was a python error. If the python script errors, the traceback is printed and the command window stays open.
One current issue with this method: the zscript does not wait for python.exe to complete before continuing script execution. This means that you have to add a delay into the zscript to allow for the python script to finish and write to outfile. If your script takes longer to finish than the zscript is delayed for, it will think there was an error. My idea for minimizing this issue is to, instead of using the existence of outfile to determine if there was an error, write out an errorFile if there was an error. That way I could add a Loop in the zscript after I execute the python command. This loop would check if outfile or errorFile exists. If outfile exists: the command completed successfully. If errorFile exists, there was an error; set pyCmdErr to 1 and Exit the loop. The loop would run for 5 to 10 seconds and delay 1 second between each loop.
There may be a better way of doing this, but I’m pretty new to this and it’s what I’ve got so far.
First the python script:
import sys
import os
import traceback
def write_cmd_output(outfile, *cmd_pieces):
""" execs the cmd and writes the value of a variable named zscriptOutput to <outfile>
<cmd> must assign a value to a variable named zscriptOutput.
"""
# Delete the file so if this errors the file won't be created.
# the zscript uses the file's existence to know if there was an error
if os.path.isfile(outfile):
os.remove(outfile)
# zscript doesn't allow string escaping so the cmd will come in split on spaces
cmd = ' '.join(cmd_pieces)
variables = {}
# exce <stmt> in <dictionary> stores the local and global variables in <dictionary>
# this is how we get the data to pass back
exec cmd in variables
if 'zscriptOutput' not in variables:
raise ValueError("cmd must assign a value to a variable named zscriptOuput:
'%s'" % cmd)
with open(outfile, 'w') as f:
f.write(variables['zscriptOutput'])
if __name__ == '__main__':
try:
write_cmd_output(*sys.argv[1:])
except:
traceback.print_exc()
raw_input('
Press Enter to exit')
And the zscript:
[VarDef, dllPath, "ZSTARTUP_ZPLUGS\MyPluginData\ZFileUtils.dll"]
[VarDef, pythonPath, "C:\Python26\python.exe"]
[VarDef, runPythonCmdFile, "C:\dev\zbrush\python\runpycmd.py"]
[VarDef, pyCmdReturnValue, ""] // This wil store the value returned by the python command
[VarDef, pyCmdErr, 0] // 0 if the script executed fine; 1 if it errored
[RoutineDef, RunPythonCmd,
// LaunchAppWithFile needs the app path to be in a memory block
[MemCreate, ZFileUTils_AppPath, [StrLength, pythonPath], 0]
[MemWriteString, ZFileUTils_AppPath, pythonPath, 0]
// The format of the command arguments are "runpycmd.py <output_file> <cmdString>"
//
[VarDef, pyArgs, [StrMerge, #runPythonCmdFile, " ", #outfile, " ", #command]]
[VarSet, err, [FileExecute, #dllPath, "LaunchAppWithFile", #pyArgs,, ZFileUTils_AppPath]]
[MemDelete, ZFileUTils_AppPath]
// Reset these so that if there is an error, we don't end up retrieving old values
[VarSet, pyCmdReturnValue, ""]
[VarSet, pyCmdErr, 0]
// This would describe an error in the DLL call, not in our python script
[If, err,
[MessageOK, "An error occurred."]
,
// To prevent the file from being read before it is written
[Delay, 1]
// If the file exists, the script executed properly
[If, [FileExists, #outfile],
// MemCreateFromFile won't fill an existing memory block
// so delete it if it exists. A non-existent memblock will have a size of 0
[If, [MemGetSize, envVarBlock] != 0,
[MemDelete, envVarBlock]
]
// Read the file
// MemCreateFromFile returns the size of the created memory block
// or if there was an error, an error code that is <= 0
[VarSet, memCreateErr, [MemCreateFromFile, envVarBlock, #outfile]]
[If, #memCreateErr <= 0,
[MessageOK, [StrMerge, "There was an error in MemCreateFromFile. Error Code: ", #memCreateErr]]
[Exit]
,
// No error, get the value from the memblock
[MemReadString, envVarBlock, pyCmdReturnValue]
]
,
// File doesn't exist, the python script errored
[VarSet, pyCmdErr, 1]
]
]
, command, outfile
]
[IButton,"Get Environment Variable", "Displays APPDATA values",
[RoutineCall, RunPythonCmd, "import os;zscriptOutput1 = os.getenv('APPDATA', '')", "C:\dev\zbrush\python\cmdout.txt"]
[If, #pyCmdErr,
[MessageOK, "There was an error in the python script, see the command window for details"]
,
[MessageOK, #pyCmdReturnValue]
]
]
phew
[QUOTE=passerby;21164]also a method like that kinda introduces the same problem im trying to work around, since all im trying to do is make it so my script doesn’t have to be edited with the correct paths, by using environment variables to find what i need, but to set that up i would need to feed it my python path, and afileutils path.[/QUOTE]
Yeah you aren’t going to be able to get away from that with zscript :/. The most barebones method would be to write your own DLL, that way you would only need to know the path to that.
It’s kind of amazing how long it takes to do what is really simple stuff in most other applications.
EDIT:
I know Zbrush also supports win32com, but available examples are incredibly slim.
got a work around zbrush seems to have a few key words that can be used in file paths, so this way i can atleast get the zPlugs, and thus ZFileUtils.dll or any custom DLL files i have there.
http://docs.pixologic.com/user-guide/customizing-zbrush/zscripting/zfileutils/#dllPath
“ZSTARTUP_ZPLUGS”
now if i only had a clue how to create a dll from c++ that can take a env var name and return the absolute path. or possibly use py2exe to create a self contained python command to do the same that i can put somewhere where zbrush can find it.