class SettingString(SettingItem): popup = ObjectProperty(None, allownone=True) textinput = ObjectProperty(None) def on_panel(self, instance, value): if value is None: return self.fbind('on_release', self._create_popup) def dismiss(self, *largs): if self.popup: self.popup.dismiss() app = App.get_running_app() if app.popup: app.popup = None self.popup = None def _validate(self, instance, answer): value = self.popup.content.ids['input'].text.strip() self.dismiss() if answer == 'yes': self.value = value def _create_popup(self, instance): content = InputPopup(text='', input_text=self.value) app = App.get_running_app() content.bind(on_answer=self._validate) self.popup = NormalPopup(title=self.title, content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 5), auto_dismiss=True) app = App.get_running_app() app.popup = self.popup self.popup.open()
def add_folder(self): """Starts the add folder process, creates an input text popup.""" content = InputPopup(hint='Folder Name', text='Enter A Folder Name:') app = App.get_running_app() content.bind(on_answer=self.add_folder_answer) self.popup = NormalPopup(title='Create Folder', content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 5), auto_dismiss=False) self.popup.open()
def delete_folder(self): """Starts the delete folder process, creates the confirmation popup.""" app = App.get_running_app() text = "Delete The Selected Folder?" content = ConfirmPopup(text=text, yes_text='Delete', no_text="Don't Delete", warn_yes=True) content.bind(on_answer=self.delete_folder_answer) self.popup = NormalPopup(title='Confirm Delete', content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 4), auto_dismiss=False) self.popup.open()
def load_theme(self): content = FileBrowser(ok_text='Load', file_editable=True, export_mode=False, file='theme.txt') content.bind(on_cancel=self.dismiss_popup) content.bind(on_ok=self.load_theme_finish) self.popup = NormalPopup(title="Select Theme To Load", content=content, size_hint=(0.9, 0.9)) self.popup.open()
def save_theme(self): content = FileBrowser(ok_text='Save', file_editable=True, export_mode=True, file='theme.txt') content.bind(on_cancel=self.dismiss_popup) content.bind(on_ok=self.save_theme_check) self.popup = NormalPopup(title="Select File To Save Theme To", content=content, size_hint=(0.9, 0.9)) self.popup.open()
def filechooser_popup(self): content = FileBrowser(ok_text='Export', directory_select=False, file_editable=True, export_mode=True, file='collage.jpg') content.bind(on_cancel=self.dismiss_popup) content.bind(on_ok=self.export_check) self.popup = NormalPopup(title="Select File To Export To", content=content, size_hint=(0.9, 0.9)) self.popup.open()
def _create_popup(self, instance): content = InputPopup(text='', input_text=self.value) app = App.get_running_app() content.bind(on_answer=self._validate) self.popup = NormalPopup(title=self.title, content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 5), auto_dismiss=True) app = App.get_running_app() app.popup = self.popup self.popup.open()
def export_finish(self): app = App.get_running_app() content = MessagePopup(text='Exporting Collage') if len(self.images) > 8: message = 'Exporting Collage, This May Take Several Minutes, Please Wait...' else: message = 'Exporting Collage, Please Wait...' self.popup = NormalPopup(title=message, content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 4)) self.popup.open() #Wait a cycle so the popup can display Clock.schedule_once(self.export_process)
def filechooser_popup(self): app = App.get_running_app() content = FileBrowser(ok_text='Export', path=app.last_browse_folder, file_editable=True, export_mode=True, file='collage.jpg') content.bind(on_cancel=self.dismiss_popup) content.bind(on_ok=self.export_check) self.popup = NormalPopup(title="Select File To Export To", content=content, size_hint=(0.9, 0.9)) self.popup.open()
def filechooser_popup(self): content = FileBrowser(ok_text='Add', directory_select=True) content.bind(on_cancel=self.filepopup_dismiss) content.bind(on_ok=self.add_directory) self.filepopup = filepopup = NormalPopup(title=self.title, content=content, size_hint=(0.9, 0.9)) filepopup.open()
def save_crashlog(self): app = App.get_running_app() app.dismiss_popup() from filebrowser import FileBrowser content = FileBrowser(ok_text='Save', directory_select=True) content.bind(on_cancel=app.dismiss_popup) content.bind(on_ok=self.save_crashlog_finish) app.popup = NormalPopup(title="Select A Location To Save Crashlog", content=content) app.popup.open()
def filechooser_popup(self): app = App.get_running_app() content = FileBrowser(ok_text='Add', path=app.last_browse_folder, directory_select=True) content.bind(on_cancel=self.filepopup_dismiss) content.bind(on_ok=self.add_directory) self.filepopup = filepopup = NormalPopup(title=self.title, content=content, size_hint=(0.9, 0.9)) filepopup.open()
def save_theme_check(self, *_): path = self.popup.content.path file = self.popup.content.file self.dismiss_popup() self.filename = os.path.join(path, file) if os.path.isfile(self.filename): app = App.get_running_app() content = ConfirmPopup(text='Overwrite the file "' + self.filename + '"?', yes_text='Overwrite', no_text="Cancel", warn_yes=True) content.bind(on_answer=self.save_theme_finish) self.popup = NormalPopup(title='Confirm Overwrite', content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 4), auto_dismiss=False) self.popup.open() else: self.save_theme_finish()
def export_check(self, *_): popup = self.popup if popup: path = popup.content.path app = App.get_running_app() app.last_browse_folder = path file = popup.content.file self.dismiss_popup() if not file.lower().endswith('.jpg'): file = file+'.jpg' self.filename = os.path.join(path, file) if os.path.isfile(self.filename): content = ConfirmPopup(text='Overwrite the file "'+self.filename+'"?', yes_text='Overwrite', no_text="Cancel", warn_yes=True) content.bind(on_answer=self.export_overwrite_answer) self.popup = NormalPopup(title='Confirm Overwrite', content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 4), auto_dismiss=False) self.popup.open() else: self.export_finish()
def _create_popup(self, *_): app = App.get_running_app() if app.database_scanning: return content = BoxLayout(orientation='vertical') popup_width = min(0.95 * Window.width, dp(500)) self.popup = popup = NormalPopup(title=self.title, content=content, size_hint=(None, 0.9), width=popup_width) if not self.value: content.add_widget( ShortLabel( height=app.button_scale * 3, text= "You must set at least one database directory.\n\nThis is a folder where your photos are stored.\nNew photos will be imported to a database folder." )) content.add_widget(BoxLayout()) else: folders = filter(None, self.value.split(';')) folderdata = [] for folder in folders: folderdata.append({'text': folder}) self.folderlist = folderlist = FolderSettingsList(size_hint=(1, .8), id='folderlist') folderlist.data = folderdata content.add_widget(folderlist) buttons = BoxLayout(orientation='horizontal', size_hint=(1, None), height=app.button_scale) addbutton = NormalButton(text='+') addbutton.bind(on_release=self.add_path) removebutton = NormalButton(text='-') removebutton.bind(on_release=self.remove_path) okbutton = WideButton(text='OK') okbutton.bind(on_release=self._dismiss) buttons.add_widget(addbutton) buttons.add_widget(removebutton) buttons.add_widget(okbutton) content.add_widget(buttons) popup.open()
class CollageScreen(Screen): sort_reverse_button = StringProperty('normal') images = [] collage_background = ListProperty([0, 0, 0, 1]) resolution = StringProperty('Medium') aspect = NumericProperty(1.3333) aspect_text = StringProperty('4:3') filename = StringProperty('') export_scale = 1 #Widget holder variables sort_dropdown = ObjectProperty() #Holder for the sort method dropdown menu popup = None #Holder for the screen's popup dialog resolution_select = ObjectProperty() color_select = ObjectProperty() aspect_select = ObjectProperty() add_remove = ObjectProperty() #Variables relating to the photo list view on the left selected = StringProperty( '') #The current folder/album/tag being displayed type = StringProperty('None') #'Folder', 'Album', 'Tag' target = StringProperty( ) #The identifier of the album/folder/tag that is being viewed photos = [] #Photoinfo of all photos in the album sort_method = StringProperty('Name') #Current album sort method sort_reverse = BooleanProperty(False) def deselect_images(self): collage = self.ids['collageHolder'] for image in collage.children: image.selected = False def delete_selected(self): collage = self.ids['collageHolder'] for image in collage.children: if image.selected: collage.remove_widget(image) def clear_collage(self): collage = self.ids['collageHolder'] collage.clear_widgets() self.images = [] self.collage_background = [0, 0, 0, 1] def add_all(self): #adds all photos to the collage using a fimonacci spiral pattern collage = self.ids['collageHolder'] size = (1 / (len(self.photos)**0.5)) #average scale of photo photos = list(self.photos) random.shuffle(photos) tau = (1 + 5**0.5) / 2 inc = (2 - tau) * 2 * math.pi theta = 0 max_x = 0 max_y = 0 coords = [] offset_scale = .5 app = App.get_running_app() app.message("Added " + str(len(photos)) + " images.") #Generate basis coordinates and determine min/max for index in range(0, len(photos)): offset = (random.random() * offset_scale) - ( .5 * offset_scale) #random angle variation r = index**0.5 theta = theta + inc + offset pos_x = 0.5 + r * math.cos(theta) if abs(pos_x) > max_x: max_x = abs(pos_x) pos_y = 0.5 + r * math.sin(theta) if abs(pos_y) > max_y: max_y = abs(pos_y) coords.append((pos_x, pos_y)) #add photos to collage for index, photo in enumerate(photos): rand_angle = random.randint(-33, 33) pos_x, pos_y = coords[index] #scale points down by max size pos_x = pos_x / max_x pos_y = pos_y / max_y #scale points down to prevent photos overlapping edges pos_x = pos_x * (1 - (size / 2)) pos_y = pos_y * (1 - (size / 2)) #convert to kivy's coordinate system pos_x = (pos_x + 1) / 2 pos_y = (pos_y + 1) / 2 #offset points to correct for photo size pos_x = pos_x - (size / 2) pos_y = pos_y - (size / 2) position = (collage.width * pos_x, collage.height * pos_y) #forces lowmem mode if more than a certain number of photos if len(photos) > 8: lowmem = True else: lowmem = False self.add_collage_image(collage, photo[0], position, size=size, angle=rand_angle, lowmem=lowmem) def export(self): self.deselect_images() self.filechooser_popup() def filechooser_popup(self): content = FileBrowser(ok_text='Export', directory_select=False, file_editable=True, export_mode=True, file='collage.jpg') content.bind(on_cancel=self.dismiss_popup) content.bind(on_ok=self.export_check) self.popup = NormalPopup(title="Select File To Export To", content=content, size_hint=(0.9, 0.9)) self.popup.open() def export_check(self, *_): path = self.popup.content.path file = self.popup.content.file self.dismiss_popup() if not file.lower().endswith('.jpg'): file = file + '.jpg' self.filename = os.path.join(path, file) if os.path.isfile(self.filename): app = App.get_running_app() content = ConfirmPopup(text='Overwrite the file "' + self.filename + '"?', yes_text='Overwrite', no_text="Cancel", warn_yes=True) content.bind(on_answer=self.export_overwrite_answer) self.popup = NormalPopup(title='Confirm Overwrite', content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 4), auto_dismiss=False) self.popup.open() else: self.export_finish() def export_overwrite_answer(self, instance, answer): del instance if answer == 'yes': self.dismiss_popup() self.export_finish() def export_finish(self): app = App.get_running_app() content = MessagePopup(text='Exporting Collage') if len(self.images) > 8: message = 'Exporting Collage, This May Take Several Minutes, Please Wait...' else: message = 'Exporting Collage, Please Wait...' self.popup = NormalPopup(title=message, content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 4)) self.popup.open() #Wait a cycle so the popup can display Clock.schedule_once(self.export_process) def export_process(self, *_): if self.resolution == 'High': self.export_scale = 4 elif self.resolution == 'Low': self.export_scale = 1 else: self.export_scale = 2 #wait for full sized images to load check_images = [] if self.export_scale > 1: for image in self.images: async_image = image.children[0].children[0] if not async_image.is_full_size: async_image.loadfullsize = True async_image._load_fullsize() check_images.append(async_image) while check_images: for image in check_images: if image.is_full_size: check_images.remove(image) #wait a cycle so kivy can finish displaying the textures Clock.schedule_once(self.export_collage_as_image) def export_collage_as_image(self, *_): collage = self.ids['collageHolder'] exported = self.export_scaled_jpg(collage, self.filename, image_scale=self.export_scale) app = App.get_running_app() self.dismiss_popup() if exported is True: app.message("Exported " + self.filename) else: app.message('Export error: ' + exported) def export_scaled_jpg(self, widget, filename, image_scale=1): from kivy.graphics import (Translate, Fbo, ClearColor, ClearBuffers, Scale) re_size = (widget.width * image_scale, widget.height * image_scale) if widget.parent is not None: canvas_parent_index = widget.parent.canvas.indexof(widget.canvas) if canvas_parent_index > -1: widget.parent.canvas.remove(widget.canvas) try: fbo = Fbo(size=re_size, with_stencilbuffer=True) with fbo: ClearColor(0, 0, 0, 0) ClearBuffers() Scale(image_scale, -image_scale, image_scale) Translate(-widget.x, -widget.y - widget.height, 0) fbo.add(widget.canvas) fbo.draw() from io import BytesIO image_bytes = BytesIO() fbo.texture.save(image_bytes, flipped=False, fmt='png') image_bytes.seek(0) from PIL import Image image = Image.open(image_bytes) image = image.convert('RGB') image.save(filename) exported = True except Exception as ex: exported = str(ex) try: fbo.remove(widget.canvas) except: pass if widget.parent is not None and canvas_parent_index > -1: widget.parent.canvas.insert(canvas_parent_index, widget.canvas) return exported def change_transform(self, transform_mode): for container in self.images: if transform_mode == 'rotscale': container.do_rotation = True container.do_scale = True container.do_translation = False elif transform_mode == 'rotate': container.do_rotation = True container.do_scale = False container.do_translation = False elif transform_mode == 'scale': container.do_rotation = False container.do_scale = True container.do_translation = False else: container.do_rotation = True container.do_scale = True container.do_translation = True def on_sort_reverse(self, *_): """Updates the sort reverse button's state variable, since kivy doesnt just use True/False for button states.""" app = App.get_running_app() self.sort_reverse_button = 'down' if to_bool( app.config.get('Sorting', 'album_sort_reverse')) else 'normal' def drop_widget(self, fullpath, position, dropped_type='file', aspect=1): """Called when a widget is dropped. Determine photo dragged, and where it needs to go.""" collage = self.ids['collageHolder'] position = collage.to_widget(*position) self.add_collage_image(collage, fullpath, position, aspect=aspect) def add_collage_image(self, collage, fullpath, position, size=0.5, angle=0, lowmem=False, aspect=1): if not lowmem: if len(self.images) > 8: lowmem = True if collage.collide_point(*position): self.deselect_images() app = App.get_running_app() photoinfo = app.database_exists(fullpath) file = os.path.join(photoinfo[2], photoinfo[0]) orientation = photoinfo[13] if orientation == 3 or orientation == 4: angle_offset = 180 elif orientation == 5 or orientation == 6: angle_offset = 270 elif orientation == 7 or orientation == 8: angle_offset = 90 else: angle_offset = 0 if orientation in [2, 4, 5, 7]: mirror = True else: mirror = False width = collage.width image_holder = ScatterImage(owner=self, source=file, rotation=angle + angle_offset, mirror=mirror, image_angle=0, photoinfo=photoinfo, lowmem=lowmem, aspect=aspect) image_holder.scale = size image_holder.selected = True if aspect < 1: image_holder.width = width * aspect image_holder.height = width else: image_holder.width = width image_holder.height = width / aspect self.images.append(image_holder) image_holder.pos = (position[0] - (width * size / 2), position[1] - (width * size / 2)) collage.add_widget(image_holder) def show_selected(self): """Scrolls the treeview to the currently selected folder""" database = self.ids['albumContainer'] database_interior = self.ids['album'] selected = self.selected data = database.data current_folder = None for i, node in enumerate(data): if node['target'] == selected and node['type'] == self.type: current_folder = node break if current_folder is not None: database_interior.selected = current_folder database.scroll_to_selected() def text_input_active(self): """Detects if any text input fields are currently active (being typed in). Returns: True or False """ input_active = False for widget in self.walk(restrict=True): if widget.__class__.__name__ == 'NormalInput' or widget.__class__.__name__ == 'FloatInput' or widget.__class__.__name__ == 'IntegerInput': if widget.focus: input_active = True break return input_active def has_popup(self): """Detects if the current screen has a popup active. Returns: True or False """ if self.popup: if self.popup.open: return True return False def dismiss_popup(self, *_): """Close a currently open popup for this screen.""" if self.popup: self.popup.dismiss() self.popup = None def dismiss_extra(self): """Deactivates running processes if applicable. Returns: True if something was deactivated, False if not. """ return False def key(self, key): """Handles keyboard shortcuts, performs the actions needed. Argument: key: The name of the key command to perform. """ if self.text_input_active(): pass else: if not self.popup or (not self.popup.open): #normal keypresses pass elif self.popup and self.popup.open: if key == 'enter': self.popup.content.dispatch('on_answer', 'yes') def scroll_photolist(self): """Scroll the right-side photo list to the current active photo.""" photolist = self.ids['albumContainer'] self.show_selected() photolist.scroll_to_selected() def refresh_all(self): self.refresh_photolist() self.refresh_photoview() def update_selected(self): pass def refresh_photolist(self): """Reloads and sorts the photo list""" app = App.get_running_app() #Get photo list self.photos = [] if self.type == 'Album': for albuminfo in app.albums: if albuminfo['name'] == self.target: photo_paths = albuminfo['photos'] for fullpath in photo_paths: photoinfo = app.database_exists(fullpath) if photoinfo: self.photos.append(photoinfo) elif self.type == 'Tag': self.photos = app.database_get_tag(self.target) else: self.photos = app.database_get_folder(self.target) #Remove video files temp_photos = [] for photo in self.photos: source = os.path.join(photo[2], photo[0]) isvideo = os.path.splitext(source)[1].lower() in movietypes if not isvideo: temp_photos.append(photo) self.photos = temp_photos #Sort photos if self.sort_method == 'Imported': sorted_photos = sorted(self.photos, key=lambda x: x[6], reverse=self.sort_reverse) elif self.sort_method == 'Modified': sorted_photos = sorted(self.photos, key=lambda x: x[7], reverse=self.sort_reverse) elif self.sort_method == 'Owner': sorted_photos = sorted(self.photos, key=lambda x: x[11], reverse=self.sort_reverse) elif self.sort_method == 'Name': sorted_photos = sorted(self.photos, key=lambda x: os.path.basename(x[0]), reverse=self.sort_reverse) else: sorted_photos = sorted(self.photos, key=lambda x: x[0], reverse=self.sort_reverse) self.photos = sorted_photos def refresh_photoview(self): #refresh recycleview photolist = self.ids['albumContainer'] photodatas = [] for photo in self.photos: source = os.path.join(photo[2], photo[0]) filename = os.path.basename(photo[0]) photodata = { 'text': filename, 'fullpath': photo[0], 'temporary': True, 'photoinfo': photo, 'folder': self.selected, 'database_folder': photo[2], 'filename': filename, 'target': self.selected, 'type': self.type, 'owner': self, 'video': False, 'photo_orientation': photo[13], 'source': source, 'title': photo[10], 'selected': False, 'selectable': True, 'dragable': True } photodatas.append(photodata) photolist.data = photodatas def clear_photolist(self): photolist = self.ids['albumContainer'] photolist.data = [] def resort_method(self, method): """Sets the album sort method. Argument: method: String, the sort method to use """ self.sort_method = method app = App.get_running_app() app.config.set('Sorting', 'album_sort', method) self.refresh_all() Clock.schedule_once(lambda *dt: self.scroll_photolist()) def resort_reverse(self, reverse): """Sets the album sort reverse. Argument: reverse: String, if 'down', reverse will be enabled, disabled on any other string. """ app = App.get_running_app() sort_reverse = True if reverse == 'down' else False app.config.set('Sorting', 'album_sort_reverse', sort_reverse) self.sort_reverse = sort_reverse self.refresh_all() Clock.schedule_once(lambda *dt: self.scroll_photolist()) def on_leave(self): """Called when the screen is left. Clean up some things.""" self.clear_collage() self.clear_photolist() def on_enter(self): """Called when the screen is entered. Set up variables and widgets, and prepare to view images.""" app = App.get_running_app() self.ids['leftpanel'].width = app.left_panel_width() self.ids['moveButton'].state = 'down' self.ids['rotateButton'].state = 'normal' self.color_select = ColorDropDown(owner=self) self.resolution_select = ResolutionDropDown(owner=self) self.aspect_select = ExportAspectRatioDropDown(owner=self) self.add_remove = AddRemoveDropDown(owner=self) #import variables self.target = app.target self.type = app.type #set up sort buttons self.sort_dropdown = AlbumSortDropDown() self.sort_dropdown.bind( on_select=lambda instance, x: self.resort_method(x)) self.sort_method = app.config.get('Sorting', 'album_sort') self.sort_reverse = to_bool( app.config.get('Sorting', 'album_sort_reverse')) #refresh views self.refresh_photolist() self.refresh_photoview()
class FileBrowser(FloatLayout): __events__ = ('on_cancel', 'on_ok') path = StringProperty() file = StringProperty() files = ListProperty() folder_files = ListProperty() filename = StringProperty() root = StringProperty() allow_no_file = BooleanProperty(False) clickfade_object = ObjectProperty(allownone=True) popup = ObjectProperty(None, allownone=True) remember = None can_delete_folder = BooleanProperty(False) multiselect = BooleanProperty(False) new_folder = StringProperty('') start_in = StringProperty() directory_select = BooleanProperty(False) file_editable = BooleanProperty(False) filters = ListProperty() target_selected = BooleanProperty(False) export_mode = BooleanProperty(False) autoselect = BooleanProperty(False) header_text = StringProperty('Select A File') cancel_text = StringProperty('Cancel') ok_text = StringProperty('OK') def __init__(self, **kwargs): if not self.start_in: self.start_in = '/' Clock.schedule_once(self.refresh_locations) self.clickfade_object = ClickFade() super(FileBrowser, self).__init__(**kwargs) def clickfade(self, widget): try: self.remove_widget(self.clickfade_object) except: pass self.clickfade_object.size = widget.size self.clickfade_object.pos = widget.to_window(*widget.pos) self.clickfade_object.begin() self.add_widget(self.clickfade_object) def toggle_select(self): file_list = self.ids['files'] if self.multiselect: file_list.toggle_select() def get_selected(self): file_list = self.ids['files'] selected = file_list.selects return selected def dismiss_popup(self, *_): """If this dialog has a popup, closes it and removes it.""" if self.popup: self.popup.dismiss() self.popup = None def add_folder(self): """Starts the add folder process, creates an input text popup.""" content = InputPopup(hint='Folder Name', text='Enter A Folder Name:') app = App.get_running_app() content.bind(on_answer=self.add_folder_answer) self.popup = NormalPopup(title='Create Folder', content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 5), auto_dismiss=False) self.popup.open() def add_folder_answer(self, instance, answer): """Tells the app to rename the folder if the dialog is confirmed. Arguments: instance: The dialog that called this function. answer: String, if 'yes', the folder will be created, all other answers will just close the dialog. """ if answer == 'yes': text = instance.ids['input'].text.strip(' ') if text: app = App.get_running_app() folder = os.path.join(self.path, text) created = False try: if not os.path.isdir(folder): os.makedirs(folder) created = True except: pass if created: app.message("Created the folder '"+folder+"'") self.path = folder self.refresh_folder() else: app.message("Could Not Create Folder.") self.dismiss_popup() def delete_folder(self): """Starts the delete folder process, creates the confirmation popup.""" app = App.get_running_app() text = "Delete The Selected Folder?" content = ConfirmPopup(text=text, yes_text='Delete', no_text="Don't Delete", warn_yes=True) content.bind(on_answer=self.delete_folder_answer) self.popup = NormalPopup(title='Confirm Delete', content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 4), auto_dismiss=False) self.popup.open() def delete_folder_answer(self, instance, answer): """Tells the app to delete the folder if the dialog is confirmed. Arguments: instance: The dialog that called this function. answer: String, if 'yes', the folder will be deleted, all other answers will just close the dialog. """ del instance if answer == 'yes': app = App.get_running_app() try: os.rmdir(self.path) app.message("Deleted Folder: \""+self.path+"\"") self.go_up() except: app.message("Could Not Delete Folder...") self.dismiss_popup() def reset_folder_position(self, *_): filelist = self.ids['fileList'] filelist.scroll_y = 1 def refresh_locations(self, *_): locations_list = self.ids['locationsList'] locations = get_drives() self.root = locations[0][0] data = [] for location in locations: data.append({ 'text': location[1], 'fullpath': location[0], 'path': location[0], 'type': 'folder', 'is_folder': True, 'owner': self, 'selectable': False }) locations_list.data = data if not self.path: self.path = locations[0][0] self.refresh_folder() def refresh_folder(self, *_): file_list = self.ids['fileList'] files = self.ids['files'] files.selects = [] data = [] files = [] dirs = [] walk = os.walk for root, list_dirs, list_files in walk(self.path, topdown=True): dirs = list_dirs[:] list_dirs.clear() files = list_files self.folder_files = files if dirs or files: self.can_delete_folder = False else: self.can_delete_folder = True dirs = sorted(dirs, key=lambda s: s.lower()) for directory in dirs: fullpath = os.path.join(self.path, directory) data.append({ 'text': directory, 'fullpath': fullpath, 'path': fullpath + os.path.sep, 'type': 'folder', 'file': '', 'owner': self, 'is_folder': True, 'selected': False, 'multiselect': self.multiselect, 'selectable': self.directory_select, 'file_size': '', 'modified': '' }) if not self.directory_select: if self.filters: filtered_files = [] for item in self.filters: filtered_files += fnmatch.filter(files, item) files = filtered_files #files = sorted(files, key=lambda s: s.lower()) files = sort_nicely(files) for file in files: fullpath = os.path.join(self.path, file) file_size = int(os.path.getsize(fullpath)) modified = int(os.path.getmtime(fullpath)) data.append({ 'text': file, 'fullpath': fullpath, 'path': self.path, 'type': file, 'file': file, 'owner': self, 'is_folder': False, 'selected': False, 'multiselect': self.multiselect, 'selectable': True, 'file_size': format_size(file_size), 'modified': datetime.datetime.fromtimestamp(modified).strftime('%Y-%m-%d, %I:%M%p') }) file_list.data = data if not self.directory_select: if self.export_mode: if not self.file: self.target_selected = False else: self.target_selected = True else: self.file = '' self.filename = '' self.target_selected = False else: self.file = '' self.filename = '' self.target_selected = True self.reset_folder_position() if self.autoselect: self.toggle_select() def go_up(self, *_): up_path = os.path.realpath(os.path.join(self.path, '..')) if not up_path.endswith(os.path.sep): up_path += os.path.sep if up_path == self.path: up_path = self.root self.path = up_path self.refresh_folder() def double_click(self, instance): if self.target_selected and not self.export_mode: self.dispatch('on_ok') def click_node(self, node): self.clickfade(node) item = node.data if item['type'] == 'folder': self.path = item['path'] self.refresh_folder() def select_item(self, item): if item: if not self.directory_select and item['type'] != 'folder': self.filename = item['fullpath'] self.file = item['file'] self.target_selected = True else: if not self.directory_select: self.target_selected = False if not self.export_mode: self.filename = '' self.file = '' def select_items(self, items): self.files = [] for item in items: if 'file' in item: self.files.append(item['file']) def on_cancel(self): pass def on_ok(self): pass
class ThemeScreen(Screen): """Screen layout of the album viewer.""" popup = None #Holder for the screen's popup dialog theme_backup = {} filename = '' preset_drop = ObjectProperty() def back(self, *_): app = App.get_running_app() app.show_database() return True def rescale_screen(self): app = App.get_running_app() self.ids['leftpanel'].width = app.left_panel_width() def drop_widget(self, fullpath, position, dropped_type='file', aspect=1): """Dummy function. Here because the app can possibly call this function for any screen.""" pass def text_input_active(self): """Detects if any text input fields are currently active (being typed in). Returns: True or False """ input_active = False for widget in self.walk(restrict=True): if widget.__class__.__name__ == 'NormalInput' or widget.__class__.__name__ == 'FloatInput' or widget.__class__.__name__ == 'IntegerInput': if widget.focus: input_active = True break return input_active def has_popup(self): """Detects if the current screen has a popup active. Returns: True or False """ if self.popup: if self.popup.open: return True return False def dismiss_popup(self, *_): """Close a currently open popup for this screen.""" if self.popup: self.popup.dismiss() self.popup = None def dismiss_extra(self): """Close any running processes.""" return False def key(self, key): """Handles keyboard shortcuts, performs the actions needed. Argument: key: The name of the key command to perform. """ if self.text_input_active(): pass else: if not self.popup or (not self.popup.open): pass elif self.popup and self.popup.open: if key == 'enter': self.popup.content.dispatch('on_answer', 'yes') def on_leave(self): """Called when the screen is left. Clean up some things.""" app = App.get_running_app() def on_enter(self): """Called when the screen is entered. Set up variables and widgets.""" app = App.get_running_app() self.ids['leftpanel'].width = app.left_panel_width() #back up theme self.theme_backup = app.theme_to_data(app.theme) #Set up preset menu self.preset_drop = NormalDropDown() for preset in themes: menu_button = MenuButton(text=preset['name']) menu_button.bind(on_release=self.set_preset) self.preset_drop.add_widget(menu_button) def set_preset(self, instance): """Sets the current dialog preset settings to one of the presets stored in the app. Argument: index: Integer, the index of the preset to set. """ self.preset_drop.dismiss() app = App.get_running_app() for preset in themes: if preset['name'] == instance.text: app.data_to_theme(preset) self.reload_colors() app.message('Reset Theme To: ' + instance.text) break def load_theme(self): content = FileBrowser(ok_text='Load', file_editable=True, export_mode=False, file='theme.txt') content.bind(on_cancel=self.dismiss_popup) content.bind(on_ok=self.load_theme_finish) self.popup = NormalPopup(title="Select Theme To Load", content=content, size_hint=(0.9, 0.9)) self.popup.open() def load_theme_finish(self, *_): path = self.popup.content.path file = self.popup.content.file self.dismiss_popup() self.filename = os.path.join(path, file) app = App.get_running_app() theme_file = self.filename loaded, data = app.load_theme_data(theme_file) if not loaded: app.message('Could Not Load Theme: ' + str(data)) return else: app.message('Loaded Theme: ' + theme_file) self.theme_backup = data app.data_to_theme(data) self.reload_colors() def save_theme(self): content = FileBrowser(ok_text='Save', file_editable=True, export_mode=True, file='theme.txt') content.bind(on_cancel=self.dismiss_popup) content.bind(on_ok=self.save_theme_check) self.popup = NormalPopup(title="Select File To Save Theme To", content=content, size_hint=(0.9, 0.9)) self.popup.open() def save_theme_check(self, *_): path = self.popup.content.path file = self.popup.content.file self.dismiss_popup() self.filename = os.path.join(path, file) if os.path.isfile(self.filename): app = App.get_running_app() content = ConfirmPopup(text='Overwrite the file "' + self.filename + '"?', yes_text='Overwrite', no_text="Cancel", warn_yes=True) content.bind(on_answer=self.save_theme_finish) self.popup = NormalPopup(title='Confirm Overwrite', content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 4), auto_dismiss=False) self.popup.open() else: self.save_theme_finish() def save_theme_finish(self, instance=None, answer='yes'): self.dismiss_popup() if answer != 'yes': return app = App.get_running_app() theme_file = self.filename data = app.theme_to_data(app.theme) saved = app.save_theme_data(theme_file, data) if saved is True: app.message('Saved Theme') else: app.message('Could Not Save Theme: ' + str(saved)) def reset_theme(self): app = App.get_running_app() app.data_to_theme(self.theme_backup) self.reload_colors() def reload_colors(self): color_element_holder = self.ids['colorElementHolder'] for widget in color_element_holder.children: try: widget.on_color_property() if widget.expanded: widget.toggle_expanded() except: pass def theme_default(self, *_): app = App.get_running_app() app.theme_default() self.reload_colors()
class FileBrowser(BoxLayout): __events__ = ('on_cancel', 'on_ok') path = StringProperty() file = StringProperty() filename = StringProperty() root = StringProperty() popup = ObjectProperty(None, allownone=True) allow_new = BooleanProperty(True) allow_delete = BooleanProperty(True) new_folder = StringProperty('') start_in = StringProperty() directory_select = BooleanProperty(False) file_editable = BooleanProperty(False) filters = ListProperty() target_selected = BooleanProperty(False) export_mode = BooleanProperty(False) header_text = StringProperty('Select A File') cancel_text = StringProperty('Cancel') ok_text = StringProperty('OK') def __init__(self, **kwargs): if not self.start_in: self.start_in = '/' Clock.schedule_once(self.refresh_locations) super(FileBrowser, self).__init__(**kwargs) def dismiss_popup(self): """If this dialog has a popup, closes it and removes it.""" if self.popup: self.popup.dismiss() self.popup = None def add_folder(self): """Starts the add folder process, creates an input text popup.""" content = InputPopup(hint='Folder Name', text='Enter A Folder Name:') app = App.get_running_app() content.bind(on_answer=self.add_folder_answer) self.popup = NormalPopup(title='Create Folder', content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 5), auto_dismiss=False) self.popup.open() def add_folder_answer(self, instance, answer): """Tells the app to rename the folder if the dialog is confirmed. Arguments: instance: The dialog that called this function. answer: String, if 'yes', the folder will be created, all other answers will just close the dialog. """ if answer == 'yes': text = instance.ids['input'].text.strip(' ') if text: app = App.get_running_app() folder = os.path.join(self.path, text) created = False try: if not os.path.isdir(folder): os.makedirs(folder) created = True except: pass if created: app.message("Created the folder '" + folder + "'") self.path = folder self.refresh_folder() else: app.message("Could Not Create Folder.") self.dismiss_popup() def delete_folder(self): """Starts the delete folder process, creates the confirmation popup.""" app = App.get_running_app() if not os.listdir(self.path): text = "Delete The Selected Folder?" content = ConfirmPopup(text=text, yes_text='Delete', no_text="Don't Delete", warn_yes=True) content.bind(on_answer=self.delete_folder_answer) self.popup = NormalPopup(title='Confirm Delete', content=content, size_hint=(None, None), size=(app.popup_x, app.button_scale * 4), auto_dismiss=False) self.popup.open() else: app.popup_message(text='Could not delete, Folder is not empty', title='Warning') def delete_folder_answer(self, instance, answer): """Tells the app to delete the folder if the dialog is confirmed. Arguments: instance: The dialog that called this function. answer: String, if 'yes', the folder will be deleted, all other answers will just close the dialog. """ del instance if answer == 'yes': app = App.get_running_app() try: os.rmdir(self.path) app.message("Deleted Folder: \"" + self.path + "\"") self.go_up() except: app.message("Could Not Delete Folder...") self.dismiss_popup() def refresh_locations(self, *_): locations_list = self.ids['locationsList'] locations = get_drives() self.root = locations[0][0] data = [] for location in locations: data.append({ 'text': location[1], 'fullpath': location[0], 'path': location[0], 'type': 'folder', 'owner': self }) locations_list.data = data if not self.path: self.path = locations[0][0] self.refresh_folder() def refresh_folder(self, *_): file_list = self.ids['fileList'] data = [] files = [] dirs = [] try: directory_elements = os.listdir(self.path) except: directory_elements = [] for file in directory_elements: fullpath = os.path.join(self.path, file) if os.path.isfile(fullpath): files.append(file) elif os.path.isdir(fullpath): dirs.append(file) dirs = sorted(dirs, key=lambda s: s.lower()) for directory in dirs: fullpath = os.path.join(self.path, directory) data.append({ 'text': directory, 'fullpath': fullpath, 'path': fullpath + os.path.sep, 'type': 'folder', 'owner': self, 'selected': False }) if not self.directory_select: if self.filters: filtered_files = [] for item in self.filters: filtered_files += fnmatch.filter(files, item) files = filtered_files files = sorted(files, key=lambda s: s.lower()) for file in files: data.append({ 'text': file, 'fullpath': os.path.join(self.path, file), 'path': self.path, 'type': file, 'file': file, 'owner': self, 'selected': False }) file_list.data = data if not self.directory_select: if self.export_mode: if not self.file: self.target_selected = False else: self.target_selected = True else: self.file = '' self.target_selected = False else: self.filename = self.path self.target_selected = True def go_up(self, *_): up_path = os.path.realpath(os.path.join(self.path, '..')) if not up_path.endswith(os.path.sep): up_path += os.path.sep if up_path == self.path: up_path = self.root self.path = up_path self.refresh_folder() def select(self, button): if button.type == 'folder': self.path = button.path self.refresh_folder() if self.directory_select: self.filename = button.fullpath self.target_selected = True elif self.export_mode: self.target_selected = True else: self.filename = '' self.target_selected = False else: self.filename = button.fullpath self.file = button.file self.target_selected = True def on_cancel(self): pass def on_ok(self): pass