Last active
October 23, 2019 05:48
-
-
Save gottadiveintopython/445cd3a69507e7382544bc4cc1a9391c to your computer and use it in GitHub Desktop.
Fast FileChooser (KivyMD)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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