简体   繁体   English

ScreenManager 中的随机屏幕 python kivy

[英]Random Screens from ScreenManager python kivy

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' "我想我已经很接近做我想做的事了,但是在这个阶段我被困了 2 天。当我在控制台中运行我的代码时,我遇到了这个错误“AttributeError:'ScreenManager' object 没有属性'random_screen' "

And I really don't know where to put that attribute... please heeeelppp: :((而且我真的不知道把那个属性放在哪里......请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()


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. Myscreen class 根本没有连接到您的其他类或 kv 文件,因此根本找不到 function。

  2. Your 3rd add.widget call adds the 'Q2' screen instead of Q3您的第三个 add.widget 调用添加了“Q2”屏幕而不是 Q3

  3. Screen does not have a self.source parameter. Screen 没有 self.source 参数。 You need to use the screenmanager to change screens.您需要使用屏幕管理器来更改屏幕。 root.manager.current = 'Q#' would be the correct way. root.manager.current = 'Q#' 将是正确的方法。

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 .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 = 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您还可以将该方法放入应用程序 class 并在 kv 文件中使用

app.random_screen()

Just means you don't have to repeat the same method in each screen class.只是意味着您不必在每个屏幕 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.另一件要考虑的事情,我不确定这将如何工作,因为我没有像您那样使用图像,但是您可以让按钮更改图像而不是更改屏幕......在应用程序 class 中可以有一个字符串属性,该属性的更改方式与按钮更改屏幕的方式相同。 In the kv file, have the image source reference this string property, app.Pic for example below...在 kv 文件中,让图像源引用此字符串属性 app.Pic,例如下面...

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()

I know this isnt another answer to your original question but as per the above comment here is a really paired back quiz app, to give you an idea of how you can change the question and the answers and use the same buttons and screen...我知道这不是您原始问题的另一个答案,但根据上面的评论,这里是一个真正配对的后备测验应用程序,让您了解如何更改问题和答案并使用相同的按钮和屏幕......

I put everything into the app class, it makes it easier to access from the kv file.我将所有内容都放入应用程序 class 中,这样可以更轻松地从 kv 文件访问。 I tried to add in a few comments to try and expalin the code我尝试添加一些评论来尝试解释代码

from kivy.app import App
from kivy.properties import StringProperty, BooleanProperty,ObjectProperty, NumericProperty
from kivy.uix.screenmanager import ScreenManager, Screen


TestBank = {1 : {"Current Question": "Press 3", "Correct Answer": "3", "Chosen Answer": "", "Multiple Chocies": ["1","2","3","4"]},
2 : {"Current Question": "Press 4", "Correct Answer": "4", "Chosen Answer": "", "Multiple Chocies": ["1","2","3","4"]},
3 : {"Current Question": "Press 1", "Correct Answer": "1", "Chosen Answer": "", "Multiple Chocies": ["1","2","3","4"]},
4 : {"Current Question": "Press 2", "Correct Answer": "2", "Chosen Answer": "", "Multiple Chocies": ["1","2","3","4"]}}

def CreateTest(A_Test = TestBank):
    #do something to randomise your test, maybe select a certain number of questions or merge several test banks etc...
    return A_Test


class MenuScreen(Screen):
    pass

class ResultScreen(Screen):
    pass

class TestScreen(Screen):
    pass

class QuizApp(App):


    TestCurrentQuestion = StringProperty()
    'This is updated when next question is selcted'

    ChosenAnswerText = StringProperty()
    'Every time an answer is selected this property is updated with the selected answer, then the label is automatically updated'
    
    CurrentQuestion = NumericProperty(1)
    'The Next and Previous buttons change this property up or down'
    
    TotalQuestions = 4


    AnswerButtonA = StringProperty()
    AnswerButtonB = StringProperty()
    AnswerButtonC = StringProperty()
    AnswerButtonD = StringProperty()
    'These are the four answer buttons, the Next and Previous buttons update these properties with the answer options, the button texts are then autmatically updated'


    Score = NumericProperty()



    def StartTest(self):
        self.Test = CreateTest()                    #The test is created
        self.UpdateQuestionAndAnswerButtons()       #This method updates the text properties that updates all the buttons

    def NextQuestion(self):
        self.CurrentQuestion +=1                    #changes the current question property +1
        self.UpdateQuestionAndAnswerButtons()       #This method updates the text properties that updates all the buttons
                      
    def PreviousQuestion(self):
        self.CurrentQuestion -=1                    #changes the current question property -1      
        self.UpdateQuestionAndAnswerButtons()       #This method updates the text properties that updates all the buttons

    def UpdateChosenAnswer(self, button):
        self.Test[self.CurrentQuestion]["Chosen Answer"] = button.text              #so that a score can be calculated, the chosen answer is stored in the dictionary TestBank
        self.ChosenAnswerText = self.Test[self.CurrentQuestion]["Chosen Answer"]    #ChosenAnswerProperty is updated, this then updates the label so the chosen answer appears on screen

    def UpdateQuestionAndAnswerButtons(self):
        self.TestCurrentQuestion = self.Test[self.CurrentQuestion]["Current Question"]          
        #TestCurrentQuestion is the property, it is updated to ["Current Question"] for the Test Dic
        
        self.AnswerButtonA, self.AnswerButtonB, self.AnswerButtonC, self.AnswerButtonD = self.Test[self.CurrentQuestion]["Multiple Chocies"]        
        #["Multiple Chocies"] is a list containing 4 items, so the four properties are updated in one line
        
        self.ChosenAnswerText = self.Test[self.CurrentQuestion]["Chosen Answer"]                
        #The chosen answer is updated, this is needed here incase the previous button is selcted, the previous selected answer will be updated to avoid confusion for the user


    def CalculateScore(self):
        score = 0
        for Question in self.Test.keys():
            if self.Test[Question]["Chosen Answer"]== self.Test[Question]["Correct Answer"]:
                score+=1
        self.Score =  score
        

    def build(self):
        sm = ScreenManager()
        sm.add_widget(MenuScreen(name='menu'))
        sm.add_widget(ResultScreen(name='result'))
        sm.add_widget(TestScreen(name='test'))


        return sm
    
QuizApp().run()

And the KV file和 KV 文件

<ResultScreen>
    BoxLayout:
        orientation: "vertical"
        padding: "20px"
        spacing: "20px"

        BoxLayout:
            Label:
                text: f"Your Score was {app.Score} out of {app.TotalQuestions}\n                   {round(app.Score/app.TotalQuestions*100)}%"
            Label:
                text: "PASS" if app.Score/app.TotalQuestions*100 >= 70 else "FAIL" #pass mark is 70%
    
        Button:
            text: "Good Bye"
            on_press: app.get_running_app().stop()
   

<MenuScreen>
    BoxLayout:
        orientation: "vertical"
        padding: "20px"
        spacing: "20px"
        
        Label:
            text:"Wecome to the  Test App"

        Button:
            text: "New Test"
            on_press: app.StartTest(); root.manager.current = "test"


<TestScreen>
    BoxLayout:
        orientation: "vertical"

        BoxLayout:
            padding: "20px"
            orientation: "vertical"

            Label:
                text: app.TestCurrentQuestion
                halign: "center"
            Label:
                text: app.ChosenAnswerText

        GridLayout:
            padding: "20px"
            spacing: "20px"
            cols: 2
            rows: 2
            
            Button:
                text: app.AnswerButtonA
                on_press: app.UpdateChosenAnswer(self)

            Button:
                text: app.AnswerButtonB
                on_press: app.UpdateChosenAnswer(self)

            Button:
                text: app.AnswerButtonC
                on_press: app.UpdateChosenAnswer(self)

            Button:
                text: app.AnswerButtonD
                on_press: app.UpdateChosenAnswer(self)
                  
        BoxLayout:

            size_hint: (1,None)
            orientation: "horizontal"
            padding: "20px"
            spacing: "20px"

            Button:
                text: "Previous"
                size_hint: (0.3,1)
                on_press: app.PreviousQuestion()
                disabled: True if app.CurrentQuestion == 1 else False

            Button:
                text: "Submit"
                size_hint: (0.4,1)
                on_press: app.CalculateScore(); root.manager.current = "result"

            Button:
                text: "Next"
                on_press: app.NextQuestion(); 
                size_hint: (0.3,1)
                disabled: True if app.CurrentQuestion  == app.TotalQuestions else False

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM