如何在屏幕中选择图像并使用 kivy 将其显示在另一个屏幕上?

Posted

技术标签:

【中文标题】如何在屏幕中选择图像并使用 kivy 将其显示在另一个屏幕上?【英文标题】:How to select an image in a screen and display it on another using kivy? 【发布时间】:2021-07-29 11:58:32 【问题描述】:

我正在使用 Kivy 创建一个简单的两屏界面,用于执行图像分类任务。在第一个屏幕中,我使用文件选择器选择图像并显示它。在第二个屏幕中,我想显示相同的图像和分类任务的结果。屏幕之间的转换是通过第一个屏幕上的按钮完成的。

我的问题是:当我按下按钮时,如何触发第二屏上图像源属性的更新,以便将所选图像显示在第二屏上? 分类部分只是为了了解我的问题的背景,我没有在代码中包含它。

这是main.py 文件

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()

这是main.kv 文件

#: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

我尝试以不同的方式绑定属性但没有成功,但由于我是 kivy 的新手,我不知道这是否有意义,所以我没有在此处包含它们。

【问题讨论】:

【参考方案1】:

我处理从一个屏幕向另一个屏幕传递信息的方式是让ScreenManager 持有属性,并让Screen 访问它们。

您的 main.py 文件现在应该是:

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

发生了什么事?所以首先在ScreenManager 中创建StringProperty。与这些属性的绑定是自动创建的,因此引用此属性的内容将随着它的更改而更新。

然后每个Screen 中的Image 类引用这个StringPropertyroot.manager.image_source

root 是根小部件, manager 是每个屏幕都有的一个属性,它指向它的父级ScreenManager image_source 是我们之前创建的属性。

希望这会有所帮助。我没有测试上述内容,所以可能有一两个错误,但我认为让ScreenManager 持有Screen 需要相互传递的对象的一般概念是我解决这个问题的方法。

【讨论】:

我应该补充一点,更改会立即发生(因为自动绑定)。您始终可以通过创建访问该属性的函数来延迟此操作,即在您的selected() 函数中写入self.ids.image.source = self.manager.image_source。这样您就可以控制一个屏幕何时访问图像。 非常感谢。我做了一些小改动,但我仍然无法让它工作。这行source: root.manager.image_source 抛出错误:AttributeError: 'NoneType' object has no attribute 'image_source'。按下按钮时,我包含了print(root.manager.image_source),这显示了正确的路径。我不明白为什么无法在图像源中访问它。我根据您的建议更新了我的初始代码。 嗯,试试这个的变体。先试试root.parent.manager.image_source,然后再试试self.parent.parent.manager.image_source。如果它们都不起作用,请为您的图像添加manager 属性。为此,首先给每个Screen 一个id(在KV 文件中),类似于id: _screen 然后为Image 类给他们一个屏幕属性screen: _screen。希望您应该能够通过传递 source: self.screen.manager.image_source 来引用图像源 太棒了。最后一个建议奏效了,我给每个屏幕一个 id,然后将其用作Image 类中的screen 属性,并将源称为source: self.screen.manager.image_source。我更改了您的答案以合并 cmets。你知道它为什么起作用吗? 是的,所以您新创建的 id 是一个 ObjectProperty,它指向 Screen,其工作方式与 parentroot 的工作方式相同。事实上,这是一种让小部件相互“对话”的好方法。假设您要基于TextInput 更新Label。为标签创建一个 id,然后使用上述技术在 TextInput 中存储对该标签的引用。最后,您可以通过声明 self.label.text = self.text 来创建绑定。

以上是关于如何在屏幕中选择图像并使用 kivy 将其显示在另一个屏幕上?的主要内容,如果未能解决你的问题,请参考以下文章

Kivy - 另一个屏幕上的滑块类值更改

如何解决“无效的属性名称”错误以使用 kivy 制作文件选择器?

Python Kivy - 在另一个屏幕上显示动态结果

从画廊 Python Kivy for Android 中选择图像

如何在 Kivy 的 Scrollable GridLayout 中显示图像

相对于布局放置和移动一组带有 kivy 的图像