ScreenManager python kivy的随机屏幕



我的应用程序中有一些图像,每个图像都属于一个屏幕,当我按下按钮时,我会尝试获得另一个屏幕但却是随机的。我想我很快就要做我想做的事了,但两天来我一直处于这个阶段。。当我在控制台中运行代码时,我会出现以下错误"AttributeError:"ScreenManager"对象没有属性"random_screen">

我真的不知道该把这个属性放在哪里。。。请注意!:(

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

你有很多bug。。。

  1. Myscreen类根本没有连接到其他类或kv文件,因此根本找不到该函数。

  2. 您的第三次add.widget调用添加了"Q2"屏幕,而不是Q3

  3. 屏幕没有self.source参数。您需要使用屏幕管理器来更改屏幕。root.manager.current="Q#"将是正确的方式。

这对我有效…

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文件

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

您也可以将该方法放在应用程序类中,并在kv文件中使用调用它

app.random_screen()

这意味着您不必在每个屏幕类中重复相同的方法。

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

另一件需要考虑的事情是,我不确定这将如何工作,因为我没有像你这样使用图像,但你可以让按钮更改图像,而不是更改屏幕。。。在应用程序类中,您可以有一个字符串属性,该属性的更改方式与按钮更改屏幕的方式相同。在kv文件中,让图像源引用此字符串属性app。下面的图片示例。。。

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

我知道这不是你原来问题的另一个答案,但根据上面的评论,这里有一个真正配对的问答应用程序,让你知道如何更改问题和答案,并使用相同的按钮和屏幕。。。

我把所有的东西都放进了应用程序类中,这使得从kv文件访问更容易。我试着添加一些评论,试图删除代码

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

和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

最新更新