First public release

pull/8/head
Thomas Bouve 2019-04-11 16:18:17 +02:00
commit 990e2cb627
12 zmienionych plików z 521 dodań i 0 usunięć

9
.gitignore vendored 100755
Wyświetl plik

@ -0,0 +1,9 @@
#Ignore vscode individual settings
.vscode
#Ignore egg-info files and folders
*.egg-info
*.pyc
#Ignore pyenv version files
.python-version

21
LICENSE 100644
Wyświetl plik

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Thomas Bouve
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

63
README.md 100644
Wyświetl plik

@ -0,0 +1,63 @@
# ipyfilechooser
A simple Python file chooser widget for use in Jupyter/IPython in conjunction with ipywidgets. The selected path and file are available via `.selected_path` and `.selected_filename` respectvely or as a single combined filepath via `selected`. The dialog can be reset to its default path and filename by using `.reset()`.
If a filename is typed in the filename text field that matches a file entry in the current folder the entry will be highlighted. To highlight the risk of overwriting existing files, the selected filepath will be green if the file does not exist and orange if it does.
## Usage
```
from ipyfilechooser import FileChooser
# Create and display a FileChooser widget
fc = FileChooser('/Users/crahan/Projects/Jupyter/ipywidgets')
display(fc)
# Print the selected path, filename, or both
print(fc.selected_path)
print(fc.selected_filename)
print(fc.selected)
# Change defaults and reset
fc.default_path = '/Users/crahan/Projects'
fc.default_filename = 'output.txt'
fc.reset()
# Shorthand reset
fc.reset(path='/Users/crahan/Projects', filename='output.txt')
```
## Functions and variables
```
fc.reset()
fc.default
fc.default_path
fc.default_filepath
fc.selected
fc.selected_path
fc.selected_filename
```
## Screenshots
![Screenshot 1](screenshots/FileChooser_screenshot_1.png)
*Fig. 1: FileChooser default view*
![Screenshot 2](screenshots/FileChooser_screenshot_2.png)
*Fig. 2: FileChooser open view*
![Screenshot 3](screenshots/FileChooser_screenshot_3.png)
*Fig. 3: Entering a filename for an existing file*
![Screenshot 4](screenshots/FileChooser_screenshot_4.png)
*Fig. 4: Existing file selected*
![Screenshot 5](screenshots/FileChooser_screenshot_5.png)
*Fig. 4: Entering a filename for a new file*
![Screenshot 6](screenshots/FileChooser_screenshot_6.png)
*Fig. 5: New file selected*
![Screenshot 7](screenshots/FileChooser_screenshot_7.png)
*Fig. 5: Quick navigation dropdown*

Wyświetl plik

@ -0,0 +1,400 @@
from ipywidgets import Dropdown, Text, Select, Button, HTML
from ipywidgets import Layout, GridBox, HBox, VBox
import os
class FileChooser(VBox):
_LBL_TEMPLATE = '<span style="margin-left:10px; color:{1};">{0}</span>'
_LBL_NOFILE = 'No file selected'
def __init__(self, path=os.getcwd(), filename='', **kwargs):
self._default_path = path.rstrip(os.path.sep)
self._default_filename = filename
self._selected_path = ''
self._selected_filename = ''
# Widgets
self._pathlist = Dropdown(
description="",
layout=Layout(
width='auto',
grid_area='pathlist'
)
)
self._filename = Text(
placeholder='output filename',
layout=Layout(
width='auto',
grid_area='filename'
)
)
self._dircontent = Select(
rows=8,
layout=Layout(
width='auto',
grid_area='dircontent'
)
)
self._cancel = Button(
description='Cancel',
layout=Layout(
width='auto',
display='none'
)
)
self._select = Button(
description='Select',
layout=Layout(width='auto')
)
# Widget observe handlers
self._pathlist.observe(
self._on_pathlist_select,
names='value'
)
self._dircontent.observe(
self._on_dircontent_select,
names='value'
)
self._filename.observe(
self._on_filename_change,
names='value'
)
self._select.on_click(self._on_select_click)
self._cancel.on_click(self._on_cancel_click)
# Selected file label
self._label = HTML(
value=self._LBL_TEMPLATE.format(
self._LBL_NOFILE,
'black'
),
placeholder='',
description=''
)
# Layout
self._gb = GridBox(
children=[
self._pathlist,
self._filename,
self._dircontent
],
layout=Layout(
display='none',
width='500px',
grid_gap='0px 0px',
grid_template_rows='auto auto',
grid_template_columns='60% 40%',
grid_template_areas='''
'pathlist filename'
'dircontent dircontent'
'''
)
)
buttonbar = HBox(
children=[
self._select,
self._cancel,
self._label
],
layout=Layout(width='auto')
)
# Call setter to set initial form values
self._set_form_values(
self._default_path,
self._default_filename
)
# Call VBox super class __init__
super().__init__(
children=[
self._gb,
buttonbar,
],
layout=Layout(width='auto'),
**kwargs
)
def _get_subpaths(self, path):
'''Walk a path and return a list of subpaths'''
if os.path.isfile(path):
path = os.path.dirname(path)
paths = [path]
path, tail = os.path.split(path)
while tail:
paths.append(path)
path, tail = os.path.split(path)
return paths
def _update_path(self, path, item):
'''Update path with new item'''
if item == '..':
path = os.path.dirname(path)
else:
path = os.path.join(path, item)
return path
def _has_parent(self, path):
'''Check if a path has a parent folder'''
return os.path.basename(path) != ''
def _get_dir_contents(self, path, showhidden=False):
'''Get directory contents'''
files = list()
dirs = list()
if os.path.isdir(path):
for item in os.listdir(path):
append = True
if item.startswith('.') and not showhidden:
append = False
full_item = os.path.join(path, item)
if os.path.isdir(full_item) and append:
dirs.append(item)
elif append:
files.append(item)
if self._has_parent(path):
dirs.insert(0, '..')
return sorted(dirs) + sorted(files)
def _set_form_values(self, path, filename):
'''Set the form values'''
# Disable triggers to prevent selecting an entry in the Select
# box from automatically triggering a new event.
self._pathlist.unobserve(
self._on_pathlist_select,
names='value'
)
self._dircontent.unobserve(
self._on_dircontent_select,
names='value'
)
self._filename.unobserve(
self._on_filename_change,
names='value'
)
# Set form values
self._pathlist.options = self._get_subpaths(path)
self._pathlist.value = path
self._dircontent.options = self._get_dir_contents(path)
self._filename.value = filename
# If the value in the filename Text box equals a value in the
# Select box and the entry is a file then select the entry.
if ((filename in self._dircontent.options) and
os.path.isfile(os.path.join(path, filename))):
self._dircontent.value = filename
else:
self._dircontent.value = None
# Reenable triggers again
self._pathlist.observe(
self._on_pathlist_select,
names='value'
)
self._dircontent.observe(
self._on_dircontent_select,
names='value'
)
self._filename.observe(
self._on_filename_change,
names='value'
)
# Set the state of the select Button
if self._gb.layout.display is None:
selected = os.path.join(
self._selected_path,
self._selected_filename
)
# filename value is empty or equals the selected value
if (filename == '') or (os.path.join(path, filename) == selected):
self._select.disabled = True
else:
self._select.disabled = False
def _on_pathlist_select(self, change):
'''Handler for when a new path is selected'''
self._set_form_values(
change['new'],
self._filename.value
)
def _on_dircontent_select(self, change):
'''Handler for when a folder entry is selected'''
new_path = self._update_path(
self._pathlist.value,
change['new']
)
# Check if folder or file
if os.path.isdir(new_path):
path = new_path
filename = self._filename.value
elif os.path.isfile(new_path):
path = self._pathlist.value
filename = change['new']
self._set_form_values(
path,
filename
)
def _on_filename_change(self, change):
'''Handler for when the filename field changes'''
self._set_form_values(
self._pathlist.value,
change['new']
)
def _on_select_click(self, b):
'''Handler for when the select button is clicked'''
if self._gb.layout.display is 'none':
self._gb.layout.display = None
self._cancel.layout.display = None
# Show the form with the correct path and filename
if self._selected_path and self._selected_filename:
path = self._selected_path
filename = self._selected_filename
else:
path = self._default_path
filename = self._default_filename
self._set_form_values(path, filename)
else:
self._gb.layout.display = 'none'
self._cancel.layout.display = 'none'
self._select.description = 'Change'
self._selected_path = self._pathlist.value
self._selected_filename = self._filename.value
# self._default_path = self._selected_path
# self._default_filename = self._selected_filename
selected = os.path.join(
self._selected_path,
self._selected_filename
)
if os.path.isfile(selected):
self._label.value = self._LBL_TEMPLATE.format(
selected,
'orange'
)
else:
self._label.value = self._LBL_TEMPLATE.format(
selected,
'green'
)
def _on_cancel_click(self, b):
'''Handler for when the cancel button is clicked'''
self._gb.layout.display = 'none'
self._cancel.layout.display = 'none'
self._select.disabled = False
def reset(self, path=None, filename=None):
'''Reset the form to the default path and filename'''
self._selected_path = ''
self._selected_filename = ''
self._label.value = self._LBL_TEMPLATE.format(
self._LBL_NOFILE,
'black'
)
if path is not None:
self._default_path = path.rstrip(os.path.sep)
if filename is not None:
self._default_filename = filename
self._set_form_values(
self._default_path,
self._default_filename
)
@property
def selected(self):
'''Get selected value'''
return os.path.join(
self._selected_path,
self._selected_filename
)
@property
def selected_path(self):
'''Get selected_path value'''
return self._selected_path
@property
def selected_filename(self):
'''Get the selected_filename'''
return self._selected_filename
@property
def default(self):
'''Get the default value'''
return os.path.join(
self._default_path,
self._default_filename
)
@property
def default_path(self):
'''Get the default_path value'''
return self._default_path
@property
def default_filename(self):
'''Get the default_filename value'''
return self._default_filename
@default_path.setter
def default_path(self, path):
'''Set the default_path'''
self._default_path = path.rstrip(os.path.sep)
self._default = os.path.join(
self._default_path,
self._filename.value
)
self._set_form_values(
self._default_path,
self._filename.value
)
@default_filename.setter
def default_filename(self, filename):
'''Set the default_filename'''
self._default_filename = filename
self._default = os.path.join(
self._pathlist.value,
self._default_filename
)
self._set_form_values(
self._pathlist.value,
self._default_filename
)
def __repr__(self):
str_ = "FileChooser(path='{0}', filename='{1}')".format(
self._default_path,
self._default_filename
)
return str_
# Todo
# - keep generic functions into __init__.py
# - move the class to FileChooser.py (allowed?)

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 11 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 43 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 43 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 18 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 48 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 18 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 187 KiB

28
setup.py 100755
Wyświetl plik

@ -0,0 +1,28 @@
#!/usr/bin/env python
from setuptools import setup, find_packages
with open("README.md", "r") as fh:
long_description = fh.read()
setup(
name="ipyfilechooser",
version="0.1b1",
author="Thomas Bouve (@crahan)",
author_email="crahan@n00.be",
description="""Python file chooser widget for use in
Jupyter/IPython in conjunction with ipywidgets""",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/crahan/ipyfilechooser",
packages=find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
],
install_requires=[
'ipywidgets'
]
)