How do I get the current file path in python dm-script - python

I want to get the file path of the current file in Digital Micrograph in python. How do I do this?
I tried to use
__file__
but I get NameError: Name not found globally.
I tried to use the dm-script GetCurrentScriptSourceFilePath() with the following code to get the value to python
import DigitalMicrograph as DM
import time
# get the __file__ by executing dm-scripts GetCurrentScriptSourceFilePath()
# function, then save the value in the persistent tags and delete the key
# again
tag = "__python__file__{}".format(round(time.time() * 100))
DM.ExecuteScriptString(
"String __file__;\n" +
"GetCurrentScriptSourceFilePath(__file__);\n" +
"number i = GetPersistentTagGroup().TagGroupCreateNewLabeledTag(\"" + tag + "\");\n" +
"GetPersistentTagGroup().TagGroupSetIndexedTagAsString(i, __file__);\n"
);
_, __file__ = DM.GetPersistentTagGroup().GetTagAsString(tag);
DM.ExecuteScriptString("GetPersistentTagGroup().TagGroupDeleteTagWithLabel(\"" + tag + "\");")
but it seems that the GetCurrentScriptSourceFilePath() function does not contain a path (which makes sense because it is executed from a string).
I found this post recommending
import inspect
src_file_path = inspect.getfile(lambda: None)
but the src_file_path is "<string>" which is obviously wrong.
I tried to raise an exception and then get the filename of it with the following code
try:
raise Exception("No error")
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
filename = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
print("File: ", filename)
but again I get <string> as the filename.
I tried to get the path out of the script window but I could not find any function to get it. But the script window has to know where its path is, otherwise Ctrl+S cannot work.
Some background
I am developing a module used for Digital Micrograph. There I also have test files. But to import the (still developed) module in the test file(s), I need the path relative to the test file.
Later the module will be installed somewhere so this should not be a problem then. But for offering a compelte set of tests, I want to be able to execute the tests without having to install the (not working) module.

For the file path to your current working directory, use:
import os
os.getcwd()

In DM-script, which you could call from a Python script, the command GetApplicationDirectory() gives you what you need. Usually, you would want to use the "open_save" one, i.e.
GetApplicationDirectory("open_save",0)
This will return the directory (String variable) that appears when File/Open is used, or File/Save is used on a new image. It is, however, not what is used on a "File/Save Workspace" or other saves.
From the F1 help documentation on that command:
Note, that the concept of "current" directory is somewhat broken on Win10 with applications - including GMS.In particular the "SetApplicationDirectory" commands do not always work as expected...
If you want to find out what the folder of a specific, currently shown document is (image or text) you can use the following DM-script. The assumption here is, that the window is the front-most window.
documentwindow win = GetDocumentWindow(0)
if ( win.WindowIsvalid() )
if ( win.WindowIsLinkedToFile() )
Result("\n" + win.WindowGetCurrentFile())

For everyone who also needs this (and just wants to copy some code) I created the following code based on #BmyGuests answer. This takes the file the script window is bound to and saves it as a persistent tag. This tag is then read from the python file (and the tag is deleted).
Important Note: This only works in the python script window where you press the Execute Script button and only if this file is saved! But from there on you probably are importing scripts which should provide the module.__file__ attribute. This meas this does not work for plugins/libraries.
import DigitalMicrograph as DM
# the name of the tag is used, this is deleted so it shouldn't matter anyway
file_tag_name = "__python__file__"
# the dm-script to execute, double curly brackets are used because of the
# python format function
script = ("\n".join((
"DocumentWindow win = GetDocumentWindow(0);",
"if(win.WindowIsvalid()){{",
"if(win.WindowIsLinkedToFile()){{",
"TagGroup tg = GetPersistentTagGroup();",
"if(!tg.TagGroupDoesTagExist(\"{tag_name}\")){{",
"number index = tg.TagGroupCreateNewLabeledTag(\"{tag_name}\");",
"tg.TagGroupSetIndexedTagAsString(index, win.WindowGetCurrentFile());",
"}}",
"else{{",
"tg.TagGroupSetTagAsString(\"{tag_name}\", win.WindowGetCurrentFile());",
"}}",
"}}",
"}}"
))).format(tag_name=file_tag_name)
# execute the dm script
DM.ExecuteScriptString(script)
# read from the global tags to get the value to the python script
global_tags = DM.GetPersistentTagGroup()
if global_tags.IsValid():
s, __file__ = global_tags.GetTagAsString(file_tag_name);
if s:
# delete the created tag again
DM.ExecuteScriptString(
"GetPersistentTagGroup()." +
"TagGroupDeleteTagWithLabel(\"{}\");".format(file_tag_name)
)
else:
del __file__
try:
__file__
except NameError:
# set a default if the __file__ could not be received
__file__ = ""
print(__file__);

Related

Python: Change Initial Directory for Open File Dialog

How to change initial directory for opening file dialog?
Even after specifying the initial directory in my Python code, the dialog still shows my Desktop.
from pydantic import DirectoryPath
import win32gui, win32con, os
import pandas as pd
def open_file_dialog():
initial_dir = 'C:/Users/i12389e/Documents' #<-----
filter= "Excel Files\0*.xlsx;*.xlsm;"
# customfilter='Other file types\0*.*\0'
fname, customfilter, flags=win32gui.GetOpenFileNameW(
InitialDir=initial_dir, #<-----
Flags=win32con.OFN_ALLOWMULTISELECT|win32con.OFN_EXPLORER,
File='somefilename', DefExt='.xlsx',
Title='Open file',
Filter=filter,
FilterIndex=0)
file_directory = fname
return file_directory
if __name__ == '__main__':
a = open_file_dialog()
print(a)
Based on this site I should use argument lpstrInitialDir instead of InitialDir. But then an error message showed up:
TypeError: 'lpstrInitialDir' is an invalid keyword argument for OPENFILENAME()
Thank you in advance for any pointer. I am struggling with it since almost 2 hours.
win32gui maps InitialDir to lpstrInitialDir.
lpstr is part of Hungarian Notation which has gone out of fashion some time ago, but still has to be used in many places by C programmers.
I think your problem is more related to algorithm used by Windows to select initial directory.
From https://learn.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-openfilenamea
The algorithm for selecting the initial directory varies on different platforms.
Windows 7 and latter:
If lpstrInitialDir has the same value as was passed the first time the application used an Open or Save As dialog box, the path most recently selected by the user is used as the initial directory.
Otherwise, if lpstrFile contains a path, that path is the initial directory.
Otherwise, if lpstrInitialDir is not NULL, it specifies the initial directory.
If lpstrInitialDir is NULL and the current directory contains any files of the specified filter types, the initial directory is the current directory.
Otherwise, the initial directory is the personal files directory of the current user.
Otherwise, the initial directory is the Desktop folder.
Analyze these steps and try to guess how system reacts after you call GetOpenFileNameW.
A lot more control in specifying initial parameters offers IFileOpenDialog, but it is COM-based and I don't know how hard it would be to use it from Python.
For GetOpenFileNameW, I would try to add path to File argument (make sure that specified path exists), and if problem still persist, put random path into InitialDir to avoid point 1..

Maya Python mass .obj import how to turn off obj import warning?

Hey I am importing a list of files into a maya scene via python.
Each time a .obj is imported maya gives me following warning:
Warning: Option "Use legacy vertex order" will only take effect when option "Multiple Objects" is enabled.
My question: Is it possible to turn off this warning? Or a way to to not show the warning in the first place?
edit: The problem is that this warning will show up on each and every file that gets imported from the list. I added a screenshot from maya.
I am loading the files like this:
cmds.file(filePath, i = True)
edit2: Here is my function that loops through files in a directory.
def loadFiles(*args):
# load References into scene from savefile
files = 'c:/testfolder'
if os.path.exists(files):
filesInFolder = [f for f in listdir(files) if isfile(join(files, f))]
for file in filesInFolder:
filePath = files + '/' + file
#cmds.file(filePath, i = True)
mel.eval("catchQuiet(`python(\"cmds.file(filePath, i=True)\")`)")
#print filePath
Thank you for your time and have a nice day!
Maya has a function called catchQuiet which is the easiest way of suppressing warning/error messages. If the expression throws an error, it will return 1 otherwise 0.
catchQuiet(python("cmds.file(\"/drive/myfile.obj\", i=True)"))
Unfortunately this function does only exist in mel but you could wrap it in python by using maya.cmds.mel to execute it.
Python Wrapper
The Python variant looks a little nasty, but that should work.
mel.eval("catchQuiet(`python(\"cmds.file('/drive/myfile.obj', i=1)\")`)")
Try this to turn off errors, warnings, and info in Script Editor:
import maya.cmds as cmds
filePath="/Users/swift/Desktop/file.ma"
cmds.file(filePath,i=True)
cmds.scriptEditorInfo(suppressErrors=True)
cmds.scriptEditorInfo(suppressWarnings=True)
cmds.scriptEditorInfo(suppressInfo=True)
or try this method just for warnings:
cmds.warning()
print '',
Then, turn them on again:
cmds.scriptEditorInfo(se=False,sw=False,si=False)

Adding a single python executable to windows system PATH for multiple computers?

I've created a command line program that I would like to distribute to some folks at work. Getting them all to install the python interpreter is simply unrealistic. Hence, I've created a single .exe file using PyInstaller. I am coming to realize however, that most people don't even know how to navigate to the directory where the .exe is, in order to invoke it. (And as of now, I haven't yet figured out how to get the program to run when clicked.) Is there a way to make the program add it's self to the users sys PATH when it is run the first time or would this require an installer? Thanks!
The common trap would be to read the PATH env. variable by using os.environ('PATH'). That would be a big mistake because this variable contains user & system paths mixed together. That's a special case for the PATH variable.
What you need to do is to fetch PATH env variable from the registry (user part), update it if needed, and write it back.
You can achieve that using winreg module, modifying the user PATH environment variable (or create if doesn't exist for this particular user)
read user PATH variable
if exists, tokenize the paths (else, path list defaults to empty)
compute the path of the current module (using os.path.dirname(__file__))
check if already in the path, if so, exit (I print the path list in that case so you can test)
create/update PATH user env. variable with the updated path list if necessary
Code:
import winreg,os
script_directory = os.path.dirname(__file__)
paths = []
key_type = winreg.REG_EXPAND_SZ # default if PATH doesn't exist
try:
keyQ = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Environment', 0, winreg.KEY_QUERY_VALUE)
path_old, key_type = winreg.QueryValueEx(keyQ, "PATH")
winreg.CloseKey(keyQ)
paths = path_old.split(os.pathsep)
except WindowsError:
pass
if script_directory in paths:
# already set, do nothing
print(paths)
else:
# add the new path
paths.append(script_directory)
# change registry
keyQ = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Environment', 0, winreg.KEY_WRITE)
winreg.SetValueEx(keyQ, 'PATH', 0, key_type, os.pathsep.join(paths))
winreg.CloseKey(keyQ)
Note that the user will have to logoff/logon for changes to take effect. Another solution would be to call setx on the PATH variable. System call, ugly, but effective immediately.
# change registry with immediate effect
import subprocess
subprocess.call(["setx","PATH",os.pathsep.join(paths)])
Or, courtesy to eryksun, some python code to propagate the registry changes to new processes. No need to logoff, no need for ugly setx, just call broadcast_change('Environment') using the code below:
import ctypes
user32 = ctypes.WinDLL('user32', use_last_error=True)
HWND_BROADCAST = 0xFFFF
WM_SETTINGCHANGE = 0x001A
SMTO_ABORTIFHUNG = 0x0002
ERROR_TIMEOUT = 0x05B4
def broadcast_change(lparam):
result = user32.SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE,
0, ctypes.c_wchar_p(lparam), SMTO_ABORTIFHUNG, 1000, None)
if not result:
err = ctypes.get_last_error()
if err != ERROR_TIMEOUT:
raise ctypes.WinError(err)
(seems that I have to refactor some code of my own with that last bit :))
env. variable read code took from here: How to return only user Path in environment variables without access to Registry?

Issue with Exception in Python

I have a chunk of Python code that is supposed to create MySQL schemas when executed. The issue is that whenever I run it, I get an error saying Raise Exception(e). Exception: [Error 2] the system cannot find the file specified
Below is my code:
from test.db.db import DB
from test.config import loggingSetup
loggingSetup.initializeLogging("createTest.py")
import logging
def createSports(db):
sqlFile = "..\\sql\\createSchema.sql"
db.run(sqlFile)
def create():
db=DB()
createSports(db)
def Main():
create()
if__name__=='__main__':
try:
main()
except Exception, e:
logging.info(e.message)
raise Exception(e)
Now I will admit, this code isn't entirely mine. I found this online, and I'm trying to rework it so it will fit my needs. My experience with python is minimal at best, but you have to start somewhere. On a basic level I under stand that the if statement is saying if __name__=='__main__' then try main(), but i guess I'm fuzzy as to why this exception is being thrown. I know this chunk of code has worked for others who have used it for similar projects, is there something wrong with my syntax that is throwing this off?
Try this :
initialize_logging(__file__)
It's because the file which you pass to the logging function isn't found. file in short gives you the path to the currently loaded module.
Read more about file here.
def createSports(db):
sqlFile = "..\\sql\\createSchema.sql" #file path goes here
in here you have to write your file path. It's an example. Complete the path and make sure this file is exist.
You should know the difference between "working directory" and actually directory. During the runtime, if you don't use the absolute path, the Code(not only python, but other language such as C++, Java), will treat the path as relative path. so the question is what it is relative to ? the answer is "working directory", you can change your working directory easily by "os.chdir". In this case, you need to transfer it to the absolute path:
solution:
1)
def createSports(db):
sqlFile = "..\\sql\\createSchema.sql"
sqlFile = os.path.abspath(sqlFile)
if not os.path.exists(sqlFile):
msg = "file %s doesn't exist" % sqlFile
raise Exception(msg)
else:
db.run(sqlFile)
2) use the full path, but pay attention to the escaped character, you should use this sqlFile = r"C:\User\Desktop\Test\sql\createSchema.sql", use the "r" to mean that this is raw sting, do NOT escape "\"

running a script when some file is created

Is it possible to have a script run on a file when it's created if it has a specific extension?
let's call that extension "bar"
if I create the file "foo.bar", then my script will run with that file as an input.
Every time this file is saved, it would also run on the file.
Can I do that? If yes, how? If not, why?
note: If there is some technicality of why this is impossible, but I can do very close, that works too!
If you are using linux use pyinotify described on the website as follows: Pyinotify: monitor filesystem events with Python under Linux.
If you also want it to work using Mac OS X and Windows, you can have a look at this answer or this library.
You could do what Werkzeug does (this code copied directly from the link):
def _reloader_stat_loop(extra_files=None, interval=1):
"""When this function is run from the main thread, it will force other
threads to exit when any modules currently loaded change.
Copyright notice. This function is based on the autoreload.py from
the CherryPy trac which originated from WSGIKit which is now dead.
:param extra_files: a list of additional files it should watch.
"""
from itertools import chain
mtimes = {}
while 1:
for filename in chain(_iter_module_files(), extra_files or ()):
try:
mtime = os.stat(filename).st_mtime
except OSError:
continue
old_time = mtimes.get(filename)
if old_time is None:
mtimes[filename] = mtime
continue
elif mtime > old_time:
_log('info', ' * Detected change in %r, reloading' % filename)
sys.exit(3)
time.sleep(interval)
You'll have to spawn off a separate thread (which is what Werkzeug does), but that should work for you if you if you don't want to add pyinotify.

Categories