class FileChooser(VBox): _LBL_TEMPLATE = '<span style="margin-left:10px; color:{1};">{0}</span>' _LBL_NOFILE = 'No file selected' _LBL_NOFOLDER = 'No folder selected' def __init__(self, path=os.getcwd(), filename='', title='', select_desc='Select', change_desc='Change', show_hidden=False, include_files=True, include_folders=True, **kwargs): self._default_path = path # path.rstrip(os.path.sep) self._default_filename = filename self._selected_path = '' self._selected_filename = '' self._show_hidden = show_hidden self._select_desc = select_desc self._change_desc = change_desc self._include_files = include_files self._include_folders = include_folders if not include_folders and not include_files: raise ValueError("You can't exclude both files and folders") # 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', visibility='visible' if self._include_files else 'hidden', ), ) 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=self._select_desc, layout=Layout(width='auto')) self._title = HTML(value=title) if title is '': self._title.layout.display = 'none' # 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 if self._include_files else self._LBL_NOFOLDER if self._include_folders else "", '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__ kwargs.setdefault('layout', Layout(width='auto')) super().__init__(children=[ self._title, self._gb, buttonbar, ], **kwargs) 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 = get_subpaths(path) self._pathlist.value = path self._filename.value = filename self._dircontent.options = get_dir_contents( path, hidden=self._show_hidden, include_files=self._include_files, include_folders=self._include_folders) # 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)) \ and self._include_files: 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 = 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) or \ (self._selected_path and not self._include_files): 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 = self._change_desc 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 not self._include_files and os.path.isdir(selected): selected = selected.rstrip(os.path.sep) 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 if self._include_files else self._LBL_NOFOLDER if self._include_folders else "", '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) def refresh(self): """Re-render the form""" self._set_form_values(self._pathlist.value, self._filename.value) @property def show_hidden(self): """Get current number of rows""" return self._show_hidden @show_hidden.setter def show_hidden(self, hidden): """Set number of rows""" self._show_hidden = hidden self.refresh() @property def rows(self): """Get current number of rows""" return self._dircontent.rows @rows.setter def rows(self, rows): """Set number of rows""" self._dircontent.rows = rows @property def title(self): """Get the title""" return self._title.value @title.setter def title(self, title): """Set the title""" self._title.value = title if title is '': self._title.layout.display = 'none' else: self._title.layout.display = None @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 @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) @property def default_filename(self): """Get the default_filename value""" return self._default_filename @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) @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 def __repr__(self): str_ = ("FileChooser(" "path='{0}', " "filename='{1}', " "show_hidden='{2}')").format(self._default_path, self._default_filename, self._show_hidden) return str_
class FileChooser(VBox, ValueWidget): """FileChooser class.""" _LBL_TEMPLATE = '<span style="margin-left:10px; color:{1};">{0}</span>' _LBL_NOFILE = 'No file selected' file_path = Unicode(allow_none=True) def __init__( self, path=os.getcwd(), filename='', show_hidden=False, use_dir_icons=False, filter_pattern=None, **kwargs): """Initialize FileChooser object.""" self._default_path = path.rstrip(os.path.sep) self._default_filename = filename self._show_hidden = show_hidden self._use_dir_icons = use_dir_icons self._filter_pattern = filter_pattern # Widgets self._pathlist = Dropdown( description="", layout=Layout( width='auto', grid_area='pathlist' ) ) self._dircontent = Select( rows=8, layout=Layout( width='auto', grid_area='dircontent' ) ) # Widget observe handlers self._pathlist.observe( self._on_pathlist_select, names='value' ) self._dircontent.observe( self._on_dircontent_select, names='value' ) # Layout self._gb = GridBox( children=[ self._pathlist, self._dircontent ], layout=Layout( width='500px', grid_gap='0px 0px', grid_template_rows='auto auto', grid_template_columns='60% 40%', grid_template_areas=''' 'pathlist pathlist' 'dircontent dircontent' ''' ) ) # Call setter to set initial form values self._set_form_values( self._default_path, self._default_filename ) self._initialize_form_values() # Call VBox super class __init__ super().__init__( children=[self._gb], layout=Layout(width='auto'), **kwargs ) 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' ) # Set form values self._pathlist.options = get_subpaths(path) self._pathlist.value = path # file/folder real names dircontent_real_names = get_dir_contents( path, show_hidden=self._show_hidden, prepend_icons=False, filter_pattern=self._filter_pattern ) # file/folder display names dircontent_display_names = get_dir_contents( path, show_hidden=self._show_hidden, prepend_icons=self._use_dir_icons, filter_pattern=self._filter_pattern ) # Dict to map real names to display names self._map_name_to_disp = { real_name: disp_name for real_name, disp_name in zip( dircontent_real_names, dircontent_display_names ) } # Dict to map display names to real names self._map_disp_to_name = dict( reversed(item) for item in self._map_name_to_disp.items() ) # Set _dircontent form value to display names self._dircontent.options = dircontent_display_names # 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 dircontent_real_names) and os.path.isfile(os.path.join(path, filename))): self._dircontent.value = self._map_name_to_disp[filename] else: self._dircontent.value = None # Re-enable triggers again self._pathlist.observe( self._on_pathlist_select, names='value' ) self._dircontent.observe( self._on_dircontent_select, names='value' ) self._update_file_path() def _on_pathlist_select(self, change): """Handle selecting a path entry.""" self._set_form_values( change['new'], self._selected_filename ) def _on_dircontent_select(self, change): """Handle selecting a folder entry.""" new_path = os.path.realpath( os.path.join( self._selected_path, self._map_disp_to_name[change['new']] ) ) # Check if folder or file if os.path.isdir(new_path): path = new_path filename = None elif os.path.isfile(new_path): path = self._selected_path filename = self._map_disp_to_name[change['new']] self._set_form_values( path, filename ) def _initialize_form_values(self): """Show the dialog.""" # Show the form with the correct path and filename if ((self._selected_path is not None) and (self._selected_filename is not None)): path = self._selected_path filename = self._selected_filename else: path = self._default_path filename = self._default_filename self._set_form_values(path, filename) @property def _selected_path(self): return self._pathlist.value @property def _selected_filename(self): if self._dircontent.value is None: return None else: return self._map_disp_to_name[self._dircontent.value] def _update_file_path(self): if self._selected_filename is None or self._selected_path is None: self.file_path = None else: self.file_path = os.path.join(self._selected_path, self._selected_filename) def refresh(self): """Re-render the form.""" self._set_form_values( self._selected_path, self._selected_filename ) @property def show_hidden(self): """Get _show_hidden value.""" return self._show_hidden @show_hidden.setter def show_hidden(self, hidden): """Set _show_hidden value.""" self._show_hidden = hidden self.refresh() @property def use_dir_icons(self): """Get _use_dir_icons value.""" return self._use_dir_icons @use_dir_icons.setter def use_dir_icons(self, dir_icons): """Set _use_dir_icons value.""" self._use_dir_icons = dir_icons self.refresh() @property def rows(self): """Get current number of rows.""" return self._dircontent.rows @rows.setter def rows(self, rows): """Set number of rows.""" self._dircontent.rows = rows @property def default_path(self): """Get the default_path value.""" return self._default_path @default_path.setter def default_path(self, path): """Set the default_path.""" self._default_path = path.rstrip(os.path.sep) self._set_form_values( self._default_path, self._selected_filename ) @property def default_filename(self): """Get the default_filename value.""" return self._default_filename @default_filename.setter def default_filename(self, filename): """Set the default_filename.""" self._default_filename = filename self._set_form_values( self._selected_path, self._default_filename ) @property def filter_pattern(self): """Get file name filter pattern.""" return self._filter_pattern @filter_pattern.setter def filter_pattern(self, filter_pattern): """Set file name filter pattern.""" self._filter_pattern = filter_pattern self.refresh() @property def selected(self): """Get selected value.""" try: return os.path.join( self._selected_path, self._selected_filename ) except TypeError: return None
class FileChooser(VBox, ValueWidget): """FileChooser class.""" _LBL_TEMPLATE = '<span style="color:{1};">{0}</span>' _LBL_NOFILE = 'No selection' def __init__(self, path: str = os.getcwd(), filename: str = '', title: str = '', select_desc: str = 'Select', change_desc: str = 'Change', show_hidden: bool = False, select_default: bool = False, dir_icon: Optional[str] = '\U0001F4C1 ', dir_icon_append: bool = False, show_only_dirs: bool = False, filter_pattern: Optional[Sequence[str]] = None, sandbox_path: Optional[str] = None, layout: Layout = Layout(width='500px'), **kwargs): """Initialize FileChooser object.""" # Check if path and sandbox_path align if sandbox_path and not has_parent_path(normalize_path(path), normalize_path(sandbox_path)): raise ParentPathError(path, sandbox_path) # Verify the filename is valid if not is_valid_filename(filename): raise InvalidFileNameError(filename) self._default_path = normalize_path(path) self._default_filename = filename self._selected_path: Optional[str] = None self._selected_filename: Optional[str] = None self._show_hidden = show_hidden self._select_desc = select_desc self._change_desc = change_desc self._select_default = select_default self._dir_icon = dir_icon self._dir_icon_append = dir_icon_append self._show_only_dirs = show_only_dirs self._filter_pattern = filter_pattern self._sandbox_path = normalize_path( sandbox_path) if sandbox_path is not None else None self._callback: Optional[Callable] = None # 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', display=(None, "none")[self._show_only_dirs]), disabled=self._show_only_dirs) self._dircontent = Select(rows=8, layout=Layout(width='auto', grid_area='dircontent')) self._cancel = Button(description='Cancel', layout=Layout(min_width='6em', width='6em', display='none')) self._select = Button(description=self._select_desc, layout=Layout(min_width='6em', width='6em')) self._title = HTML(value=title) if title == '': self._title.layout.display = 'none' # 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=Layout(margin='0 0 0 1em')) # Layout self._gb = GridBox( children=[self._pathlist, self._filename, self._dircontent], layout=Layout(display='none', width='auto', grid_gap='0px 0px', grid_template_rows='auto auto', grid_template_columns='60% 40%', grid_template_areas=''' 'pathlist {}' 'dircontent dircontent' '''.format( ('filename', 'pathlist')[self._show_only_dirs]))) buttonbar = HBox(children=[ self._select, self._cancel, Box([self._label], layout=Layout(overflow='auto')) ], layout=Layout(width='auto')) # Call setter to set initial form values self._set_form_values(self._default_path, self._default_filename) # Use the defaults as the selected values if self._select_default: self._apply_selection() # Call VBox super class __init__ super().__init__(children=[self._title, self._gb, buttonbar], layout=layout, **kwargs) def _set_form_values(self, path: str, filename: str) -> None: """Set the form values.""" # Check if the path falls inside the configured sandbox path if self._sandbox_path and not has_parent_path(path, self._sandbox_path): raise ParentPathError(path, self._sandbox_path) # 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') try: # Fail early if the folder can not be read _ = os.listdir(path) # In folder only mode zero out the filename if self._show_only_dirs: filename = '' # Set form values restricted_path = self._restrict_path(path) subpaths = get_subpaths(restricted_path) if os.path.splitdrive(subpaths[-1])[0]: # Add missing Windows drive letters drives = get_drive_letters() subpaths.extend(list(set(drives) - set(subpaths))) self._pathlist.options = subpaths self._pathlist.value = restricted_path self._filename.value = filename # file/folder real names dircontent_real_names = get_dir_contents( path, show_hidden=self._show_hidden, show_only_dirs=self._show_only_dirs, dir_icon=None, filter_pattern=self._filter_pattern, top_path=self._sandbox_path) # file/folder display names dircontent_display_names = get_dir_contents( path, show_hidden=self._show_hidden, show_only_dirs=self._show_only_dirs, dir_icon=self._dir_icon, dir_icon_append=self._dir_icon_append, filter_pattern=self._filter_pattern, top_path=self._sandbox_path) # Dict to map real names to display names self._map_name_to_disp = { real_name: disp_name for real_name, disp_name in zip(dircontent_real_names, dircontent_display_names) } # Dict to map display names to real names self._map_disp_to_name = { disp_name: real_name for real_name, disp_name in self._map_name_to_disp.items() } # Set _dircontent form value to display names self._dircontent.options = dircontent_display_names # 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 dircontent_real_names) and os.path.isfile(os.path.join(path, filename))): self._dircontent.value = self._map_name_to_disp[filename] else: self._dircontent.value = None # Update the state of the select button if self._gb.layout.display is None: # Disable the select button if path and filename # - equal an existing folder in the current view # - contains an invalid character sequence # - equal the already selected values # - don't match the provided filter pattern(s) check1 = filename in dircontent_real_names check2 = os.path.isdir(os.path.join(path, filename)) check3 = not is_valid_filename(filename) check4 = False check5 = False # Only check selected if selected is set if ((self._selected_path is not None) and (self._selected_filename is not None)): selected = os.path.join(self._selected_path, self._selected_filename) check4 = os.path.join(path, filename) == selected # Ensure only allowed extensions are used if self._filter_pattern: check5 = not match_item(filename, self._filter_pattern) if (check1 and check2) or check3 or check4 or check5: self._select.disabled = True else: self._select.disabled = False except PermissionError: # Deselect the unreadable folder and generate a warning self._dircontent.value = None warnings.warn(f'Permission denied for {path}', RuntimeWarning) # Reenable triggers 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') def _on_pathlist_select(self, change: Mapping[str, str]) -> None: """Handle selecting a path entry.""" self._set_form_values(self._expand_path(change['new']), self._filename.value) def _on_dircontent_select(self, change: Mapping[str, str]) -> None: """Handle selecting a folder entry.""" new_path = os.path.realpath( os.path.join(self._expand_path(self._pathlist.value), self._map_disp_to_name[change['new']])) # Check if folder or file if os.path.isdir(new_path): path = new_path filename = self._filename.value else: path = self._expand_path(self._pathlist.value) filename = self._map_disp_to_name[change['new']] self._set_form_values(path, filename) def _on_filename_change(self, change: Mapping[str, str]) -> None: """Handle filename field changes.""" self._set_form_values(self._expand_path(self._pathlist.value), change['new']) def _on_select_click(self, _b) -> None: """Handle select button clicks.""" if self._gb.layout.display == 'none': # If not shown, open the dialog self._show_dialog() else: # If shown, close the dialog and apply the selection self._apply_selection() # Execute callback function if self._callback is not None: try: self._callback(self) except TypeError: # Support previous behaviour of not passing self self._callback() def _show_dialog(self) -> None: """Show the dialog.""" # Show dialog and cancel button self._gb.layout.display = None self._cancel.layout.display = None # Show the form with the correct path and filename if ((self._selected_path is not None) and (self._selected_filename is not None)): path = self._selected_path filename = self._selected_filename else: path = self._default_path filename = self._default_filename self._set_form_values(path, filename) def _apply_selection(self) -> None: """Close the dialog and apply the selection.""" self._selected_path = self._expand_path(self._pathlist.value) self._selected_filename = self._filename.value if ((self._selected_path is not None) and (self._selected_filename is not None)): selected = os.path.join(self._selected_path, self._selected_filename) self._gb.layout.display = 'none' self._cancel.layout.display = 'none' self._select.description = self._change_desc self._select.disabled = False if os.path.isfile(selected): self._label.value = self._LBL_TEMPLATE.format( self._restrict_path(selected), 'orange') else: self._label.value = self._LBL_TEMPLATE.format( self._restrict_path(selected), 'green') def _on_cancel_click(self, _b) -> None: """Handle cancel button clicks.""" self._gb.layout.display = 'none' self._cancel.layout.display = 'none' self._select.disabled = False def _expand_path(self, path) -> str: """Calculate the full path using the sandbox path.""" if self._sandbox_path: path = os.path.join(self._sandbox_path, path.lstrip(os.sep)) return path def _restrict_path(self, path) -> str: """Calculate the sandboxed path using the sandbox path.""" if self._sandbox_path == os.sep: pass elif self._sandbox_path == path: path = os.sep elif self._sandbox_path: if os.path.splitdrive(self._sandbox_path)[0] and len( self._sandbox_path) == 3: # If the value is 'c:\\', strip 'c:' so we retain the leading os.sep char path = strip_parent_path( path, os.path.splitdrive(self._sandbox_path)[0]) else: path = strip_parent_path(path, self._sandbox_path) return path def reset(self, path: Optional[str] = None, filename: Optional[str] = None) -> None: """Reset the form to the default path and filename.""" # Check if path and sandbox_path align if path is not None and self._sandbox_path and not has_parent_path( normalize_path(path), self._sandbox_path): raise ParentPathError(path, self._sandbox_path) # Verify the filename is valid if filename is not None and not is_valid_filename(filename): raise InvalidFileNameError(filename) # Remove selection self._selected_path = None self._selected_filename = None # Hide dialog and cancel button self._gb.layout.display = 'none' self._cancel.layout.display = 'none' # Reset select button and label self._select.description = self._select_desc self._select.disabled = False self._label.value = self._LBL_TEMPLATE.format(self._LBL_NOFILE, 'black') if path is not None: self._default_path = normalize_path(path) if filename is not None: self._default_filename = filename self._set_form_values(self._default_path, self._default_filename) # Use the defaults as the selected values if self._select_default: self._apply_selection() def refresh(self) -> None: """Re-render the form.""" self._set_form_values(self._expand_path(self._pathlist.value), self._filename.value) @property def show_hidden(self) -> bool: """Get _show_hidden value.""" return self._show_hidden @show_hidden.setter def show_hidden(self, hidden: bool) -> None: """Set _show_hidden value.""" self._show_hidden = hidden self.refresh() @property def dir_icon(self) -> Optional[str]: """Get dir icon value.""" return self._dir_icon @dir_icon.setter def dir_icon(self, dir_icon: Optional[str]) -> None: """Set dir icon value.""" self._dir_icon = dir_icon self.refresh() @property def dir_icon_append(self) -> bool: """Get dir icon value.""" return self._dir_icon_append @dir_icon_append.setter def dir_icon_append(self, dir_icon_append: bool) -> None: """Prepend or append the dir icon.""" self._dir_icon_append = dir_icon_append self.refresh() @property def rows(self) -> int: """Get current number of rows.""" return self._dircontent.rows @rows.setter def rows(self, rows: int) -> None: """Set number of rows.""" self._dircontent.rows = rows @property def title(self) -> str: """Get the title.""" return self._title.value @title.setter def title(self, title: str) -> None: """Set the title.""" self._title.value = title if title == '': self._title.layout.display = 'none' else: self._title.layout.display = None @property def default(self) -> str: """Get the default value.""" return os.path.join(self._default_path, self._default_filename) @property def default_path(self) -> str: """Get the default_path value.""" return self._default_path @default_path.setter def default_path(self, path: str) -> None: """Set the default_path.""" # Check if path and sandbox_path align if self._sandbox_path and not has_parent_path(normalize_path(path), self._sandbox_path): raise ParentPathError(path, self._sandbox_path) self._default_path = normalize_path(path) self._set_form_values(self._default_path, self._filename.value) @property def default_filename(self) -> str: """Get the default_filename value.""" return self._default_filename @default_filename.setter def default_filename(self, filename: str) -> None: """Set the default_filename.""" # Verify the filename is valid if not is_valid_filename(filename): raise InvalidFileNameError(filename) self._default_filename = filename self._set_form_values(self._expand_path(self._pathlist.value), self._default_filename) @property def sandbox_path(self) -> Optional[str]: """Get the sandbox_path.""" return self._sandbox_path @sandbox_path.setter def sandbox_path(self, sandbox_path: str) -> None: """Set the sandbox_path.""" # Check if path and sandbox_path align if sandbox_path and not has_parent_path(self._default_path, normalize_path(sandbox_path)): raise ParentPathError(self._default_path, sandbox_path) self._sandbox_path = normalize_path( sandbox_path) if sandbox_path is not None else None # Reset the dialog self.reset() @property def show_only_dirs(self) -> bool: """Get show_only_dirs property value.""" return self._show_only_dirs @show_only_dirs.setter def show_only_dirs(self, show_only_dirs: bool) -> None: """Set show_only_dirs property value.""" self._show_only_dirs = show_only_dirs # Update widget layout self._filename.disabled = self._show_only_dirs self._filename.layout.display = (None, "none")[self._show_only_dirs] self._gb.layout.children = [self._pathlist, self._dircontent] if not self._show_only_dirs: self._gb.layout.children.insert(1, self._filename) self._gb.layout.grid_template_areas = ''' 'pathlist {}' 'dircontent dircontent' '''.format(('filename', 'pathlist')[self._show_only_dirs]) # Reset the dialog self.reset() @property def filter_pattern(self) -> Optional[Sequence[str]]: """Get file name filter pattern.""" return self._filter_pattern @filter_pattern.setter def filter_pattern(self, filter_pattern: Optional[Sequence[str]]) -> None: """Set file name filter pattern.""" self._filter_pattern = filter_pattern self.refresh() @property def value(self) -> Optional[str]: """Get selected value.""" return self.selected @property def selected(self) -> Optional[str]: """Get selected value.""" selected = None if ((self._selected_path is not None) and (self._selected_filename is not None)): selected = os.path.join(self._selected_path, self._selected_filename) return selected @property def selected_path(self) -> Optional[str]: """Get selected_path value.""" return self._selected_path @property def selected_filename(self) -> Optional[str]: """Get the selected_filename.""" return self._selected_filename def __repr__(self) -> str: """Build string representation.""" properties = f"path='{self._default_path}'" properties += f", filename='{self._default_filename}'" properties += f", title='{self._title.value}'" properties += f", show_hidden={self._show_hidden}" properties += f", select_desc='{self._select_desc}'" properties += f", change_desc='{self._change_desc}'" properties += f", select_default={self._select_default}" properties += f", show_only_dirs={self._show_only_dirs}" properties += f", dir_icon_append={self._dir_icon_append}" if self._sandbox_path is not None: properties += f", sandbox_path='{self._sandbox_path}'" if self._dir_icon: properties += f", dir_icon='{self._dir_icon}'" if self._filter_pattern: if isinstance(self._filter_pattern, str): properties += f", filter_pattern='{self._filter_pattern}'" else: properties += f", filter_pattern={self._filter_pattern}" return f"{self.__class__.__name__}({properties})" def register_callback( self, callback: Callable[[Optional['FileChooser']], None]) -> None: """Register a callback function.""" self._callback = callback def get_interact_value(self) -> Optional[str]: """Return the value which should be passed to interactive functions.""" return self.selected
class FileChooser(VBox): """ # FileChooser code is from # https://github.com/crahan/ipyfilechooser """ _LBL_TEMPLATE = '<span style="margin-left:10px; color:{1};">{0}</span>' _LBL_NOFILE = 'No file selected' def __init__(self, path=os.getcwd(), filename='', show_hidden=False, **kwargs): self._default_path = path.rstrip(os.path.sep) self._default_filename = filename self._selected_path = '' self._selected_filename = '' self._show_hidden = show_hidden # 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', button_style= 'warning', # 'success', 'info', 'warning', 'danger' or '' layout=Layout(width='auto', display='none')) self._select = Button( description='Select', button_style= 'success', # 'success', 'info', 'warning', 'danger' or '' 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 _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 = get_subpaths(path) self._pathlist.value = path self._filename.value = filename self._dircontent.options = get_dir_contents(path, hidden=self._show_hidden) # 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 = 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 == '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) def refresh(self): '''Re-render the form''' self._set_form_values(self._pathlist.value, self._filename.value) @property def show_hidden(self): '''Get current number of rows''' return self._show_hidden @show_hidden.setter def show_hidden(self, hidden): '''Set number of rows''' self._show_hidden = hidden self.refresh() @property def rows(self): '''Get current number of rows''' return self._dircontent.rows @rows.setter def rows(self, rows): '''Set number of rows''' self._dircontent.rows = rows @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 @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) @property def default_filename(self): '''Get the default_filename value''' return self._default_filename @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) @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 nmrname(self): # added by MAD return os.path.join( os.path.basename(os.path.dirname(self._selected_path)), os.path.basename(self._selected_path)) def __repr__(self): str_ = ("FileChooser(" "path='{0}', " "filename='{1}', " "show_hidden='{2}')").format(self._default_path, self._default_filename, self._show_hidden) return str_