Skip to content

Commit

Permalink
feat: implement the FileInput API for extra.FileInput
Browse files Browse the repository at this point in the history
  • Loading branch information
egormkn authored and mariobuikhuizen committed Nov 23, 2023
1 parent f20c554 commit 3d93213
Show file tree
Hide file tree
Showing 2 changed files with 289 additions and 46 deletions.
215 changes: 194 additions & 21 deletions ipyvuetify/extra/file_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import io
import os
import sys
import warnings

import IPython
import nest_asyncio
import traitlets
from ipywidgets import dlink, widget_serialization
from traitlets import Any, Bool, Dict, Float, Int, List, Unicode, Union

import ipyvuetify as v

Expand Down Expand Up @@ -52,7 +55,7 @@ def __init__(self, widget, file_index, timeout=30):
self.timeout = timeout
self.valid = True
self.offset = 0
self.size = widget.file_info[file_index]["size"]
self.size = widget.v_model[file_index]["size"]

self.chunk_queue = []

Expand Down Expand Up @@ -148,35 +151,205 @@ def readall(self):


class FileInput(v.VuetifyTemplate):
template = traitlets.Unicode(load_template("file_input.vue")).tag(sync=True)
data = traitlets.Unicode("{myfiles: undefined}").tag(sync=True)

file_info = traitlets.List().tag(sync=True)
version = traitlets.Int(0).tag(sync=True)
multiple = traitlets.Bool(True).tag(sync=True)
disabled = traitlets.Bool(False).tag(sync=True)
directory = traitlets.Bool(False).tag(sync=True)
accept = traitlets.Unicode().tag(sync=True)
total_progress = traitlets.Int(0).tag(sync=True)
show_progress = traitlets.Bool(True).tag(sync=True)
progress_indeterminate = traitlets.Bool(False).tag(sync=True)
append_icon = Unicode(None, allow_none=True).tag(sync=True)

append_outer_icon = Unicode(None, allow_none=True).tag(sync=True)

autofocus = Bool(None, allow_none=True).tag(sync=True)

background_color = Unicode(None, allow_none=True).tag(sync=True)

chips = Bool(None, allow_none=True).tag(sync=True)

clear_icon = Unicode(None, allow_none=True).tag(sync=True)

clearable = Bool(None, allow_none=True).tag(sync=True)

color = Unicode(None, allow_none=True).tag(sync=True)

counter = Union([Bool(), Float(), Unicode()], default_value=None, allow_none=True).tag(sync=True)

counter_size_string = Unicode(None, allow_none=True).tag(sync=True)

counter_string = Unicode(None, allow_none=True).tag(sync=True)

dark = Bool(None, allow_none=True).tag(sync=True)

dense = Bool(None, allow_none=True).tag(sync=True)

disabled = Bool(None, allow_none=True).tag(sync=True)

error = Bool(None, allow_none=True).tag(sync=True)

error_count = Union([Float(), Unicode()], default_value=None, allow_none=True).tag(sync=True)

error_messages = Union([Unicode(), List(Any())], default_value=None, allow_none=True).tag(sync=True)

filled = Bool(None, allow_none=True).tag(sync=True)

flat = Bool(None, allow_none=True).tag(sync=True)

full_width = Bool(None, allow_none=True).tag(sync=True)

height = Union([Float(), Unicode()], default_value=None, allow_none=True).tag(sync=True)

hide_details = Union([Bool(), Unicode()], default_value=None, allow_none=True).tag(sync=True)

# Missing Vuetify prop: hide-input

hint = Unicode(None, allow_none=True).tag(sync=True)

id = Unicode(None, allow_none=True).tag(sync=True)

label = Unicode(None, allow_none=True).tag(sync=True)

light = Bool(None, allow_none=True).tag(sync=True)

loader_height = Union([Float(), Unicode()], default_value=None, allow_none=True).tag(sync=True)

loading = Union([Bool(), Unicode()], default_value=None, allow_none=True).tag(sync=True)

messages = Union([Unicode(), List(Any())], default_value=None, allow_none=True).tag(sync=True)

multiple = Bool(None, allow_none=True).tag(sync=True)

outlined = Bool(None, allow_none=True).tag(sync=True)

persistent_hint = Bool(None, allow_none=True).tag(sync=True)

# Missing Vuetify prop: persistent-placeholder

placeholder = Unicode(None, allow_none=True).tag(sync=True)

prefix = Unicode(None, allow_none=True).tag(sync=True)

prepend_icon = Unicode(None, allow_none=True).tag(sync=True)

prepend_inner_icon = Unicode(None, allow_none=True).tag(sync=True)

readonly = Bool(None, allow_none=True).tag(sync=True)

reverse = Bool(None, allow_none=True).tag(sync=True)

rounded = Bool(None, allow_none=True).tag(sync=True)

rules = List(Any(), default_value=None, allow_none=True).tag(sync=True)

shaped = Bool(None, allow_none=True).tag(sync=True)

show_size = Union([Bool(), Float()], default_value=None, allow_none=True).tag(sync=True)

single_line = Bool(None, allow_none=True).tag(sync=True)

small_chips = Bool(None, allow_none=True).tag(sync=True)

solo = Bool(None, allow_none=True).tag(sync=True)

solo_inverted = Bool(None, allow_none=True).tag(sync=True)

success = Bool(None, allow_none=True).tag(sync=True)

success_messages = Union([Unicode(), List(Any())], default_value=None, allow_none=True).tag(sync=True)

suffix = Unicode(None, allow_none=True).tag(sync=True)

truncate_length = Union([Float(), Unicode()], default_value=None, allow_none=True).tag(sync=True)

type = Unicode(None, allow_none=True).tag(sync=True)

validate_on_blur = Bool(None, allow_none=True).tag(sync=True)

value = Any(None, allow_none=True).tag(sync=True)

# VueWidget props

v_model = List(Dict(), allow_none=True).tag(sync=True)

style_ = Unicode(None, allow_none=True).tag(sync=True)

class_ = Unicode(None, allow_none=True).tag(sync=True)

attributes = Dict(None, allow_none=True).tag(sync=True)

v_slots = List(Dict()).tag(sync=True, **widget_serialization)

v_on = Unicode(None, allow_none=True).tag(sync=True)

# Component-specific props

template = Unicode(load_template("file_input.vue")).tag(sync=True)

version = Int(0).tag(sync=True)

total_progress = Int(0).tag(sync=True)

progress_indeterminate = Bool(False).tag(sync=True)

file_info = List(Dict(), allow_none=True) # Deprecated, use v_model instead

total_progress_inner = 0
total_size_inner = 0

def __init__(self, **kwargs):
def __init__(self, *args, **kwargs):
self.chunk_listeners = {}
self.stats = []
super().__init__(**kwargs)

# Keep default behaviour
kwargs.setdefault("multiple", True)
kwargs.setdefault("clearable", True)

attributes = kwargs.setdefault("attributes", {})
if "accept" in kwargs:
warnings.warn("accept argument is deprecated, use attributes dictionary instead", DeprecationWarning)
attributes["accept"] = kwargs["accept"]
del kwargs["accept"]
if "directory" in kwargs:
warnings.warn("directory argument is deprecated, use attributes dictionary instead", DeprecationWarning)
attributes["directory"] = kwargs["directory"]
del kwargs["directory"]
if "show_progress" in kwargs:
warnings.warn(
"show_progress argument is deprecated, use v_slots to change the progress slot", DeprecationWarning
)
del kwargs["show_progress"]
if "file_info" in kwargs:
warnings.warn("file_info cannot be set on FileInput")
del kwargs["file_info"]
if "v_model" in kwargs:
warnings.warn("v_model cannot be set on FileInput")
del kwargs["v_model"]

# Maintain backwards compatibility for the file_info
dlink((self, "v_model"), (self, "file_info"))

super().__init__(*args, **kwargs)

if not hasattr(IPython.get_ipython(), "kernel"):
return
kernel = IPython.get_ipython().kernel
if kernel.implementation == "ipython":
nest_asyncio.apply()

@traitlets.observe("file_info")
def _file_info_changed(self, _):
# @property
# def file_info(self):
# warnings.warn("file_info is deprecated, use v_model instead", DeprecationWarning)
# return self.v_model

# @file_info.setter
# def file_info(self, _):
# warnings.warn("file_info is deprecated and cannot be set on FileInput")

@property
def show_progress(self):
warnings.warn("show_progress is deprecated, use v_slots to change the progress slot", DeprecationWarning)
return True

@show_progress.setter
def show_progress(self, _):
warnings.warn("show_progress is deprecated, use v_slots to change the progress slot", DeprecationWarning)

@traitlets.observe("v_model")
def _v_model_changed(self, _):
self.version += 1
self.reset_stats()

Expand All @@ -190,8 +363,8 @@ def update_stats(self, file_index, bytes_read):

def get_files(self, timeout=30):
files = []
for index, file in enumerate(self.file_info):
file = copy.deepcopy(self.file_info[index])
for index, file in enumerate(self.v_model):
file = copy.deepcopy(self.v_model[index])
file["file_obj"] = ClientSideFile(self, index, timeout=timeout)
files.append(file)
return files
Expand All @@ -201,10 +374,10 @@ def clear(self):
self.send({"method": "clear", "args": []})

def reset_stats(self):
self.stats = [0 for _ in self.file_info]
self.stats = [0 for _ in self.v_model]
self.total_progress = 0
self.total_progress_inner = 0
self.total_size_inner = sum([f["size"] for f in self.file_info])
self.total_size_inner = sum([f["size"] for f in self.v_model])

def vue_upload(self, content, buffers):
listener_id = content["id"]
Expand Down
Loading

0 comments on commit 3d93213

Please sign in to comment.