Source code for schrodinger.ui.qt.filedialog

"""
Schrodinger version of the QFileDialog class of the QtGui module.

Defines a FileDialog class that mimics the Maestro's file dialogs.

Copyright Schrodinger, LLC. All rights reserved.
"""

# Contributors: Pat Lorton, Matvey Adzhigirey

import os
import os.path
import sys
from collections import OrderedDict
from past.utils import old_div

import schrodinger.ui.qt.icons as icons  # noqa: F401
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.ui import maestro_ui
from schrodinger.ui.maestro_ui import MM_QProjS
from schrodinger.ui.qt import utils as qt_utils
from schrodinger.utils import fileutils


[docs]def use_native_file_dialog(): return maestro_ui.maestro_native_file_dialog_is_enabled()
[docs]def filter_string_from_formats(formats=[fileutils.MAESTRO]): # noqa: M511 """ Create a Qt file dialog filter string from a list of structure formats. :param formats: List of formats, see schrodinger.utils.fileutils module for available format string constants. :type formats: list of str :return: Filter string :rtype: str """ _FORMAT_NAMES = { fileutils.PDB: "PDB files", fileutils.MOL2: "Mol2 files", fileutils.SD: "SD files", fileutils.MAESTRO: "Maestro files", fileutils.MAESTRO_STRICT: "Maestro files", fileutils.SMILES: "SMILES files", fileutils.SMILESCSV: "SMILES CSV files", fileutils.CMS: "Desmond CMS files", fileutils.PFX: "PathFinder reactant files" } filter_items = [] all_supported = [] for fmt in formats: try: name = _FORMAT_NAMES[fmt] exts = fileutils.EXTENSIONS[fmt] except KeyError: raise ValueError("Unsupported format: %s" % fmt) # PANEL-19253: redefine PDB format extensions to also include assembly PDB extensions if fmt == fileutils.PDB: exts = ['.pdb*', '.ent*'] exts = ['*' + ext for ext in exts] type_str = name + " (%s)" % " ".join(exts) filter_items.append(type_str) all_supported.extend(exts) if len(filter_items) > 1: type_str = "All supported files (%s)" % " ".join(all_supported) filter_items.insert(0, type_str) return ';;'.join(filter_items)
# Common filters: MAESTRO_FILTER = filter_string_from_formats([fileutils.MAESTRO]) SD_FILTER = filter_string_from_formats([fileutils.SD]) PDB_FILTER = filter_string_from_formats([fileutils.PDB]) MAESTRO_SD_FILTER = filter_string_from_formats( [fileutils.MAESTRO, fileutils.SD]) MAESTRO_PDB_FILTER = filter_string_from_formats( [fileutils.MAESTRO, fileutils.PDB])
[docs]def filter_string_from_extensions(extensions, add_all=False): """ Create a filter string for the given extensions :param dict extensions: Keys are descriptive file types ('Image file'), values are an iterable of associated extension(s). If there is only one extension it can be given as a simple string rather than an iterable of length 1 (['.png', '.jpg'] or just '-2d.png'). :param bool add_all: Whether to add an additional "All files" filter :rtype: str :return: A string formatted for the file dialog filter keyword """ strings = [] for name, exts in extensions.items(): if isinstance(exts, str): exts = [exts] exstr = ' '.join('*' + x for x in exts) strings.append(f'{name} ({exstr})') if add_all: strings.append('All files (*)') return ';;'.join(strings)
[docs]class CustomSideBarMixin(object): def _setup_sidebar(self, sidebar_links=None): """ Used to set up the sidebar directories in file browsers. Note that this method sets up a signal/slot for the ListView model that holds the data for the sidebar, and this model is altered by calls to setIconProvider(), so this method should be called after any calls to that method. :type sidebar_links: dict :param sidebar_links: dictionary of additional sidebar links. Keys are names of the links and values are paths """ # Needed for customization to work, on Qt5: self.setOption(QtWidgets.QFileDialog.DontUseNativeDialog, True) # These are our "Fav 4" directories - add them to every dialog HOME = 'Home' DESKTOP = 'Desktop' DOCUMENTS = 'Documents' WORKING = 'Working Directory' # Load up a dictionary with our favorite 4 first then any requested links dirs = OrderedDict() services = QtGui.QDesktopServices() dirs[HOME] = _get_standard_loc("HomeLocation") dirs[DESKTOP] = _get_standard_loc("DesktopLocation") dirs[DOCUMENTS] = _get_standard_loc("DocumentsLocation") dirs[WORKING] = os.getcwd() if sidebar_links: dirs.update(sidebar_links) # Convert the dirs dictionary to the types needed by PyQt. We need to avoid # adding directories that don't exist, and we need to avoid adding the same # directory twice. Both cases cause problems with QT. EV:130022, # EV:135513. self._links = OrderedDict() for name, path in dirs.items(): if os.path.exists(path): url = QtCore.QUrl.fromLocalFile(path) if url not in list(self._links.values()): self._links[name] = url url_list = list(self._links.values()) self.setSidebarUrls(url_list) # We want to give links their proper names, so we need to # grab the model that displays them so we can edit the name sidebar = self.findChild(QtWidgets.QListView, "sidebar") self._smodel = sidebar.model() # Update the original side-bar: self._updateItemNames() # Update the side-bar item names every time it's modified by the user: self._smodel.dataChanged.connect(self._updateItemNames) def _updateItemNames(self): """ Update the names of the side-bar items. e.g. user's home directory will be named "Home" instead of "$USERNAME"; and the CWD will be named "Working Directory" instead of the basename of the current path. """ sidebar_urls = self.sidebarUrls() for name, url in self._links.items(): try: index = sidebar_urls.index(url) except ValueError: # This item was removed by the user or Qt (during cleanup) continue self._smodel.setData(self._smodel.index(index, 0), name)
def _get_standard_loc(standard_loc): """ Get the specified location in a way that works on Qt4 and Qt5. Once we've fully switched to Qt5, this code can be inlined and this function can be removed. """ standard_loc = getattr(QtCore.QStandardPaths, standard_loc) return QtCore.QStandardPaths.writableLocation(standard_loc)
[docs]def get_existing_directory(parent="", caption='Choose Directory', dir=None, accept_label='Choose', file_mode=QtWidgets.QFileDialog.Directory, *, show_dirs_only=True, **kwargs): """ Convenience function that returns a directory name as selected by the user The base class `base_file_dialog` documents the keyword arguments for this class. :type file_mode: QFileDialog.FileMode :param file_mode: What the user can select. See the PyQt documentation for QFileDialog.FileMode (currently AnyFile, ExistingFile, Directory and ExistingFiles are the options). :type show_dirs_only: bool :param show_dirs_only: Whether we should show only directories in the file dialog. If False, both files and directories will be shown. :rtype: str or None :return: full pathname of the file selected by the user, or None if Cancel was pressed """ files = base_file_dialog(parent=parent, caption=caption, dir=dir, accept_label=accept_label, file_mode=file_mode, show_dirs_only=show_dirs_only, **kwargs) try: return files[0] except (IndexError, TypeError): return files
def _add_extension_if_necessary(files, filter): """ Attach the first extension in filter to each filename in files that does not already have an extension :type files: list :param files: list of filenames :type filter: str :param filter: the list of filters that can be applied. The first extension is used. The format is ``"Filetype1 (*.ex1);;Filetype2 (*.ex2 *.ex3)"`` :rtype: list :return: list of filenames in the same order as files. Each item in the returned list is unchanged if it had an extension, or has an extension added to it if it did not. The added extension is the first one in the selected filter list of dialog. Examples filters:: "Image files (*.png *.jpg *.bmp)" "Images (*.png *.xpm *.jpg);;Text files (*.txt);;All files (*)" """ # Extract the first filter extension try: chunk = filter.split('(')[1] except (IndexError, AttributeError): # No extension listed in filter return chunk = chunk.split(')')[0] chunk = chunk.split()[0] extension = chunk.replace('*', "") new_filenames = [] for afile in files: if not os.path.splitext(afile)[1]: afile = afile + extension new_filenames.append(afile) return new_filenames
[docs]def get_save_file_name(parent="", caption='Save File', dir=None, filter='All Files (*)', accept_label='Save', accept_mode=QtWidgets.QFileDialog.AcceptSave, file_mode=QtWidgets.QFileDialog.AnyFile, **kwargs): """ Convenience function that returns a filename as selected by the user The base class `base_file_dialog` documents the keyword arguments for this class. :rtype: str or None :return: full pathname of the file selected by the user, or None if Cancel was pressed """ files = base_file_dialog(parent=parent, caption=caption, dir=dir, filter=filter, accept_label=accept_label, accept_mode=accept_mode, file_mode=file_mode, **kwargs) #Ignore adding extension to files if the cancel button was used if files: files = _add_extension_if_necessary(files, _last_selected_filter) try: return files[0] except (IndexError, TypeError): return files
[docs]def get_open_file_names(parent="", caption='Open Files', dir=None, filter='All Files (*)', accept_label='Open', file_mode=QtWidgets.QFileDialog.ExistingFiles, **kwargs): """ Convenience function that returns a list of filenames as selected by the user The base class `base_file_dialog` documents the keyword arguments for this class. :rtype: list of str or None :return: list of full file pathnames selected by the user, or None if Cancel was pressed. """ files = base_file_dialog(parent=parent, caption=caption, dir=dir, filter=filter, accept_label=accept_label, file_mode=file_mode, **kwargs) return files
[docs]def get_open_file_name(parent="", caption='Open File', dir=None, filter='All Files (*)', **kwargs): """ Convenience function that returns a single filename as selected by the user The base class `base_file_dialog` documents the keyword arguments for this class. :rtype: str or None :return: full pathname of the file selected by the user, or None if Cancel was pressed """ files = base_file_dialog(parent=parent, caption=caption, dir=dir, filter=filter, **kwargs) try: return files[0] except (IndexError, TypeError): return files
[docs]def get_open_wm_file_name(parent="", dir=None, **kwargs): """ Convenience function that returns a single WaterMap file as selected by the user. See `base_file_dialog` for documentation. :return: Full pathname of the WaterMap file selected by the user or None if cancel was pressed. :rtype: str or None """ caption = "Please select a WaterMap file." exts = ["*_wm.mae", "*_wm.maegz", "*_wm.mae.gz"] filter = "WaterMap files ({0})".format(" ".join(exts)) return get_open_file_name(parent, caption=caption, dir=dir, filter=filter, **kwargs)
_last_selected_filter = None """The last filter chosen in a file dialog""" _last_selected_directory = {} """ Dictionary - keys are browser ID's and values are the last directory for that id """ _history_by_id = {} """ Dictionary - keys are browser ID's and values are the dialog's history """
[docs]def get_last_selected_directory(idval): """ Return the last directory selected by a user in a dialog with the given id value. If there is no entry for the given id value, None is returned. :type idval: str, int or float :param idval: The value passed to a filedialog using the id keyword argument :rtype: str or None :return: The last directory opened by a dialog with the given id value, or None if no entry exists for the id value. """ return _last_selected_directory.get(idval)
[docs]def base_file_dialog(parent="", caption='Open File', dir=None, filter='All Files (*)', selectedFilter=None, options=None, default_suffix=None, default_filename=None, accept_label='Open', accept_mode=QtWidgets.QFileDialog.AcceptOpen, file_mode=QtWidgets.QFileDialog.ExistingFile, confirm=True, custom_sidebar=True, sidebar_links=None, id=None, *, show_dirs_only=False): """ Convenience function that creates a highly customizable file dialog :type parent: qwidget :param parent: the widget over which this dialog should be shown. :type caption: str :param caption: the name that appears in the titlebar of this dialog. :type dir: str :param dir: the initial directory displayed in this dialog. If id keyword is also supplied, subsequent calls will open in the last opened directory, which can be different from dir. :type filter: str :param filter: the list of filters that can be applied to this directory. the format is ``"Filetype1 (*.ex1);;Filetype2 (*.ex2 *.ex3)"``. :type selectedFilter: str :param selectedFilter: the filter used by default. if not specified, the first filter in the filters string is used. :type options: qfiledialog.option enum :param options: see the qfiledialog.option and qfiledialog.setoptions documentation :type default_suffix: str :param default_suffix: the suffix applied to a filename if the user does supply one. the suffix will have a leading '.' appended to it. :type default_filename: str :param default_filename: A default base filename to use to save files in save dialogs. By default, the filename field of the dialog is blank. :type accept_label: str :param accept_label: the text on the 'accept' button :type accept_mode: qfiledialog.acceptmode :param accept_mode: whether the dialog is in open or save mode. see the pyqt documentation for qfiledialog.acceptmode (currently acceptopen and acceptsave are the two options) :type file_mode: qfiledialog.filemode :param file_mode: what the user can select. see the pyqt documentation for qfiledialog.filemode (currently anyfile, existingfile, directory and existingfiles are the options) :type confirm: bool :param confirm: true if a confirmation dialog should be used if the user selects an existing file, false if not :type custom_sidebar: bool :param custom_sidebar: True if the Schrodinger sidebar should be used, False if the default PyQt sidebar should be used. :type sidebar_links: dict :param sidebar_links: Used to create extra links in the left-hand sidebar of the dialog. The keys of the dictionary are a unique identifier for each link (note that 'home' and 'working' are already used), and the values are tuples of the form (path, name) where path and name are str, path indicates the path the sidebar link points to, and name is the name displayed for the link. :type id: str, int or float :param id: The identifier used for this dialog. Dialogs with the same identifier will remember the last directory chosen by the user with any dialog of the same id and open in that directory. The dir keyword parameter can be used to override the initial directory the dialog opens in, but the chosen directory will still be stored for the next time a dialog with the same identifier opens. The default (no id given) is to not remember the chosen directory. Additionally, the id is used to keep track of recent places for the given file dialog. :type show_dirs_only: bool :param show_dirs_only: Whether we should show only directories in the file dialog. If False, both files and directories will be shown. :rtype: list or None :return: list of full file pathnames selected by the user, or none if cancel was pressed. Note that all pathnames are strings, and have been converted to platform-consistent pathnames via os.path.normpath. """ global _last_selected_filter mydir = '.' if dir is not None: mydir = dir if id is not None: mydir = _last_selected_directory.get(id, mydir) dialog = FileDialog(parent=parent, caption=caption, directory=mydir, filter=filter, custom_sidebar=custom_sidebar, sidebar_links=sidebar_links) if default_filename: dialog.selectFile(default_filename) dialog.setAcceptMode(accept_mode) dialog.setFileMode(file_mode) if show_dirs_only: dialog.setOption(QtWidgets.QFileDialog.ShowDirsOnly, True) # Apply the user options if not parent or not caption: # The QFileDialog class does not apply the other parameters if parent or # caption is not set. if not parent and caption: dialog.setWindowTitle(caption) dialog.setDirectory(os.path.abspath(mydir)) dialog.setNameFilter(filter) if selectedFilter: dialog.selectNameFilter(selectedFilter) if options is not None: dialog.setOptions(options) if default_suffix: dialog.setDefaultSuffix(default_suffix) if not confirm: dialog.setOption(QFileDialog.DontConfirmOverwrite) dialog.setLabelText(QFileDialog.Accept, accept_label) if id and _history_by_id.get(id): dialog.setHistory(_history_by_id.get(id)) ok = dialog.exec() if ok: # If not cancel _last_selected_filter = str(dialog.selectedNameFilter()) files = [os.path.normpath(str(x)) for x in dialog.selectedFiles()] if files and id is not None: _last_selected_directory[id] = \ str(dialog.directory().absolutePath()) _history_by_id[id] = dialog.history() return files return None
[docs]def fix_splitter(dialog): """ Alters the splitter between the file pane and the directory pane so that both sides are visible. Because Qt saves the state of the dialog, if the users moves the splitter all the way to one side or the other, all future dialogs will show up that way, and it can be very confusing if the file side isn't shown. :type dialog: `QtWidgets.QFileDialog` :param dialog: The dialog to check & fix if necessary """ try: splitter = dialog.findChildren(QtWidgets.QSplitter)[0] except IndexError: # There should be one splitter in the Dialog - this must be a custom # Dialog return sizes = splitter.sizes() try: fileside = sizes[1] dirside = sizes[0] except IndexError: # Not the splitter we wanted - this must be a custom Dialog return if not fileside: sizes = [old_div(dirside, 2), old_div(dirside, 2)] splitter.setSizes(sizes) elif not dirside: sizes = [old_div(fileside, 2), old_div(fileside, 2)] splitter.setSizes(sizes) # Do not let file section of dialog collapse (python-1960) splitter.setCollapsible(1, False)
[docs]class FileDialog(QtWidgets.QFileDialog, CustomSideBarMixin): """ File browser dialog with custom sidebar. This class name was changed from QFileDialog to FileDialog because PyQt on the Mac OS uses Mac native dialogs instead of the class object if the class name is QFileDialog. """ _pytest_abort_hook = lambda self: None # To prevent showing during tests getExistingDirectory = staticmethod(get_existing_directory) getSaveFileName = staticmethod(get_save_file_name) getOpenFileNames = staticmethod(get_open_file_names) getOpenFileName = staticmethod(get_open_file_name)
[docs] def __init__(self, parent=None, caption="", directory="", filter='All Files (*)', custom_sidebar=True, sidebar_links=None): """ :type parent: qwidget :param parent: the widget over which this dialog should be shown. If not given, the Dialog will be placed by PyQt. :type caption: str :param caption: the name that appears in the titlebar of this dialog. If not given the title will be the default PyQt caption. :type directory: str :param directory: the initial directory displayed in this dialog, default is the current directory. :type filter: str :param filter: the list of filters that can be applied to this directory the format is ``"Filetype1 (*.ex1);;Filetype2 (*.ex2 *.ex3)"``. Default is all files. :type custom_sidebar: bool :param custom_sidebar: True if the Schrodinger sidebar should be used, False if the default PyQt sidebar should be used. :type sidebar_links: dict :param sidebar_links: Use to create extra links in the left-hand sidebar of the dialog. the keys of the dictionary are a unique identifier for each link (note that 'home' and 'working' are already used), and the values are tuples of the form (path, name) where path and name are str, path indicates the path the sidebar link points to, and name is the name displayed for the link. """ # Ev:98084 directory must be absolute path: directory = os.path.abspath(directory) if not parent: QtWidgets.QFileDialog.__init__(self) elif not caption: QtWidgets.QFileDialog.__init__(self, parent) else: QtWidgets.QFileDialog.__init__(self, parent, caption, directory, filter) if custom_sidebar and (not use_native_file_dialog()): self._setup_sidebar(sidebar_links=sidebar_links) # Make sure both sides of the file-chooser splitter are visible fix_splitter(self) if use_native_file_dialog(): self.setOption(QtWidgets.QFileDialog.DontUseNativeDialog, False)
[docs] def exec(self): self._pytest_abort_hook() with qt_utils.remove_wait_cursor: return super().exec()
# This allows legacy scripts to still access the old class name QFileDialog QFileDialog = FileDialog
[docs]class CustomFileDialog(FileDialog): """ A File Dialog that has all contents below the file view (File name and File type fields) shifted down a row so that custom controls can be placed. """
[docs] def __init__(self, *args, num_free_rows=1, **kwargs): super().__init__(*args, **kwargs) dlg_layout = self.layout() assert num_free_rows >= 0 fileNameLabel = self.findChild(QtWidgets.QLabel, "fileNameLabel") dlg_layout.addWidget(fileNameLabel, 2 + num_free_rows, 0) self.file_name_edit = self.findChild(QtWidgets.QLineEdit, "fileNameEdit") dlg_layout.addWidget(self.file_name_edit, 2 + num_free_rows, 1) buttonBox = self.findChild(QtWidgets.QDialogButtonBox, "buttonBox") dlg_layout.addWidget(buttonBox, 2 + num_free_rows, 2, 2, 1) fileTypeLabel = self.findChild(QtWidgets.QLabel, "fileTypeLabel") dlg_layout.addWidget(fileTypeLabel, 3 + num_free_rows, 0) self.file_type_combo = self.findChild(QtWidgets.QComboBox, "fileTypeCombo") dlg_layout.addWidget(self.file_type_combo, 3 + num_free_rows, 1)
################################################ ## Below code is specific to Maestro projects ## ################################################
[docs]class OpenDirAsFileDialog(MM_QProjS): """ A file dialog tailored to allow the user to "open" directories such as projects or phase databases as if they were files. Usage:: dlg = OpenDirAsFileDialog() project_path = dlg.getFilename() if project_path: # User accepted the dialog with a directory selection. """
[docs] def __init__(self, parent=None, caption='Open Project', directory='.', accept='Open', filter=MM_QProjS.MM_QPROJS_MAESTRO, label='Project:'): """ :param QWidget parent: Dialog parent. :param str caption: Dialog :param str directory: Directory to open in the dialog. :param str accept: Text for dialog accept button. :type filter: Directory filter (MM_QPROJS_MAESTRO, MM_QPROJS_CANVAS, MM_QPROJS_PHDB). :param filter: MM_QProjS.MMENUM_QPROJS_APP :param str label: Text for dialog file name label. """ cloud_warning = not use_native_file_dialog() super().__init__(parent, filter, use_native_file_dialog(), cloud_warning) self.setWindowTitle(caption) self.setAcceptOpen() self.setLabelText(self.Accept, accept) self.setLabelText(self.FileName, label) self.setDir(os.path.abspath(directory))
[docs] def getFilename(self): """ Open the dialog, allow the user to choose the directory and return the path. :return: Path to directory if dialog accepted else None. :rtype: str or NoneType. """ with qt_utils.remove_wait_cursor: if self.exec(): # FIXME MAE-45189: On Windows - `getSelectedFullPath` is # returning path with both forward and backslash separators. return self.getSelectedFullPath().replace('\\', '/') else: return None
[docs]class ProjectOpenDialog(OpenDirAsFileDialog): """ A file dialog tailored to opening Projects. """
[docs] def __init__(self, parent=None, caption='Open Project', directory='.', accept='Open', filter=MM_QProjS.MM_QPROJS_MAESTRO, label='Project:'): # See OpenDirAsFileDialog.__init__ for documentation. super().__init__(parent=parent, caption=caption, directory=directory, accept=accept, filter=filter, label=label)
[docs]def get_existing_project_name(*args, **kwargs): """ Convenience function to open a Open Project dialog and return the path the user selects. Parameters are passed to and documented in the `ProjectOpenDialog` class. :type id: str, int or float :param id: The identifier used for this dialog. Dialogs with the same identifier will remember the last directory chosen by the user with any dialog of the same id and open in that directory. The dir keyword parameter can be used to override the initial directory the dialog opens in, but the chosen directory will still be stored for the next time a dialog with the same identifier opens. The default (no id given) is to not remember the chosen directory. :rtype: str or None :return: The path to the project if the user selects one, or None if the user cancels the dialog """ # Restore the correct starting directory if requested id = kwargs.pop('id', None) if id is not None: mydir = _last_selected_directory.get(id, '.') kwargs['directory'] = mydir mydialog = ProjectOpenDialog(*args, **kwargs) afile = mydialog.getFilename() # Save the ending directory if requested if afile and id is not None: _last_selected_directory[id] = str(mydialog.directory().absolutePath()) return afile
############################################### ## Below code is specific to Phase databases ## ###############################################
[docs]class PhaseDatabaseOpenDialog(OpenDirAsFileDialog): """ A file dialog for opening phase databases. """
[docs] def __init__(self, parent=None, caption='Open Phase database', directory='.', accept='Open', filter=MM_QProjS.MM_QPROJS_PHDB, label='Project:'): # See OpenDirAsFileDialog.__init__ for documentation. super().__init__(parent=parent, caption=caption, directory=directory, accept=accept, filter=filter, label=label)
[docs]def get_existing_phase_database(*args, **kwargs): """ Convenience function to open an Open Project dialog and return the path the user selects. All parameters are passed to and documented in the PhaseDatabaseOpenDialog class. :type id: str, int or float :param id: The identifier used for this dialog. Dialogs with the same identifier will remember the last directory chosen by the user with any dialog of the same id and open in that directory. The dir keyword parameter can be used to override the initial directory the dialog opens in, but the chosen directory will still be stored for the next time a dialog with the same identifier opens. The default (no id given) is to not remember the chosen directory. :rtype: str or None :return: The path to the project if the user selects one, or None if the user cancels the dialog """ # Restore the correct starting directory if requested id = kwargs.pop('id', None) if id is not None: mydir = _last_selected_directory.get(id, '.') kwargs['directory'] = mydir mydialog = PhaseDatabaseOpenDialog(*args, **kwargs) afile = mydialog.getFilename() # Save the ending directory if requested if afile and id is not None: _last_selected_directory[id] = str(mydialog.directory().absolutePath()) return afile
# For testing purposes: if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) mydialog = PhaseDatabaseOpenDialog() print(mydialog.getFilename()) #EOF