使用 Kivy 切换屏幕:AttributeError: 'NoneType' object has no attribute 'transition'

Posted

技术标签:

【中文标题】使用 Kivy 切换屏幕:AttributeError: \'NoneType\' object has no attribute \'transition\'【英文标题】:Switching screens using Kivy: AttributeError: 'NoneType' object has no attribute 'transition'使用 Kivy 切换屏幕:AttributeError: 'NoneType' object has no attribute 'transition' 【发布时间】:2020-07-27 09:20:55 【问题描述】:

我在尝试使用 python 中的 kivy 库从另一个类中调用定义在一个类中的“屏幕切换”函数时遇到了 AttributionError

我的代码结构如下:

from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.app import App

def init_grid(width, height, mines):
    global grid_field
    global flagged
    # Initiate the gird field with empty grids
    grid_field = [[ClassA() for i in range(width)] for j in range(height)]
    flagged = 0
    ...


class Main(App):
   def build(self):
      sm = ScreenManager()

      current = Current(name="current")
      screenA = ScreenA(name="screenA")
      screenB = ScreenB(name="screenB")
      ...
      ...

      sm.add_widget(current)
      sm.add_widget(screenA)
      sm.add_widget(screenB)
      ...
      ...

      sm.current = "current"
      return sm

...

class ClassA():
    # a class my code is at when the error arises, 
    # which is not the starting "current" screen class 
    # but a different class that serves a certain purpose to my program
    def __init__(self, **kwargs):
        ...
        ...
        self.button = Button()
        self.button.bind(on_touch_down=self.method)


    def method(self, instance, touch):
        if (a condition):
            ScreenA().change_to_B()

    ...




...


class ScreenA(Screen):
    def __init__(self, **kwargs):
        Screen.__init__(self, **kwargs)
        ...
        ...
        init_grid(10, 10, 10)

    def change_to_B(self):
        self.manager.transition.direction = "left"
        self.manager.current = "screenB"


...


class ScreenB(Screen):
    def __init__(self, **kwargs):
        Screen.__init__(self, **kwargs)
        ...
        ...


if __name__ == '__main__':
    Main().run()

错误信息是:

in change_to_B
     self.manager.transition.direction = "left"
 AttributeError: 'NoneType' object has no attribute 'transition'

在上面的代码中,我在ScreenA 类中定义了屏幕切换函数change_to_B,并在ClassA 类中调用了这个函数。我在我创建的ScreenAScreenB 两个类中都继承了Screen 父类。但奇怪的是,错误消息似乎表明对象screenANoneType 对象,这不应该是这种情况,因为我在通过继承Screen 父级创建类时声明它是屏幕对象类。

谁能指出我错过了什么错误?提前谢谢!!!

编辑:

我想我需要弄清楚我的代码。所以我基本上是在尝试写扫雷游戏,ClassA是扫雷游戏板组成的网格,它有一个Button属性来实现点击功能method(如上代码所示) .在点击函数method中,我试图调用类ScreenA中定义的屏幕切换函数change_to_B(),这是在ScreenManager的build方法中声明的屏幕对象(我认为)。

代码流程为:Current(name="current")(与本题无关) --> ScreenA(name="screenA"),其中ClassA()是在全局列表变量中创建的函数 init_grid() --> ClassA(),点击 UI 调用绑定到 ClassA() 对象的 Button 属性的函数。

ClassA 对象在函数init_grid 中创建,其中创建了一个全局变量grid_field 以将ClassA 对象存储在一个二维列表中。

【问题讨论】:

【参考方案1】:

常见错误 - 每个屏幕使用多个对象,因此您将一个对象添加到 ScreenManager 并且该对象具有属性 manager。但是然后你在这里创建另一个ScreenA 类的对象:

def __init__(self, **kwargs):
        ...
        ...
        ScreenA().change_to_B()

屏幕管理器对该对象一无所知。所以你应该记住的是你将objects添加到屏幕管理器,而不是classes。 您没有提供整个代码,所以我不知道您在哪里定义 ClassA 类的对象,但您应该从 build 方法传递对象以使其工作。像这样的:

class ScreenA(Screen):
    def __init__(self, **kwargs):
        Screen.__init__(self, **kwargs)
        ...
        ...
        # here we pass the object of the ScreenA class (which is self) to init grid to be able to pass it to ClassA
        init_grid(10, 10, 10, self)

# add object to arguments
def init_grid(width, height, mines, screenA):
    global grid_field
    global flagged
    # pass the object to ClassA
    grid_field = [[ClassA(screenA) for i in range(width)] for j in range(height)]

class classA():
    # don't forget to add object to arguments
    def __init__(self, screenA, **kwargs):
        ...
        ...
        self.screenA = screenA

    def method(self, instance, touch):
        if (a condition):
            self.screenA.change_to_B()

或者使用全局变量:

current = Current(name="current")
screenA = ScreenA(name="screenA")
screenB = ScreenB(name="screenB")

class Main(App):
   def build(self):
      sm = ScreenManager()

      global current
      global screenA
      global screenB
      ...
      ...

      sm.add_widget(current)
      sm.add_widget(screenA)
      sm.add_widget(screenB)
      ...
      ...

      sm.current = "current"
      return sm

class ClassA():
    def method(self, instance, touch):
        global screenA
        if (a condition):
            screenA.change_to_B()

【讨论】:

您好,谢谢您的回答。但是我可以仔细检查一下是class classA() 还是class ClassA()?也很抱歉我上面的代码模棱两可,但调用屏幕切换函数的行在单击时绑定到ClassAButton 属性的另一个方法下,而不是__init__()。我尝试在 ScreenManagerbuild() 函数中添加 classA 对象,但它给了我 NameError "screenA is not defined"... @DanielQiao 告诉我你在哪里创建ClassA 的对象,这样我就可以编辑我的答案了 您好,很抱歉给您带来麻烦,但我已经包含了创建 ClassA() 对象的函数以及调用该函数的位置(在 ScreenA 中),以及我的代码的工作流程。跨度> @DanielQiao 我已经编辑了我的答案,检查那里的代码。因此,我们将 ScreenA 类的对象从该类传递给 init_grid 方法,然后从那里我们将其传递给 ClassA。有点奇怪,但在你的情况下,它会是这样的。或者您可以使用其他方式并使用全局变量。我认为这会更简单。 甜蜜!它成功地解决了我的问题,并且全局变量方法更加直接和干净!非常感谢!!!!!!

以上是关于使用 Kivy 切换屏幕:AttributeError: 'NoneType' object has no attribute 'transition'的主要内容,如果未能解决你的问题,请参考以下文章

为啥切换屏幕在 kivy 中不起作用?

在单独的 .kv (Kivy) 文件中定义的屏幕之间切换

kivy如何切换屏幕?

在kivy中制作一个切换到不同屏幕的按钮列表视图

Kivy/ Kivymd 地图切换屏幕

(Kivy Python)在 .py 文件中按下按钮时切换屏幕