kivy +Python皮:多屏幕配置中如何根据外部函数响应更新表单标签

ffdz8vbo  于 2022-12-01  发布在  Python
关注(0)|答案(2)|浏览(130)

嗨,所有的kivy/pythonMaven。我正在寻找一个建议/解决方案,在尝试了所有我发现的最后一周。

我的设想是这样的:

1.我有一个带有2个屏幕的kivy应用程序,因此我使用屏幕管理器,每个屏幕(1和2)都在我的.kv文件中定义
1.在我的第一个屏幕上点击登录后,应用程序跳转到第二个屏幕。在第二个屏幕上,我有一个按钮和一个标签(保持简单的例子)。现在的问题:

  • 当点击按钮时,我调用了一个方法foo 1(),它在同一个类中:Screen 2.foo 1()使用特定参数调用on_press(从.kv文件中,使用on_press = root.foo1(arg)
  • 基于接收到的参数,函数计算一些东西,然后调用另一个函数foo 2(),传递结果。foo 2()位于另一个python文件(external.py)中,在另一个类中(因此我们有不同的文件,不同的类,在那个类中有一个方法:foo 2).到目前为止一切正常
  • 在完成一些计算之后,**foo 2()**应该会返回结果。2在哪里?3嗯,在我的kivy标签里面,在Screen 2上。

遇到的问题:

当尝试将foo 2()的结果写入screenTwo中的文本标签**(my_label)时,foo 2()无法示例化screen 2的id(根据我尝试的内容,会引发各种错误(见下文)。
我可以理解其中的原因:我打电话的那一刻
foo 2()来自foo 1()我们将作为my_label的父项退出screenTwo,这将更改“self”上下文(ofself.ids.my_label.text = result
from
foo 2()).现在它不再引用screenTwo**,但可能是**foo 2()**函数的内存地址,我认为该函数现在充当父函数(至少这是我的结论,我仍然是Python的初学者)。

我尝试过的事情:

基本上我能找到的所有东西,试图找到我需要枚举的“真正的”父对象,找到my_label实际所在的子对象ID,但无法找到:
1.我试着在Screen 2类中声明一个ObjectProperty = None,然后给标签一个id,再给Screen 2类中引用id的变量名。很多教程都推荐这样做,但是在屏幕之间传递东西时,这样做是有效的,在我的例子中就不行了。
1.尝试将对标签目标的引用从self.ids更改为(在foo 2()内

  • root.self.ids.my文本
  • 我的标签.文本
  • 主目录.根目录.管理器.get_current('screenTwo').ids.my_label.text
  • 应用程序.主.根.管理器.ids(“屏幕二”).ids.my_label.text
  • 和maaaany其他...:(

根据我所尝试的(多次尝试),我收到了各种错误。以下是一些错误:

  • -名称错误:名称'root'未定义 *
  • -属性错误:“string”对象没有属性“text”*
    • Kivy属性错误:'NoneType'对象没有属性'ids'*

我似乎不明白这个self/root/app上下文是如何工作的,尽管查阅了各种资源(stackoverflow,pdf文件,kivy官方文档)(如果有人能推荐一个好的教程或为所有像我这样的新手解释一下...哇,这将是多么有帮助)。
而且,我也找不到任何与这是否真的可能相关的东西,考虑到在保存当前屏幕的类中,你实际上是在调用位于另一个py文件中的外部函数:kivy甚至支持将响应从外部函数传递回主函数吗?(我认为它支持,因为这是纯Python,而不是kivy。kivy只是在正确的上下文中管理编写....如果我能弄清楚的话:()。
无论如何,这是我的示例代码(py + kv文件)。如果你们中的任何人能给我一个提示或解决方案,告诉我如何调用一个函数,该函数调用一个外部函数,然后将响应写回到我启动事件的屏幕上,在一个标签中,我将非常感谢!

main.py

from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.app import MDApp
from kivy.app import App
from kivymd.uix.label import MDLabel
from kivy.properties import ObjectProperty, StringProperty
import external

class Screen1Main(Screen):
    pass

class Screen2(Screen):
    def foo1():
        # 1. do something 
        myArg = "x"

        # 2. then call foo2()
        external.myExternalClass.foo2(myArg)

class WindowManager(ScreenManager):
    pass


class MainApp(MDApp):
    def __init__(self, **kwargs):
        self.title = "My Application"
        super().__init__(**kwargs)

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

外部.py

from kivy.app import App
import main

class myExternalClass:

    def foo2(arg1):

        #1. does something
            blabla
            
        #2. gets the result
            myResult = "anything"
       

       #3. tries to write the result into my_label (located in Screen 2, which is a child 
       # of my main app (in file: main.py), who manages the multi-screen via a screen manager within 
       # WindowsManager class)
        
       Screen2.manager.get_screen("screenTwo").ids.my_label.text = myResult    

---

干线.kv

第一次

eoxn13cs

eoxn13cs1#

请尝试使用:

MDApp.get_running_app().root.get_screen('screenTwo').ids.my_label.text = myResult

这会取得MDApp并指涉它的rootWindowManager),以取得所需的Screen

bkhjykvo

bkhjykvo2#

我不确定拥有独立类的实用性--但是试着在这里给出一些传递信息的例子。我认为关键是在你的主应用程序中设置对象引用。
main.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.app import MDApp
from kivymd.uix.label import MDLabel
from kivymd.uix.textfield import MDTextField
from kivy.lang import Builder

class Screen1Main(Screen):
    kv_text_field: MDTextField

class Screen2(Screen):
    my_label: MDLabel

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

class myExternalClass:

    def foo2(self, arg):
        self.myResult = "anything"
        self._screen2.my_label.text = arg

    def __init__(self, screen2):
        self.myResult = ""
        # get a reference to this screen so you can access it in this class instance
        self._screen2: Screen2 = screen2

class WindowManager(ScreenManager):
    pass

class MainApp(MDApp):

    def foo1(self, my_button):
        # 1. do something
        myArg = "from main App"
        print(f"{my_button}, {type(my_button)}")

        # 2. then call foo2()
        self.my_external_class.foo2(myArg)

    def main_screen(self, my_button):
        print(my_button)
        self.my_top_widget.current = "Screen1Main"

    def kv_change_screen(self, my_button, arg: str):
        print(f"{self}, {my_button}, {arg}")
        self.screen2.my_label.text = arg
        self.my_top_widget.current = "Screen2"

    def build(self) -> WindowManager:

        self.my_top_widget.add_widget(self.screen1main)
        self.my_top_widget.add_widget(self.screen2)
        self.my_top_widget.current="Screen1Main"
        return self.my_top_widget

    def __init__(self, **kwargs):
        self.title = "My Application"
        super().__init__(**kwargs)
        self.my_top_widget = WindowManager()
        self.screen2 = Screen2(name="Screen2", )
        self.screen1main = Screen1Main(name="Screen1Main")
        self.screen1main.kv_text_field.text = "message to screen 2"
        self.my_external_class = myExternalClass(screen2=self.screen2)

if __name__ == "__main__":
    # load the file by name - this is more clear
    Builder.load_file("main_app.kv")
    my_app = MainApp()
    my_app.run()

主应用程序.kv

#:import kivy kivy
# main_app.kv
<Screen1Main>:
    kv_text_field: kv_text_field
    MDBoxLayout:
        orientation: 'vertical'
        MDTextField:
            id: kv_text_field
        MDIconButton:
            text: "button"
            icon: "message-arrow-right"
            on_press: app.kv_change_screen(self, kv_text_field.text)

<Screen2>:
    my_label: my_label
    GridLayout:
        cols: 2
        Label:
            text: "Screen2"
        MDLabel:
            id: my_label
            text: "-"

        MDFlatButton:
            id: my_button
            text: "foo1"
            on_release: app.foo1(self)
        MDIconButton:
            id: my_button
            icon: "message-arrow-left"
            on_release: app.main_screen(self)

您可能不需要WindowManager的类定义,如果不打算扩充该类,则可以直接使用ScreenManager。
我希望这里的例子可以帮助你,即使我没有直接遵循你的应用程序的确切流程和结构。

相关问题