Ask multiple directories dialog in Tkinter - python

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()

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

os.makedirs() doesnt make the dir instantly, but only after the application closes

This method runs fine, but it doesn't create the dir. Yet when I close my application, only THEN will it create the dir.
text is a name, for example: (Jason)
def addpatient(self, text):
newpath = os.getcwd() + '/names/' + text + '/'
if not os.path.exists(newpath):
os.makedirs(newpath)
Is there something I am missing, or am I using it wrong?
os.makedirs creates the new directory/directories imediately. Meaning that, if you run
os.makedirs(newpath)
print('newpath created!')
when your reach the second line, the new path has already been created.
So... what I suspect is happpening is that you are running your code in some IDE and that you don't see the new directory being created in your project's directory tree or whatever your IDE has. This can simply bee because your IDE only updates the directory tree every few seconds or only after your program finishes running. Try running this:
import os
import time
def addpatient(text):
etc...
addpatient("examplepath")
time.sleep(30) # go check if examplepath has been created!!
Here, you have created examplepath and then your have "paused" your program for 30 seconds. During these 30 seconds, go check if your directrory has been created.
Another way of checking would be to run:
import os
import time
def addpatient(text):
etc...
addpatient("examplepath")
if os.path.exists("examplepath"):
print("Path exists!")
else:
print("Path does not exist!")
Let me know if I'm completely wrong and your path is actually not being created!

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.

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.

traverse upward on a directory structure until it gets to the file system top level

I have already created a script which sets "FLAG" after backup on uppermost level of each directory.
now I need a script that could be run "in any directory" and that would tell me "when the last successful backup ran" by reading the last modification time of the "FLAG".
Of course I can also manually find out on what file system my current directory is located and then go up in the directory hierarchy until I get to the file system's top level directory and find the flag file there but that is not very convenient. The command that I would like to write would do the same thing, but automatically.
1) I do not know how to do the part where it traverses upward on a directory structure until it gets to the file system top level.
2) reading the Flag time might work sth like this:
import time
fileTileInSeconds = os.path.getmtime("FLAG") (not sure)
ModDate = time.localtime(fileTileInSeconds)
print ModDate.tm_year,ModDate.tm_mon,ModDate.tm_mday,ModDate.tm_hour,ModDate.tm_min,ModDate.tm_sec
quit()
Any idea/suggestion would be appreciated.
Use os.path.parent to go up the directory tree, and use os.stat to see which device each directory is on. When the device changes, you have crossed the boundary of the filesystem. Like this:
#!/usr/bin/python
import os
import stat
import sys
def find_mount(path):
path = os.path.realpath(path)
s = os.stat(path)[stat.ST_DEV]
ps = s
while path != '/' and ps == s:
parent = os.path.dirname(path)
ps = os.stat(parent)[stat.ST_DEV]
if ps == s:
path = parent
return path
if __name__ == "__main__":
for path in sys.argv[1:]:
print find_mount(path)

Categories