Skip to content

Instantly share code, notes, and snippets.

@gottadiveintopython
Last active October 23, 2019 05:48
Show Gist options
  • Select an option

  • Save gottadiveintopython/445cd3a69507e7382544bc4cc1a9391c to your computer and use it in GitHub Desktop.

Select an option

Save gottadiveintopython/445cd3a69507e7382544bc4cc1a9391c to your computer and use it in GitHub Desktop.
Fast FileChooser (KivyMD)
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
try:
from kivymd.uix.dialog import MDDialog
from kivymd.uix.label import MDLabel
except ImportError:
from kivymd.dialog import MDDialog
from kivymd.label import MDLabel
@dataclass
class ItemData:
path: Path
is_selected: bool = False
default_item_data = ItemData(path=Path())
Builder.load_string('''
#:import md_icons kivymd.icon_definitions.md_icons
<MDFastFileChooser>:
orientation: 'vertical'
padding: dp(5)
spacing: dp(5)
BoxLayout:
spacing: dp(5)
size_hint_y: None
height: self.minimum_height
MDLabel:
font_style: 'Icon'
size_hint: None, None
size: self.texture_size
text_size: None, None
id: button_move_to_parent
text: md_icons['arrow-up-bold']
# color: app.theme_cls.accent_color
on_touch_down: if self.collide_point(*args[1].opos): root.current_dir = root.current_dir.parent
pos_hint: {'center_y': .5, }
MDLabel:
text: str(root.current_dir)
size_hint_y: None
height: self.texture_size[1]
pos_hint: {'center_y': .5, }
RecycleView:
id: rv
viewclass: 'MDFastFileChooserItem'
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)
<MDFastFileChooserItem>:
spacing: dp(10)
canvas:
Color:
rgba: app.theme_cls.accent_color if self.data.is_selected else (0, 0, 0, 0)
Rectangle:
pos: self.pos
size: self.size
MDLabel:
font_style: 'Icon'
size_hint: None, None
text_size: None, None
width: sp(24)
text: md_icons['folder'] if root.data.path.is_dir() else ''
pos_hint: {'center_y': .5, }
MDLabel:
font_style: 'Body1'
size_hint_x: 1
text_size: None, None
text: root.data.path.name
pos_hint: {'center_y': .5, }
''')
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 MDFastFileChooser(Factory.BoxLayout):
current_dir = ObjectProperty()
multiselect = BooleanProperty(False)
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:
dialog = MDDialog(
title="Error",
content=MDLabel(
font_style='Body1',
text='Access denied!'
),
size_hint=(.8, .2),
auto_dismiss=True,
)
dialog.open()
Clock.schedule_once(lambda __: setattr(self, 'current_dir', new_current_dir.parent))
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 MDFastFileChooserItem(Factory.ButtonBehavior, Factory.BoxLayout):
data = ObjectProperty(default_item_data, 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
for sibling in self.parent.children:
sibling.property('data').dispatch(sibling)
elif path.is_dir():
fc.current_dir = path
if __name__ == "__main__":
from kivy.app import App
from kivy.properties import ObjectProperty
from kivymd.theming import ThemeManager
class TestApp(App):
theme_cls = ThemeManager()
def build(self):
return MDFastFileChooser(multiselect=True, current_dir=Path())
def on_stop(self):
for path in self.root.get_selection():
print(path)
TestApp().run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment