I've got a problem with saving file with extension (get path to file and append extension) in PyQt4 with QFileDialog. My Python code looks like that:
dialog = QtGui.QFileDialog()
dialog.setDefaultSuffix(".json")
file = dialog.getSaveFileName(None, "Title", "", "JSON (.json)")
It works, path is correct, dialog title and filter are in dialog window, but second line was ignored. File doesn't have any extension.
How to add extension by default? What am I doing wrong?
Calling setDefaultSuffix on an instance of QFileDialog has no effect when you use the static functions. Those functions will create their own internal file-dialog, and so the only options that can be set on it are whatever is made available via the arguments.
Of course, setDefaultSuffix will work if the instance of QFileDialog is shown directly:
dialog = QtGui.QFileDialog()
dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden)
dialog.setDefaultSuffix('json')
dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
dialog.setNameFilters(['JSON (*.json)'])
if dialog.exec_() == QtGui.QDialog.Accepted:
print(dialog.selectedFiles())
else:
print('Cancelled')
But note that you cannot get a native file-dialog using this method.
If the file-name filters are specified correctly (see above, and
Barmak Shemirani's answer), the native file-dialog may provide a means of automatically selecting the filename extension (this is certainly the case with KDE on Linux, but I don't know about other platforms).
Try with *.json instead of .json
file = dialog.getSaveFileName(None, "Title", "", "JSON (*.json)");
Related
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..
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.
We are looking at deploying a PyQt application on an Azure server, and the application works well enough, albeit a little slow to respond to user actions.
We have a problem, however, and that is that the QFileDialog allows pretty much any explore action: copy a file from the virtual machine to the user's local drive, open a file within 'Program Files (x86)' in Notepad, etc.
Approaches already considered:
As the python application has to have read and write permissions to
run under 'Program Files (x86)', we can't use file permissions to
control access.
We can turn the Python into an inscrutable .exe, but this could
still be copied using the context menus in the file dialog.
We could use the file filters and then hide them, so you can only
see (and mess with) the relevant files, but the user could still
copy entire directories.
The only thing we can think of is to create our own file dialog from scratch, but that's very tedious. Are there any 'out of the box' solutions?
The QFileDialog class already has this functionality:
dialog = QtGui.QFileDialog()
dialog.setOption(QtGui.QFileDialog.ReadOnly, True)
dialog.exec_()
This only seems to work with Qt's built-in file-dialog, though. If you use the static functions to open a native file-dialog, the ReadOnly option seems to be ignored (I've only tested this on Linux, though).
looking at exemple of qtreeview they show a file explorer so i think it's actually not a big task to implement a simple file system explorer. it's specialy easy thanks to QFileSystemModel http://doc.qt.io/qt-5/model-view-programming.html#using-models-and-views
Here's what I actually did, based on #ekhumoro's advice:
from PyQt4 import QtGui
import guidata
import re
class _DirectoryFilterProxyModel(QtGui.QSortFilterProxyModel):
""" A basic filter to be applied to the file items to be displayed.
Based on C++ example at:
https://stackoverflow.com/questions/2101100/qfiledialog-filtering-folders. """
def __init__(self, ignore_directories=[], *args, **kw):
""" Constructor
:param ignore_directories: A list of directories to exclude. These
can be regular expressions or straight names. """
QtGui.QSortFilterProxyModel.__init__(self, *args, **kw)
self.ignore_directories = ignore_directories
def filterAcceptsRow(self, sourceRow, sourceParent):
fileModel = self.sourceModel()
index0 = fileModel.index(sourceRow, 0, sourceParent)
if fileModel:
if fileModel.isDir(index0):
for directory in self.ignore_directories:
if re.match(directory, fileModel.fileName(index0)):
return False
return True
else: # For files
return True
else:
return False
And instantiated:
app = guidata.qapplication()
dialog = QtGui.QFileDialog()
proxyModel = _DirectoryFilterProxyModel(ignore_directories=["Program Files", "Program Files (x86)", "Windows"])
dialog.setProxyModel(proxyModel)
dialog.setOption(QtGui.QFileDialog.ReadOnly, True)
dialog.setOption(QtGui.QFileDialog.HideNameFilterDetails, True)
dialog.exec_()
My thanks to #serge_gubenko and #Gayan on the page qfiledialog - Filtering Folders? for providing the C++ implementation, from which I derived the above.
I've built a (Linux) GUI application that can be launched from a terminal and accepts an undefined number of files as arguments. The app reads sys.argv and lists the name of these files in a QListWidget.
The code is something like:
import sys
from PyQt4.QtGui import QApplication, QMainWindow, QCoreApplication
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# parse command line arguments
for i in QCoreApplication.argv()[1:]:
...
def main():
app = QApplication(sys.argv)
...
What I want to do is to be able to select multiple files from a file manager and open them with my app through the "Open with..." option provided by file managers. How this can be achieved?
With the current code, when I try it only one of the selected files is shown on the QListWidget.
Edit:
It finally seems that it depends to the file manager.
I tried with a few file managers and...
pcmanfm: It only opens one of the selected files.
spacefm: Works properly!
dolphin: It opens each file to a different instance of my program. If
I select 3 files it will open my app 3 times, one for each file.
nautilus: I didn't manage to open any files with it. My program is not listed in the suggested applications and I didn't find any way to do it.
There's not really enough information to give a definite answer, but...
First, have you checked that a print sys.argv at the top of the code looks like you were expecting?
If so, does it work if you change the line...
for i in QCoreApplication.argv()[1:]:
...to...
for i in sys.argv[1:]:
For debugging purposes, you might also like to include the line...
assert QCoreApplication.argv()[1:] == sys.argv[1:]
...just before you start the for-loop.
Use a QFileDialog: Documentation
I'm programming an excel-add on and I don't need main windows and all the shiny widgets/toolkits, but I need dialogs.
here's my attempt at creating one(using pywin32):
import win32ui, win32con
def openFileDialog(extensions = None, multi = False):
"""open a Windows File 'Open' Dialog. returns a list of path of files selected by user.
Keyword arguments:
extensions (default None) - a list or tuple containing (description, ext)
pair of acceptable files.
ext can be a string - i.e) '*.txt', or a list or tuple of many strings ['*.txt', '*.htm']
description is a string describing corresponding ext.
multi - (default False) True to allow user to select more than one files.
"""
openFlags = win32con.OFN_OVERWRITEPROMPT|win32con.OFN_FILEMUSTEXIST
if multi:
openFlags|=win32con.OFN_ALLOWMULTISELECT
dlg = win32ui.CreateFileDialog(1,
None,
None,
openFlags,
extensionFilterString(extensions))
if dlg.DoModal()!=win32con.IDOK:
return None
return dlg.GetPathNames()
although they work, I think that is really un-pythonic. I had a look at pyside and they have QFileDialog - but it requires QApplication and seems like that Qapplication takes over the main loop. I don't think I need all the hassle and I really don't know about python gui programming. What is the most convenient way to open file dialogs in python?