Skip to content

Commit

Permalink
create functions: save, folder, filename, _get_file_filters
Browse files Browse the repository at this point in the history
  • Loading branch information
jborbely committed Jul 24, 2017
1 parent 7416055 commit 56cccf2
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 8 deletions.
144 changes: 136 additions & 8 deletions msl/qt/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def question(message, default=True, title=None):


def double(message, default=0, minimum=-2147483647, maximum=2147483647, precision=1, title=None):
"""Get a floating-point value from the user.
"""Request a floating-point value from the user.
Parameters
----------
Expand All @@ -102,7 +102,7 @@ def double(message, default=0, minimum=-2147483647, maximum=2147483647, precisio
-------
:obj:`float` or :obj:`None`
The floating-point value or :obj:`None` if the user cancelled
the request to enter a number.
the request to enter a floating-point number.
"""
app, title = _get_app_and_title(title)
value, ok = QtWidgets.QInputDialog.getDouble(app.activeWindow(), title, message,
Expand All @@ -112,7 +112,7 @@ def double(message, default=0, minimum=-2147483647, maximum=2147483647, precisio


def integer(message, default=0, minimum=-2147483647, maximum=2147483647, step=1, title=None):
"""Get an integer value from the user.
"""Request an integer value from the user.
Parameters
----------
Expand Down Expand Up @@ -145,7 +145,7 @@ def integer(message, default=0, minimum=-2147483647, maximum=2147483647, step=1,


def item(message, items, index=0, title=None):
"""Select an item from a list of items.
"""Request an item from a list of items.
Parameters
----------
Expand All @@ -162,7 +162,8 @@ def item(message, items, index=0, title=None):
Returns
-------
:obj:`object`
The selected item or :obj:`None` if the user cancelled the request to select an item.
The selected item or :obj:`None` if the user cancelled the request to
select an item.
.. note::
The data type of the selected item is preserved. For example, if
Expand All @@ -180,7 +181,7 @@ def item(message, items, index=0, title=None):


def text(message, default='', multi_line=False, title=None):
"""Get text from the user.
"""Request text from the user.
Parameters
----------
Expand All @@ -197,8 +198,8 @@ def text(message, default='', multi_line=False, title=None):
Returns
-------
:obj:`str`
The text that the user entered or :obj:`None` if the user cancelled the request
to enter text.
The text that the user entered or :obj:`None` if the user cancelled the
request to enter text.
"""
app, title = _get_app_and_title(title)
if multi_line:
Expand All @@ -212,10 +213,137 @@ def text(message, default='', multi_line=False, title=None):
return value.strip() if ok else None


def save(initial=None, filters=None, title='Save As'):
"""Request the user to select the name of a file to save.
Parameters
----------
initial : :obj:`str`, optional
The initial directory to start in.
filters : :obj:`str` or :obj:`list` of :obj:`str` or :obj:`dict`
Only files that match the specified filters are shown.
Examples::
'Images (*.png *.xpm *.jpg)'
'Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)'
['Images (*.png *.xpm *.jpg)', 'Text files (*.txt)', 'XML files (*.xml)']
{'Images': ('*.png', '*.xpm', '*.jpg'), 'Text files': '*.txt'}
title : :obj:`str`, optional
The text to display in the title bar of the pop-up window.
Returns
-------
:obj:`str`
The name of the file to save or :obj:`None` if the user cancelled the
request to select a file name.
"""
app, title = _get_app_and_title(title)
filters = _get_file_filters(filters)
name, _ = QtWidgets.QFileDialog.getSaveFileName(app.activeWindow(), title, initial, filters)
return name if len(name) > 0 else None


def folder(initial=None, title='Select Folder'):
"""Request to select an existing folder or to create a new folder.
Parameters
----------
initial : :obj:`str`, optional
The initial directory to start in.
title : :obj:`str`, optional
The text to display in the title bar of the pop-up window.
Returns
-------
:obj:`str`
The name of the selected folder or :obj:`None` if the user cancelled
the request to select a folder.
"""
app, title = _get_app_and_title(title)
name = QtWidgets.QFileDialog.getExistingDirectory(app.activeWindow(), title, initial)
return name if len(name) > 0 else None


def filename(initial=None, filters=None, multiple=False, title='Select File'):
"""Request to select the file(s) to open.
Parameters
----------
initial : :obj:`str`, optional
The initial directory to start in.
filters : :obj:`str` or :obj:`list` of :obj:`str` or :obj:`dict`
Only files that match the specified filters are shown.
Examples::
'Images (*.png *.xpm *.jpg)'
'Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)'
['Images (*.png *.xpm *.jpg)', 'Text files (*.txt)', 'XML files (*.xml)']
{'Images': ('*.png', '*.xpm', '*.jpg'), 'Text files': '*.txt'}
multiple : :obj:`bool`
Whether multiple files can be selected.
title : :obj:`str`, optional
The text to display in the title bar of the pop-up window.
Returns
-------
:obj:`str` or :obj:`list` of :obj:`str`
The name(s) of the file(s) to open or :obj:`None` if the user cancelled
the request to select the file(s).
"""
app, title = _get_app_and_title(title)
filters = _get_file_filters(filters)
if multiple:
if title == 'Select File':
title += 's'
name, _ = QtWidgets.QFileDialog.getOpenFileNames(app.activeWindow(), title, initial, filters)
else:
name, _ = QtWidgets.QFileDialog.getOpenFileName(app.activeWindow(), title, initial, filters)
return name if len(name) > 0 else None


def _get_app_and_title(title):
"""Returns a tuple of the QApplication instance and the title bar text."""
app = application()
if title is None:
w = app.activeWindow()
title = 'MSL' if w is None else w.windowTitle()
return app, title


def _get_file_filters(filters):
"""Make the `filters` value be in the appropriate syntax."""
def _check_extn(ex):
"""Check the format of the file extension."""
if ex is None:
return ALL_FILES
if '*' in ex:
return ex
if ex.startswith('.'):
return '*' + ex
return '*.' + ex

ALL_FILES = 'All Files (*)'

if filters is None:
return ALL_FILES

if isinstance(filters, dict):
f = ''
for name, extn in filters.items():
if isinstance(extn, (list, tuple)):
f += '{} ({});;'.format(name, ' '.join(_check_extn(e) for e in extn))
else:
f += '{} ({});;'.format(name, _check_extn(extn))
return f[:-2]

if isinstance(filters, (list, tuple)):
return ';;'.join(f if f is not None else ALL_FILES for f in filters)

if filters.endswith(';;'):
return filters[:-2]

return filters
29 changes: 29 additions & 0 deletions tests/test_prompt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from msl.qt import prompt


def test_get_file_filters():
assert 'All Files (*)' == prompt._get_file_filters(None)
assert '' == prompt._get_file_filters('')
assert '' == prompt._get_file_filters([])
assert '' == prompt._get_file_filters({})

assert 'Text files (*.txt)' == prompt._get_file_filters('Text files (*.txt)')
assert 'Text files (*.txt)' == prompt._get_file_filters(['Text files (*.txt)'])
assert 'Text files (*.txt)' == prompt._get_file_filters({'Text files': '*.txt'})
assert 'Text files (*.txt)' == prompt._get_file_filters({'Text files': ('*.txt',)})
assert 'Text files (*.txt)' == prompt._get_file_filters({'Text files': 'txt'})
assert 'Text files (*.txt)' == prompt._get_file_filters({'Text files': '.txt'})
assert 'Text files (test*.txt)' == prompt._get_file_filters({'Text files': 'test*.txt'})

assert 'ABC files (abc*)' == prompt._get_file_filters('ABC files (abc*)')
assert 'ABC files (abc*)' == prompt._get_file_filters({'ABC files': 'abc*'})

assert 'Data files (*.txt *.xml *.csv my*.hdf5)' == prompt._get_file_filters({'Data files': ('txt', '.xml', '*.csv', 'my*.hdf5')})
assert 'Data files (*.txt ab*.xml *.csv my*.hdf5);;XML files (*.xml)' == prompt._get_file_filters({'Data files': ('txt', 'ab*.xml', '*.csv', 'my*.hdf5'), 'XML files': 'xml'})

assert 'Data files (m*.xml);;All files (*)' == prompt._get_file_filters({'Data files': 'm*.xml', 'All files': '*'})

assert 'Image files (*.png)' == prompt._get_file_filters('Image files (*.png);;')
assert 'Image files (*.png *.jpg);;Text files (*.txt)' == prompt._get_file_filters('Image files (*.png *.jpg);;Text files (*.txt)')
assert 'Image files (*.png *.jpg);;Text files (*.txt)' == prompt._get_file_filters(['Image files (*.png *.jpg)', 'Text files (*.txt)'])
assert 'Image files (*.png *.jpg);;Text files (*.txt);;All Files (*)' == prompt._get_file_filters(['Image files (*.png *.jpg)', 'Text files (*.txt)', None])

0 comments on commit 56cccf2

Please sign in to comment.