Skip to content

Instantly share code, notes, and snippets.

@gottadiveintopython
Last active October 20, 2019 14:56
Show Gist options
  • Select an option

  • Save gottadiveintopython/1ecdd982cba3d918c7fdd7452966fc28 to your computer and use it in GitHub Desktop.

Select an option

Save gottadiveintopython/1ecdd982cba3d918c7fdd7452966fc28 to your computer and use it in GitHub Desktop.
simple and fast FileChooser
import typing
from pathlib import Path
from dataclasses import dataclass
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.factory import Factory
from kivy.properties import ObjectProperty, BooleanProperty
@dataclass
class ItemData:
path: Path
is_selected: bool = False
Builder.load_string('''
<FastFileChooser>:
orientation: 'vertical'
padding: dp(5)
spacing: dp(5)
BoxLayout:
spacing: dp(5)
size_hint_y: None
height: self.minimum_height
Button:
size_hint: None, None
size: sp(30), sp(30)
id: button_move_to_parent
text: '^'
on_press: root.current_dir = root.current_dir.parent
Label:
text: str(root.current_dir)
size_hint_y: None
height: self.texture_size[1]
RecycleView:
id: rv
viewclass: 'FastFileChooserItem'
RecycleBoxLayout:
orientation: 'vertical'
size_hint_y: None
size_hint_x: 1
height: self.minimum_height
default_size_hint: 1, None
default_size: 0, sp(25)
<FastFileChooserItem>:
spacing: dp(10)
canvas:
Color:
rgba:
((1, 1, 0, .2) if
(self.data is not None and self.data.is_selected)
else (0, 0, 0, 0))
Rectangle:
pos: self.pos
size: self.size
Label:
size_hint_x: None
width: sp(20)
text:'>' if (root.data is not None) and root.data.path.is_dir() else ''
Label:
size_hint_x: None
width: self.texture_size[0]
text:'' if root.data is None else root.data.path.name
''')
def is_root_path(path:Path) -> bool:
from kivy.utils import platform
abs_str = str(path.resolve())
if platform == 'win':
if len(abs_str) <= 3 and abs_str[1] == ':':
return True
else:
if abs_str == '/':
return True
return False
class FastFileChooser(Factory.BoxLayout):
multiselect = BooleanProperty(False)
current_dir = ObjectProperty()
def on_current_dir(self, __, new_current_dir):
if not self.ids:
Clock.schedule_once(lambda dt: self.on_current_dir(__, new_current_dir))
return
new_current_dir = new_current_dir.resolve()
try:
paths = sorted(
# (child for child in new_current_dir.iterdir()),
(child for child in new_current_dir.iterdir() if child.is_dir() or child.is_file()),
key=lambda p: p.name.lower())
except PermissionError:
Factory.Popup(
size_hint=(.5, .5),
title='Error',
content=Factory.Label(
text="Access denied!")
).open()
return
self.current_dir = new_current_dir
self.ids.button_move_to_parent.disabled = is_root_path(new_current_dir)
self.ids.rv.data = ({'data': ItemData(path=path) } for index, path in enumerate(paths))
def get_selection(self) -> typing.Iterable[Path]:
return (dict_['data'].path for dict_ in self.ids.rv.data if dict_['data'].is_selected)
class FastFileChooserItem(Factory.ButtonBehavior, Factory.BoxLayout):
data = ObjectProperty(rebind=True)
@property
def rv(self):
return self.parent.parent
@property
def fc(self):
return self.parent.parent.parent
def on_press(self):
rv = self.rv
data = self.data
path = data.path
fc = self.fc
if path.is_file():
if data.is_selected:
data.is_selected = False
else:
if not fc.multiselect:
for dict_ in rv.data:
dict_['data'].is_selected = False
data.is_selected = True
rv.refresh_from_data()
self.property('data').dispatch(self)
elif path.is_dir():
fc.current_dir = path
def _test():
from kivy.app import runTouchApp
ffc = FastFileChooser(multiselect=False, current_dir=Path())
runTouchApp(ffc)
for path in ffc.get_selection():
print(path)
if __name__ == "__main__":
_test()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment