Переключение экранов с использованием Kivy: AttributeError: объект 'NoneType' не имеет атрибута 'transition' - PullRequest
0 голосов
/ 14 апреля 2020

Я столкнулся с AttributionError при попытке вызвать функцию «переключения экрана», определенную в одном классе из другого, используя библиотеку kivy в python.

Структура моего кода следующая :

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'

Из моего кода выше я определил функцию переключения экрана change_to_B в классе ScreenA и вызвал эту функцию в классе ClassA. Я унаследовал родительский класс Screen в обоих классах ScreenA и ScreenB, которые я создал. Но как ни странно, сообщение об ошибке, кажется, предполагает, что объект screenA является объектом NoneType, что не должно быть так, как я объявил, что это экранный объект при создании класса путем наследования родительского элемента Screen класс.

Кто-нибудь может указать на ошибку, которую я упускаю? Заранее спасибо !!!

РЕДАКТИРОВАТЬ:

Я думаю, мне нужно очистить мой код. Поэтому я в основном пытаюсь написать игру тральщика, и ClassA - это сетка, из которой состоит игровая доска тральщика, и у нее есть атрибут Button для реализации функции нажатия method (показано в коде выше) , В функции щелчка method я пытался вызвать функцию переключения экрана change_to_B(), определенную в классе ScreenA, которая является экранным объектом, объявленным в методе сборки ScreenManager (я думаю).

Поток кода: Current (name = "current") (что не имеет отношения к этому вопросу) -> ScreenA (name = "screenA"), где ClassA() создается в переменной глобального списка из функции init_grid() -> ClassA (), при нажатии на который пользовательский интерфейс вызывает функцию, связанную с атрибутами Button объектов ClassA().

Объект ClassA создается в функции init_grid, где создается глобальная переменная grid_field для хранения объектов ClassA в 2D-списке.

1 Ответ

1 голос
/ 14 апреля 2020

Распространенная ошибка - вы используете более одного объекта каждого экрана, поэтому вы добавляете один объект к ScreenManager, и этот объект имеет свойство manager. Но затем вы создаете еще один объект класса ScreenA:

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

Screen Manager ничего не знает об этом объекте. Поэтому следует помнить, что вы добавляете 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()
...