def export_finish(self):
        app = App.get_running_app()
        if len(self.collage.images) > 8:
            message = 'Exporting Collage, This May Take Several Minutes, Please Wait...'
        else:
            message = 'Exporting Collage, Please Wait...'
        self.popup = ScanningPopup(title=message, auto_dismiss=False, size_hint=(None, None), size=(app.popup_x, app.button_scale * 4))
        self.popup.scanning_text = ''
        self.popup.button_text = 'Ok'
        self.popup.open()

        self.exportthread = threading.Thread(target=self.export_process)
        self.exportthread.start()
 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 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 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()
class CollageScreen(Screen):
    #Display variables
    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
    collage_type = StringProperty('Pile')

    sort_reverse_button = StringProperty('normal')
    resolution = StringProperty('Medium')
    aspect = NumericProperty(1.3333)
    aspect_text = StringProperty('4:3')
    filename = StringProperty('')
    export_scale = 1
    collage_background = ListProperty([0, 0, 0, 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()
    collage_type_select = ObjectProperty()
    add_remove = ObjectProperty()
    collage = ObjectProperty()
    exportthread = ObjectProperty()

    #Variables relating to the photo list view on the left
    sort_method = StringProperty('Name')  #Current album sort method
    sort_reverse = BooleanProperty(False)

    def on_collage_background(self, *_):
        self.collage.collage_background = self.collage_background

    def deselect_images(self):
        self.collage.deselect_images()

    def delete_selected(self):
        self.collage.delete_selected()

    def clear_collage(self):
        self.collage.clear()

    def add_all(self):
        self.collage.add_photos(list(self.photos))

    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()
        if len(self.collage.images) > 8:
            message = 'Exporting Collage, This May Take Several Minutes, Please Wait...'
        else:
            message = 'Exporting Collage, Please Wait...'
        self.popup = ScanningPopup(title=message, auto_dismiss=False, size_hint=(None, None), size=(app.popup_x, app.button_scale * 4))
        self.popup.scanning_text = ''
        self.popup.button_text = 'Ok'
        self.popup.open()

        self.exportthread = threading.Thread(target=self.export_process)
        self.exportthread.start()

    def export_process(self, *_):
        scanning = 0
        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.collage.images:
                scanning = scanning + 10
                if scanning > 100:
                    scanning = 0
                self.popup.scanning_percentage = scanning
                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:
                scanning = scanning + 10
                if scanning > 100:
                    scanning = 0
                self.popup.scanning_percentage = scanning
                if image.is_full_size:
                    check_images.remove(image)

        self.collage.show_guides(False)

        #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.collage
        exported = self.export_scaled_jpg(collage, self.filename, image_scale=self.export_scale)
        app = App.get_running_app()
        self.popup.dismiss()
        self.popup = None
        if exported is True:
            app.message("Exported "+self.filename)
        else:
            app.message('Export error: '+exported)
        self.collage.show_guides(True)

    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):
        self.collage.transform_mode = transform_mode

    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."""

        position = self.collage.to_widget(*position)
        self.collage.drop_image(fullpath, position, aspect=aspect)

    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:
            if self.popup.title.startswith('Exporting Collage'):
                return False
            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_collage_type(self, *_):
        self.set_collage()

    def set_collage(self):
        if self.collage:
            self.collage.clear()
        collage_holder = self.ids['collageHolder']
        collage_holder.clear_widgets()
        if self.collage_type == 'Pile':
            self.collage = ScatterCollage()
        elif self.collage_type == '3':
            self.collage = GridCollage3()
        elif self.collage_type == '2x2':
            self.collage = GridCollage2x2()
        elif self.collage_type == '5':
            self.collage = GridCollage5()
        elif self.collage_type == '2x3':
            self.collage = GridCollage2x3()
        elif self.collage_type == '3x2':
            self.collage = GridCollage3x2()
        elif self.collage_type == '3x3':
            self.collage = GridCollage3x3()
        self.collage.collage_background = self.collage_background
        collage_holder.add_widget(self.collage)

    def on_leave(self):
        """Called when the screen is left.  Clean up some things."""

        app = App.get_running_app()
        app.clear_drags()
        self.clear_collage()
        collage_holder = self.ids['collageHolder']
        collage_holder.clear_widgets()
        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.set_collage()
        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)
        self.collage_type_select = CollageTypeDropDown(owner=self)

        #import variables
        self.target = app.export_target
        self.type = app.export_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()