Python: Change Initial Directory for Open File Dialog - python

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..

Related

How do I get Windows to remember the last directory used using tkinter filedialog?

Having code like:
from tkinter import filedialog
image_formats= [("JPEG", "*.jpg"), ("All files", "*.*")]
file=filedialog.askopenfilename(filetypes=image_formats)
I can open a file dialog box which leads me to a .jpg file.
On my Windows 7 development box this remembers over closing and opening the program the directory -- easy allowing selecting multiple files from the directory.
However, after distribution, using cx_Freeze and its bdist_msi option, the same program to a Windows 10 machine the directory is no longer remembered. How do I get the Windows 7 behaviour on the Windows 10 system? Preferably I do not perform this manually but rely on the underlying Windows mechanism.
PS Full bdist_msi distribution of the actual program is available at https://sites.google.com/site/klamerschutte/system/app/pages/admin/revisions?wuid=wuid:gx:403dfb9983518218
I know this is an old question but I happened to run into this issue recently. I tried this with the latest version of Python 3.7.
My way around this: Just don't add an initialdir argument. On Windows 10, it will start from the last used directory when the filedialog is called.
If I understand your question properly you want to know how to set the initial starting directory whenever the dialog is shown for selecting a file (of whatever types).
You may set this by the initialdir argument to askopenfilename which will take the path of the starting directory you want.
For example if I always wanted the dialog to open to the user's home folder then I could use os.path.expanduser('~') as the initialdir argument.
If you want it to remember the last directory used then get the parent directory of the file selected from the dialog using os.pardir and store it in a variable.
Try below code, it will remember the last directory used by the tool
filename = askopenfilename(parent=root,title='Choose template file', filetypes =[('Excel Files', '*.xlsx')])
Try this:
CORRECTION:
create a pages_to_connect_pages.py to allow variables go inside any class (from stackoverflow, I don´t remember the reference, sorry)
2.create a default directory in never_opened_directory.txt, to use first time
last_opened_directory.txt is created when you open a directory
use their references in pages_to_connect_pages.py
insert references in main app as bellow
[![create this file and all variables go to any class using Pages.something (took from stackoverflow, dont remember where from) ][1]][1]
[![create files to "remember" last directory opened after main app is closed][2]][2]
in main app:
my_app
from pages_to_connect_pages import Pages
# we see if the file last_opened_directory.txt is empty(never used)
filesize = os.path.getsize("last_opened_directory.txt")
# if Pages.last_opened_directory == '':
if filesize == 0:
Pages.never_opened_directory = open("never_opened_directory.txt", "r")
directory_to_open = Pages.never_opened_directory.read()
Pages.never_opened_directory.close()
# elif Pages.last_opened_directory != '':
elif filesize != 0:
Pages.last_opened_directory = open("last_opened_directory.txt", "r")
directory_to_open = Pages.last_opened_directory.read()
Pages.last_opened_directory.close()
else:
pass
reference:Python save variable even after closing the gui
If you want askopenfilename to remember last used directory, set initialdir to a nonexistent folder, something like initialdir = "/áéá".
This works for me on Windows 10.

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?

Python Scripts + Use of folders and paths

This time I need help with python and paths manipulations. In first place i will will show you the structure im using on this set of apps:
MainFolder:
Folder1.
Subfolder1.
Subfolder2.
Folder2.
The folder I have the scripts in, is Folder 2. But i need the path to Main Folder(1 back from Folder2). But the method i'm using, isn't quite reliable.
Currently i'm using os.getcwd(), but if I lunch a shell trought an Excel Macros, the path breaks. From time to time the code brakes(often in loops that use paths).
I need to lunch said scripts trought Excel or at least CMD. Because this will be for People who knows just enough about computers to make it every day. And it need to operate on everyone machines.
PS: The scripts works just fine, but they do need to be in a separate folder from the files they are working on.
UPDATE 1 AS REQUESTED:
I've made a class and this is the class
class mypath:
def Revpath(self):
CWD = os.getcwd()
Revpaths = CWD[:-14]
return Revpaths
def Saldos(self):
CWD = os.getcwd()
Revpaths = CWD[:-14]
Cuadraje = Revpaths+"Stock\\Saldos"
return Cuadraje
def Banco(self,IDkind):
CWD = os.getcwd()
Revpaths = CWD[:-14]
Stocks = Revpaths+"Stock\\kind\\"+IDkind
return Stocks
mp = mypath()
Then I just assign the returned value to a Var. One is used on a CSV writter(Wich happens to be the most common miss path). And the other to set a download folder, for a firefox profile. Each script uses the same Class, and the logic is 100% the same on every script. But i only can show you this much, because the code on itself is messy and have sensitive data in it.
UPDATE 2: SOLVED
Solved this by replacing os.getcwd() for os.path.realpath(__file__)
Because of language (First Lanaguage is Spanish), I've assumed that the Current Working Directory was the one with the script, instead it returned the PyCharm Settings folders(I'm still clueless about this, cus' i'm launching the scripts trough a Shell from a excel macross button).
Also i've updated the code on the class i've presented above, for stability in my applications.

Ask multiple directories dialog in Tkinter

I am trying to select multiple folders. I need the equivalent of askopenfilenames() for directories, but only askdirectory() exists, which only allows to select one folder.
Previously I found a custom script that did this for Matlab (uigetdir).
Any way of doing this in Python?
I need to batch process files in about 50 folders at a time, selecting them one by one is not realistic.
Also, I'm not a programmer, just trying to process my geophysical data, wouldn't be able to "code it myself" as I've seen suggested in other places.
Would have thought such a basic thing would be included in the basic functions.
Having had the same problem I developed my own solution. With this you can select one directory at a time then select cancel when you are finished.
The function the returns a list of the directories you have selected.
def fun_directory_selector(request_string: str, selected_directory_list: list, search_directory):
directory_path_string = filedialog.askdirectory(initialdir=search_directory, title=request_string)
if len(directory_path_string) > 0:
selected_directory_list.append(directory_path_string)
fun_directory_selector('Select the next Directory or Cancel to end',
selected_directory_list,
os.path.dirname(directory_path_string))
return selected_directory_list
The OP asked for a solution with Tkinter which isn't available but a solution is possible with wxPython-Phoenix
####### Retrieve a list of directories with wxPython-Phoenix - tested on python3.5
### installation instruction for wxPython-Phoenix : https://wiki.wxpython.org/How%20to%20install%20wxPython#Installing_wxPython-Phoenix_using_pip
### modified from : https://wxpython.org/Phoenix/docs/html/wx.lib.agw.multidirdialog.html
import os
import wx
import wx.lib.agw.multidirdialog as MDD
# Our normal wxApp-derived class, as usual
app = wx.App(0)
dlg = MDD.MultiDirDialog(None, title="Custom MultiDirDialog", defaultPath=os.getcwd(), # defaultPath="C:/Users/users/Desktop/",
agwStyle=MDD.DD_MULTIPLE|MDD.DD_DIR_MUST_EXIST)
if dlg.ShowModal() != wx.ID_OK:
print("You Cancelled The Dialog!")
dlg.Destroy()
paths = dlg.GetPaths()
#Print directories' path and files
for path in enumerate(paths):
print(path[1])
directory= path[1].replace('OS (C:)','C:')
print(directory)
for file in os.listdir(directory):
print(file)
dlg.Destroy()
app.MainLoop()

Accounting for a changing path

In relation to another question, how do you account for paths that may change? For example, if a program is calling a file in the same directory as the program, you can simply use the path ".\foo.py" in *nix. However, apparently Windows likes to have the path hard-coded, e.g. "C:\Python_project\foo.py".
What happens if the path changes? For example, the file may not be on the C: drive but on a thumb drive or external drive that can change the drive letter. The file may still be in the same directory as the program but it won't match the drive letter in the code.
I want the program to be cross-platform, but I expect I may have to use os.name or something to determine which path code block to use.
Simple answer: You work out the absolute path based on the environment.
What you really need is a few pointers. There are various bits of runtime and environment information that you can glean from various places in the standard library (and they certainly help me when I want to deploy an application on windows).
So, first some general things:
os.path - standard library module with lots of cross-platform path manipulation. Your best friend. "Follow the os.path" I once read in a book.
__file__ - The location of the current module.
sys.executable - The location of the running Python.
Now you can fairly much glean anything you want from these three sources. The functions from os.path will help you get around the tree:
os.path.join('path1', 'path2') - join path segments in a cross-platform way
os.path.expanduser('a_path') - find the path a_path in the user's home directory
os.path.abspath('a_path') - convert a relative path to an absolute path
os.path.dirname('a_path') - get the directory that a path is in
many many more...
So combining this, for example:
# script1.py
# Get the path to the script2.py in the same directory
import os
this_script_path = os.path.abspath(__file__)
this_dir_path = os.path.dirname(this_script_path)
script2_path = os.path.join(this_dir_path, 'script2.py')
print script2_path
And running it:
ali#work:~/tmp$ python script1.py
/home/ali/tmp/script2.py
Now for your specific case, it seems you are slightly confused between the concept of a "working directory" and the "directory that a script is in". These can be the same, but they can also be different. For example the "working directory" can be changed, and so functions that use it might be able to find what they are looking for sometimes but not others. subprocess.Popen is an example of this.
If you always pass paths absolutely, you will never get into working directory issues.
If your file is always in the same directory as your program then:
def _isInProductionMode():
""" returns True when running the exe,
False when running from a script, ie development mode.
"""
return (hasattr(sys, "frozen") or # new py2exe
hasattr(sys, "importers") # old py2exe
or imp.is_frozen("__main__")) #tools/freeze
def _getAppDir():
""" returns the directory name of the script or the directory
name of the exe
"""
if _isInProductionMode():
return os.path.dirname(sys.executable)
return os.path.dirname(__file__)
should work. Also, I've used py2exe for my own application, and haven't tested it with other exe conversion apps.
What -- specifically -- do you mean by "calling a file...foo.py"?
Import? If so, the path is totally outside of your program. Set the PYTHONPATH environment variable with . or c:\ or whatever at the shell level. You can, for example, write 2-line shell scripts to set an environment variable and run Python.
Windows
SET PYTHONPATH=C:\path\to\library
python myapp.py
Linux
export PYTHONPATH=./relative/path
python myapp.py
Execfile? Consider using import.
Read and Eval? Consider using import.
If the PYTHONPATH is too complicated, then put your module in the Python lib/site-packages directory, where it's put onto the PYTHONPATH by default for you.
I figured out by using os.getcwd(). I also learned about using os.path.join to automatically determine the correct path format based on the OS. Here's the code:
def openNewRecord(self, event): # wxGlade: CharSheet.<event_handler>
"""Create a new, blank record sheet."""
path = os.getcwd()
subprocess.Popen(os.path.join(path, "TW2K_char_rec_sheet.py"), shell=True).stdout
It appears to be working. Thanks for the ideas.

Categories