Search code examples
imagekivykivy-language

How to select an image in a screen and display it on another using kivy?


I am creating a simple, two-screen interface with Kivy for an image classification task. In the first screen, I am selecting an image using the file chooser and displaying it. In the second screen, I want to show the same image and the result of the classification task. The transition between screens is done via a button on the first screen.

My question is: When I press the button, how can I trigger an update of the image source property on the second screen, such that the chosen image will be displayed on the second screen? The classification part is just to understand the background of my question, I did not include it in the code.

Here is the main.py file

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty

class WindowManager(ScreenManager):
    image_source = StringProperty()
    def selected(self,filename):
        try:    
            self.image_source = filename[0]
        except:
            pass
    
# Screen where the image is selected
class ImageSelector(Screen):
    pass

# Display image & classification results
class ClassificationResultWindow(Screen):
    pass

class MainApp(App):
    def build(self):        
        self.image_selector = ImageSelector()
        self.scan_result_window = ClassificationResultWindow()

if __name__ == "__main__":
    MainApp().run()

And here is the main.kv file

#:kivy 2.0.0
WindowManager:
    ImageSelector:
    ClassificationResultWindow:

<ImageSelector>:
    name: "image_selector"
    id: image_selector
    BoxLayout:
        orientation: 'vertical'
        id: image_box
        FileChooserListView:
            id: filechooser
            on_selection: 
                root.manager.selected(filechooser.selection)
                print(root.manager.image_source)             
            size_hint: 1, 10           
        Image:
            id: image
            source: root.manager.image_source
            size_hint: 1, 4 
        Button:
            id: diagnose
            text: "Classify"
            on_release: 
                print(root.manager.image_source) 
                app.root.current = "classification_result"

<ClassificationResultWindow>:
    name: "classification_result"
    BoxLayout:
        orientation: 'vertical'
        id: box
        Image:
            id: scan
            source: root.manager.image_source
            size_hint: 1, 10        
        Label:
            text: "Here comes the classification result"
            font_size: 30
            size_hint: 1, 2
            id: label

I tried without success binding the property in different ways, but since I'm new to kivy I didn't know if it makes sense so I didn't include them here.


Solution

  • The way I handle passing information from one screen onto the other is by having the ScreenManager hold properties, and have the Screen access them.

    Your main.py file should now be:

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.uix.screenmanager import ScreenManager, Screen
    from kivy.properties import StringProperty
    
    class WindowManager(ScreenManager):
        image_source = StringProperty()
        def selected(self,filename):
            try:    
                self.image_source = filename[0]
            except:
                pass
        
    # Screen where the image is selected
    class ImageSelector(Screen):
        pass
    
    # Display image & classification results
    class ClassificationResultWindow(Screen):
        pass
    
    class MainApp(App):
        pass     
    
    if __name__ == "__main__":
        MainApp().run()
    
    #:kivy 2.0.0
    WindowManager:
        ImageSelector:
        ClassificationResultWindow:
    
    <ImageSelector>:
        name: "image_selector"
        id: image_selector
        BoxLayout:
            orientation: 'vertical'
            id: image_box
            FileChooserListView:
                id: filechooser
                on_selection: 
                    root.manager.selected(filechooser.selection)          
                size_hint: 1, 10           
            Image:
                id: image
                screen: image_selector 
                source: self.screen.manager.image_source
                size_hint: 1, 4 
            Button:
                id: diagnose
                text: "Classify"
                on_release: 
                    app.root.current = "classification_result"
    
    <ClassificationResultWindow>:
        name: "classification_result"
        id: classification_results
        BoxLayout:
            orientation: 'vertical'
            id: box
            Image:
                id: scan
                screen: classification_results
                source: self.screen.manager.image_source
                size_hint: 1, 10        
            Label:
                text: "Here comes the classification result"
                font_size: 30
                size_hint: 1, 2
                id: label
    

    What is going on? So first a StringProperty is created in the ScreenManager. Bindings to these properties are automatically created and thus things referencing this property will update as it is changed.

    Then the Image class in each Screen references this StringProperty through root.manager.image_source.

    • root is the root widget,
    • manager is a property that each screen has which points it towards its parent ScreenManager
    • image_source is the property we created earlier.

    Hope this helps. I have not tested the above, so there may be an error or two, but I think the general concept of having the ScreenManager hold objects that Screen's need to pass on to one another is how I solve this issue.