Skip to content
Snippets Groups Projects
Commit 46f92f78 authored by hugo chauvet's avatar hugo chauvet
Browse files

Add selectfiles ui

parent 1e6f750c
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env python3
"""
Simple files selector build on top of IpyWidgets
Author: H.Chauvet
"""
import logging
import os
import ipywidgets as widgets
def list_files(path: str, search: [str, None] = None,
hidden: bool = False, fileexts: [list, None]= None) -> list:
"""
Get the list of files from the given path.
List of files could be filtered by search term
or with file extension list.
Parameters
----------
path: string,
The path to scann for folders
search: string or None,
The search term to filter the folder list
hiiden: boolean,
Do we return hidden file
fileexts: list of string, or None,
Extension that must end the filename. format should be lowercase
and start with a dot such as ['.jpeg', '.png']
Return a list of folder names
"""
if path.startswith('~'):
path = os.path.expanduser(path)
abspath = os.path.abspath(os.path.dirname(path))
listf = os.listdir(abspath)
# Create a fullpath to those listed paths
listall = tuple(os.path.join(abspath, p) for p in listf)
# Keep only basename
listf = (os.path.basename(p) for p in listall if not os.path.isdir(p))
listdir = (os.path.basename(p) for p in listall if os.path.isdir(p))
# filter extensions
if isinstance(fileexts, (list, tuple)) and len(fileexts)>0:
listf = (p for p in listf if os.path.splitext(p)[1] in fileexts)
listall = tuple(listdir) + tuple(listf)
if not hidden:
listall = (p for p in listall if not p.strip().startswith('.'))
if search is not None and len(search)>0:
listall = (p for p in listall if search in p)
return tuple(listall)
class SelectFiles():
"""
A simple folder selector build on top of ipywidgets.
"""
def __init__(self, init_path='./', fileext=None, autoclose=False, show_hidden=False,
load_callback=None, layout=widgets.Layout()):
"""
autoclose: boolean,
Do we need to autoclose the widget when select button is clicked. The default is True
fileext: List of extension or None,
File extension, to filter files in the file list
to end with the given list of extension end (such as tif,
jpeg)
show_hidden: boolean,
If True show hidden files in file list. Default is False.
load_callback: function or None,
Function called when the select button is clicked. Default action is None
layout: None or ipywidgets.Layout object,
Layout to pass to the VBox model
"""
self.callback = load_callback
self.selected_files = []
self.autoclose = autoclose
self.hidden = show_hidden
self.fileext = fileext
self.init_path = init_path
self.wpath = widgets.Text(
value=init_path,
placeholder='Enter the path (../ to go back, / start at disk root, ~/ start at your Home)',
description='',
disabled=False,
layout=widgets.Layout(width='auto')
)
self.wpathlist = widgets.SelectMultiple(
value=[],
options=list_files(init_path, hidden=self.hidden, fileexts=self.fileext),
layout=widgets.Layout(min_height='110px', width='auto')
)
self.wselectbtn = widgets.Button(
description='Select File(s)',
disabled=True,
button_style='info', # 'success', 'info', 'warning', 'danger' or ''
tooltip='Search or select a file in the list above',
icon='caret-square-o-up',
layout=widgets.Layout(width='auto')
)
# Connect to callback
self.wpath.observe(self.autocomplete, names='value')
self.wpathlist.observe(self.on_list_select, 'value')
self.wselectbtn.on_click(self.on_click)
# Build the layout
self.box = widgets.AppLayout(header=self.wpath,
left_sidebar=None,
center=self.wpathlist,
right_sidebar=None,
footer=self.wselectbtn,
layout=layout)
def autocomplete(self, val):
"""
Perform the autocompletion of the directory that match with the input
path in val parameter
"""
nval = val['new']
# Do we add str to path or we are deleting the path
forward = bool(len(val['new']) > len(val['old']))
search_in_path = os.path.basename(nval).strip()
if nval.strip() == '':
nval = './'
try:
good_paths = list_files(nval, search_in_path, hidden=self.hidden, fileexts=self.fileext)
except Exception as err:
logging.error(err)
good_paths = None
if good_paths is None:
self.wpathlist.options = ['Not a valid Path']
self.wselectbtn.disabled = True
self.wselectbtn.button_style = 'danger'
self.wselectbtn.description = 'Not a valid file or path!'
self.wselectbtn.icon = 'times-circle-o'
self.wpathlist.value = []
else:
# Update list of paths
self.wpathlist.options = good_paths
# Update the select button style
if len(good_paths) == 0:
# Something wrong no file found
self.wselectbtn.disabled = True
self.wselectbtn.button_style = 'danger'
self.wselectbtn.description = 'Not a valid file!'
self.wselectbtn.icon = 'times-circle-o'
self.wpathlist.value = []
else:
if len(self.wpathlist.value) == 0:
self.wselectbtn.disabled = True
self.wselectbtn.button_style = 'info'
self.wselectbtn.description = 'Select file(s) [0/%i]' % (len(self.wpathlist.options))
self.wselectbtn.icon = 'caret-square-o-up'
if forward:
# Auto fill files name if only one name remains
if len(search_in_path)>0:
fstarts = tuple(p for p in good_paths if p.startswith(search_in_path))
if len(fstarts) == 1:
tmp = self.wpath.value
base = os.path.dirname(tmp)
lastdir = os.path.basename(tmp)
lastdir = lastdir.replace(lastdir, fstarts[0])
self.wpath.value = os.path.join(base, lastdir)
# If only one good path found, enter to the folder if the name is completed
if len(good_paths) == 1:
# Auto enter in the folder if it's a folder or select the file if it's a file
if good_paths[0] == search_in_path and os.path.isdir(os.path.expanduser(self.wpath.value)):
self.wpath.value = self.wpath.value + '/'
# If it's a valid file autoselect it
if good_paths[0] == search_in_path and not os.path.isdir(os.path.expanduser(self.wpath.value)):
self.wselectbtn.disabled = False
self.wselectbtn.button_style = 'success'
self.wselectbtn.description = 'Load %s file' % os.path.basename(self.wpath.value) # remove the last / in path str
self.wselectbtn.icon = 'check'
def on_list_select(self, val):
"""
Callback when selected folder in list are changed
"""
nselected = val['new']
if len(nselected) > 0:
self.wselectbtn.disabled = False
self.wselectbtn.button_style = 'success'
self.wselectbtn.description = 'Load (%i) file' % len(nselected)
self.wselectbtn.icon='check'
else:
self.wselectbtn.disabled = True
self.wselectbtn.button_style = 'info'
self.wselectbtn.description = 'Select files(s) [0/%i]' % (len(self.wpathlist.options))
self.wselectbtn.icon = 'caret-square-o-up'
def on_click(self, val):
"""
Call back when the load button is clicked
"""
if '~' in self.wpath.value:
rpath = os.path.expanduser(self.wpath.value)
else:
rpath = self.wpath.value
if len(self.wpathlist.value) > 0:
self.selected_files = [os.path.abspath(os.path.join(os.path.dirname(rpath), p)) for p in self.wpathlist.value]
else:
self.selected_files = [os.path.abspath(rpath)]
if self.autoclose:
self.box.close()
if self.callback is not None:
try:
self.callback(self.selected_files)
except Exception as err:
logging.error(err)
def __call__(self):
return self.box
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment