Random Screens from ScreenManager python kivy

Viewed 19

I have in my app some images , each image belongs to a screen and when I press a button I try to get another screen but a random one. I think I am pretty close to do what I want to do but for 2 days I am stuck at this stage.. when i run my code in the console i have this error " AttributeError: 'ScreenManager' object has no attribute 'random_screen' "

And I really don't know where to put that attribute ... please heeeelppp ! :((

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


class MyScreens(Screen):
    screens = ["Q1", "Q2", "Q3"]  # ........

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

    def random_screen(self, *_):
        self.source = choice(self.screens)


class Q1(Screen):
    pass


class Q2(Screen):
    pass


class Q3(Screen):
    pass


class TestApp(App):

    def build(self):

        sm = ScreenManager()
        sm.add_widget(Q1(name='Q1'))
        sm.add_widget(Q2(name='Q2'))
        sm.add_widget(Q2(name='Q3'))

        return sm


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




<Q1>:
    Image:
        source: 'Q1.png'

        FloatLayout:
            size: root.width, root.height/2

            Button:
                size_hint: 0.3, 0.25
                pos_hint: {"x":0.09, "top":1.16}
                background_color: 1, 1, 1, 0.2
                on_release: app.root.random_screen()

<Q2>:
    Image:
        source: 'Q2.png'

        FloatLayout:
            size: root.width, root.height/2

            Button:
                size_hint: 0.3, 0.25
                pos_hint: {"x":0.09, "top":1.16}
                background_color: 1, 1, 1, 0.2
                on_press: app.root.random_screen()

<Q3>:
    Image:
        source: 'Q3.png'

        FloatLayout:
            size: root.width, root.height/2

            Button:
                size_hint: 0.3, 0.25
                pos_hint: {"x":0.09, "top":1.16}
                background_color: 1, 1, 1, 0.2
                on_press: app.root.random_screen()


2 Answers

You got a lot of bugs in there...

  1. Myscreen class isn't connected to your other classes or kv file at all, so that function will not be found at all.

  2. Your 3rd add.widget call adds the 'Q2' screen instead of Q3

  3. Screen does not have a self.source parameter. You need to use the screenmanager to change screens. root.manager.current = 'Q#' would be the correct way.

This worked for me...

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


class Q1(Screen):
    def random_screen(self):
        screens = ['Q1', 'Q2', 'Q3']
        return random.choice(screens)



class Q2(Screen):
    def random_screen(self):
        screens = ['Q1', 'Q2', 'Q3']
        return random.choice(screens)


class Q3(Screen):
    def random_screen(self):
        screens = ['Q1', 'Q2', 'Q3']
        return random.choice(screens)


class TestApp(App):

    def build(self):

        sm = ScreenManager()
        sm.add_widget(Q1(name='Q1'))
        sm.add_widget(Q2(name='Q2'))
        sm.add_widget(Q3(name='Q3'))

        return sm


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

KV file

<Q1>:
    Image:
        source: 'Q1.png'

        FloatLayout:
            size: root.width, root.height/2

            Button:
                size_hint: 0.3, 0.25
                pos_hint: {"x":0.09, "top":1.16}
                background_color: 1, 1, 1, 0.2
                on_release: root.manager.current = root.random_screen()
<Q2>:
    Image:
        source: 'Q2.png'

        FloatLayout:
            size: root.width, root.height/2

            Button:
                size_hint: 0.3, 0.25
                pos_hint: {"x":0.09, "top":1.16}
                background_color: 1, 1, 1, 0.2
                on_release: root.manager.current = root.random_screen()
<Q3>:
    Image:
        source: 'Q3.png'

        FloatLayout:
            size: root.width, root.height/2

            Button:
                size_hint: 0.3, 0.25
                pos_hint: {"x":0.09, "top":1.16}
                background_color: 1, 1, 1, 0.2
                on_release: root.manager.current = root.random_screen()

You could also put the method in the app class and in the kv file call it by using

app.random_screen()

Just means you don't have to repeat the same method in each screen class.

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


class Q1(Screen):
    Pass

class Q2(Screen):
    Pass

class Q3(Screen):
    Pass


class TestApp(App):

    def random_screen(self):
        screens = ['Q1', 'Q2', 'Q3']
        return random.choice(screens)

    def build(self):

        sm = ScreenManager()
        sm.add_widget(Q1(name='Q1'))
        sm.add_widget(Q2(name='Q2'))
        sm.add_widget(Q3(name='Q3'))

        return sm


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

kV:

<Q1>:
    Image:
        source: 'Q1.png'

        FloatLayout:
            size: root.width, root.height/2

            Button:
                size_hint: 0.3, 0.25
                pos_hint: {"x":0.09, "top":1.16}
                background_color: 1, 1, 1, 0.2
                on_release: root.manager.current = app.random_screen()

<Q2>:
    Image:
        source: 'Q2.png'

        FloatLayout:
            size: root.width, root.height/2

            Button:
                size_hint: 0.3, 0.25
                pos_hint: {"x":0.09, "top":1.16}
                background_color: 1, 1, 1, 0.2
                on_release: root.manager.current = app.random_screen()

<Q3>:
    Image:
        source: 'Q3.png'

        FloatLayout:
            size: root.width, root.height/2

            Button:
                size_hint: 0.3, 0.25
                pos_hint: {"x":0.09, "top":1.16}
                background_color: 1, 1, 1, 0.2
                on_release: root.manager.current = app.random_screen()

Another thing to consider, and I am not sure how this would work as I haven't used images like the way you are doing, but you could have the button change the image rather then change the screen... In the app class you could have a string property that is changed in the same way the button changes your screen. In the kv file, have the image source reference this string property, app.Pic for example below...

from kivy.app import App
from kivy.uix.properties import StringProperty 
from kivy.uix.screenmanager import ScreenManager, Screen
import random


class Q1(Screen):
    Pass


class TestApp(App):
    Pic = StringProperty('Q1.png')

    def random_pic(self):
        Pics = ['Q1.png', 'Q2.png', 'Q3.png']
        self.Pic = random.choice(Pics)

    def build(self):

        sm = ScreenManager()
        sm.add_widget(Q1(name='Q1'))
        

        return sm


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

kV:

<Q1>:
    Image:
        source: app.Pic

        FloatLayout:
            size: root.width, root.height/2

            Button:
                size_hint: 0.3, 0.25
                pos_hint: {"x":0.09, "top":1.16}
                background_color: 1, 1, 1, 0.2
                on_release: app.random_pic()
Related