Kivy screenmanager "No Screen with name" error when redirecting

I am experimenting with kivy by developing an app to register exercise routines, with a few screens in a screenmanager. However, when running the code, I am getting an ScreenManagerException:

kivy.uix.screenmanager.ScreenManagerException: No Screen with name "('Suplementos', <kivy.uix.button.Button object at 0xb3d3341c>)".

The app is currently made of the following pieces of code (python 2.7 + kivy 1.9):

main.py

#coding:utf-8

from functools import partial
from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.scrollview import ScrollView

import time

class TelaInicial(Screen):
    layout_telainicial = ObjectProperty(None)

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

        opcoes = [
                'Suplementos',
                'Cardiovascular',
                'Novo treino'
                ]

        for opt in opcoes:
            Opcao = Button(
                            text=str(opt), 
                            size_hint_y=None, 
                            height=60,
                            )
            Opcao.bind(on_press=partial(self.ir_para, opt))

            self.ids.layout_telainicial.add_widget(Opcao)

    def ir_para(self, *args):
        self.parent.current = str(args)


class Suplementos(Screen): 
    layout_suplementos = ObjectProperty(None)

    def __init__(self, **kwargs):
        hora = time.asctime()
        super(Suplementos, self).__init__(**kwargs)
        mostrar_hora = Label(text=str(hora))
        self.ids.layout_suplementos.add_widget(mostrar_hora)

class Cardiovascular(Screen):
    layout_cardiovascular = ObjectProperty(None)

    def __init__(self, **kwargs):
        hora = time.asctime()
        super(Cardiovascular, self).__init__(**kwargs)
        mostrar_hora = Label(text=str(hora))
        self.ids.layout_cardiovascular.add_widget(mostrar_hora)

class NovoTreino(Screen):
    layout_novotreino = ObjectProperty(None)

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

        AdicionarExercicio = Button(
                                    text='+', 
                                    size_hint_y=None, 
                                    height=60,
                                    )
        AdicionarExercicio.bind(on_press=lambda a: self.NovoExercicio)

        self.ids.layout_novotreino.add_widget(AdicionarExercicio)

    def NovoExercicio(self, *args):
        self.parent.current = 'Seleção de exercícios'

class SelecaoExercicio(Screen):
    pass

class GerenciadorApp(App):
    def build(self):
        MyScreenManager = ScreenManager()
        MyScreenManager.add_widget(TelaInicial())
        MyScreenManager.add_widget(Suplementos())
        MyScreenManager.add_widget(Cardiovascular())
        MyScreenManager.add_widget(NovoTreino())
        MyScreenManager.add_widget(SelecaoExercicio())
        return MyScreenManager

GerenciadorApp().run()

gerenciador.kv

<TelaInicial>
    name: 'Tela inicial'
    ScrollView:
        GridLayout:
            id: layout_telainicial
            cols: 1
            padding: 10
            spacing: 10
            size_hint: 1, None
            height: self.minimum_height

<Suplementos>
    name: 'Suplementos'
    ScrollView:
        GridLayout:
            id: layout_suplementos
            cols: 1
            padding: 10
            spacing: 10
            size_hint: 1, None
            height: self.minimum_height
            Label:
                text: 'Registro de suplementos'

<Cardiovascular>
    name: 'Cardiovascular'
    ScrollView:
        GridLayout:
            id: layout_cardiovascular
            cols: 1
            padding: 10
            spacing: 10
            size_hint: 1, None
            height: self.minimum_height
            Label:
                text: 'Exercício cardiovascular'

<NovoTreino>
    name: 'Novo treino'
    ScrollView:
        GridLayout:
            id: layout_novotreino
            cols: 1
            padding: 10
            spacing: 10
            size_hint: 1, None
            height: self.minimum_height
            Label:
                text: 'Treino de musculação'

<SelecaoExercicio>
    name: 'Seleção de exercícios'
    ScrollView:
        GridLayout:
            id: layout_selecaoexercicios
            cols: 1
            padding: 10
            spacing: 10
            size_hint: 1, None
            height: self.minimum_height

In a former version of the code, I also tried a lambda function instead of the partial() fuction, used within the TelaInicial class constructor to bind the "on_press" buttons' properties:

class TelaInicial(Screen):
    layout_telainicial = ObjectProperty(None)

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

        opcoes = [
                'Suplementos',
                'Cardiovascular',
                'Novo treino'
                ]

        for opt in opcoes:
            Opcao = Button(
                            text=str(opt), 
                            size_hint_y=None, 
                            height=60,
                            )
            Opcao.bind(on_press=self.ir_para(opt))

            self.ids.layout_telainicial.add_widget(Opcao)

    def ir_para(self, destino, *args):
        self.parent.current = str(destino)

This would return always the last screen in the list of options, 'NovoTreino', for all buttons, instead of the screen identified by each button.

Why isn't the code working in each of these cases - and how could I fix it?

Upvotes: 1

Views: 1141

Answers (1)

el3ien
el3ien

Reputation: 5405

The problem is that you set the screenmanagers current to the whole list of args, when you only want the first item of args.

>>> print(args)
('Suplementos', <kivy.uix.button.Button object at 0xb3d3341c>)
>>> print(args[0])
'Suplementos'

So what you need is:

def ir_para(self, *args):
    self.parent.current = str(args[0])

Upvotes: 1

Related Questions