I'm making a game in kivy, and I have a Label with a text that says "GAME OVER" I want this text to appear whenever the game is over and disappear whenever I click on "start game". How can I do that? I tried a lot but can't seem to find any solution, Any help is appreciated! Thank You so much! Below is my code!
main.py
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.image import Image
from kivy.core.audio import SoundLoader
from kivy.clock import Clock
from kivy.properties import NumericProperty
from kivy.vector import Vector
class HomeScreen(Screen):
pass
def play_sound(self):
sound = SoundLoader.load('button press sound.wav.')
if sound:
sound.play()
sound = SoundLoader.load('Crowd sound effect.wav')
sound.loop = True
sound.play()
class GameScreen(Screen):
pass
def play_sound(self):
sound = SoundLoader.load('button press sound.wav.')
if sound:
sound.play()
class Ball(Image):
velocity = NumericProperty(0)
def on_touch_down(self, touch):
if Vector(self.center).distance(touch.pos) <= 33:
label = App.get_running_app().root.get_screen('game_screen').ids.score
label.text = str(int(label.text) + 1)
sound = SoundLoader.load('Soccer ball sound.wav')
sound.play()
self.source = "icons/ball.png"
self.velocity = 275
return super().on_touch_down(touch)
def on_touch_up(self, touch):
if Vector(self.center).distance(touch.pos) <= 33:
self.source = "icons/ball.png"
return super().on_touch_up(touch)
class MainApp(App):
GRAVITY = 300
def move_ball(self, time_passed):
ball = self.root.ids.game_screen.ids.ball
ball.y = ball.y + ball.velocity * time_passed
ball.velocity = ball.velocity - self.GRAVITY * time_passed
self.check_collision()
def check_collision(self):
ball = self.root.ids.game_screen.ids.ball
if ball.top < 96:
self.root.ids.game_screen.ids.score.text = "0"
self.game_over()
def game_over(self):
self.root.ids.game_screen.ids.ball.pos = (0, (0.5) )
print("game over")
self.frames.cancel()
self.root.ids.game_screen.ids.start_button.disabled = False
self.root.ids.game_screen.ids.start_button.opacity = 1
def next_frame(self, time_passed):
self.move_ball(time_passed)
def start_game(self):
#Clock.schedule_interval(self.move_ball, 1/60.)
self.root.ids.game_screen.ids.ball.velocity = 275
self.frames = Clock.schedule_interval(self.next_frame, 1/60.)
self.root.ids.game_screen.ids.score.text = "0"
def change_screen(self, screen_name):
self.root.current = screen_name
MainApp().run()
homescreen.kv
#:import utils kivy.utils
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
<HomeScreen>:
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#39B3F2")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .9
Image:
source: "icons/keepyup.png"
FloatLayout:
Button:
font_size: dp(20)
font_name: 'SackersGothicStd-Medium.otf'
text: "PLAY"
color: "gold"
pos_hint: { "center_x": .5, "center_y": .3}
size: 80, 55
size_hint: None, None
background_normal: ''
background_color: (57/255.0, 179/255.0, 242/255.0, .10)
on_press:
on_release:
root.play_sound()
root.manager.transition = FadeTransition()
app.change_screen("game_screen")
gamescreen.kv
#:import utils kivy.utils
<GameScreen>:
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#39B3F2")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .1
Image:
source: "icons/sun.png"
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .2
Image:
source: "icons/clouds.png"
GridLayout:
rows: 1
pos_hint: {"bottom": 1, "left": 1}
size_hint: 1, .5
Image:
source: "icons/Field4.png"
allow_stretch: True
keep_ratio: False
pos: self.pos
Label:
id: score
size_hint: None, None
font_size: dp(25)
font_name: 'SackersGothicStd-Medium.otf'
text: "0"
color: "gold"
pos_hint: { "center_x": 0.1, "center_y": 0.9}
Label:
id: over
size_hint: None, None
font_size: dp(25)
font_name: 'SackersGothicStd-Medium.otf'
text: "GAME OVER!"
color: "gold"
outline_color: "white"
outline_width: 1
pos_hint: { "center_x": 0.5, "center_y": 0.6}
Button:
size_hint: None, None
font_size: dp(20)
font_name: 'SackersGothicStd-Medium.otf'
text: "Start Game"
color: "gold"
pos_hint: { "center_x": 0.5, "center_y": 0.3}
size: 150, 55
size_hint: None, None
background_normal: ''
background_color: (57/255.0, 179/255.0, 242/255.0, .10)
id: start_button
on_release:
self.disabled = True
self.opacity = 0
root.play_sound()
app.start_game()
Ball:
source: "icons/ball.png"
size_hint: None, None
size: 500, 500
pos_hint: {"center_x": 0.5}
id: ball
main.kv
#:include kv/homescreen.kv
#:include kv/gamescreen.kv
ScreenManager:
id: screen_manager
HomeScreen:
name: "home_screen"
id: home_screen
GameScreen:
name: "game_screen"
id: game_screen
You can just modify the opacity of the Label. In your start_game() method, set the opacity to 0:
def start_game(self):
#Clock.schedule_interval(self.move_ball, 1/60.)
self.root.ids.game_screen.ids.ball.velocity = 275
self.frames = Clock.schedule_interval(self.next_frame, 1/60.)
self.root.ids.game_screen.ids.score.text = "0"
self.root.ids.game_screen.ids.over.opacity = 0
And in the game_over() method, set the opacity to 1:
def game_over(self):
self.root.ids.game_screen.ids.ball.pos = (0, (0.5) )
print("game over")
self.frames.cancel()
self.root.ids.game_screen.ids.start_button.disabled = False
self.root.ids.game_screen.ids.start_button.opacity = 1
self.root.ids.game_screen.ids.over.opacity = 1
Instead of using a Label I'd recommend using a Popup for this.
You can define that the popup will close after clicking and will remove and re-add widgets to a main layout on which all the other game-play related layouts will reside
Here's my example:
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.button import Button
class GameOverApp(App):
pass
# The main layout
class MyMainLayout(BoxLayout):
def game_over(self):
# creating a button
btn = Button(text="Restart")
# Creating a popup and adding the button to it
# auto_dismiss means that clicking anywhere else won't close the popup
my_popup = Popup(title="Game over!", content=btn, auto_dismiss=False)
# binding two functions, one for restarting the game and one for closing the popup
btn.bind(on_press=self.restart_game)
btn.bind(on_press=my_popup.dismiss)
# open the popup
my_popup.open()
def restart_game(self, event):
# Clear all widgets from the mainlayout and re-initialize them
self.clear_widgets()
self.add_widget(GameLayout())
# the layout which holds all the game items
class GameLayout(BoxLayout):
pass
GameOverApp().run()
gameover.kv
MyMainLayout:
<MyMainLayout>
GameLayout:
Button:
text: "Click me for game over"
on_press: self.parent.game_over()
<GameLayout>
Label:
text: "game data..."
Popups with floatlayouts
Kivy documentation
Related
So I have managed to get widgets of varying height into a recycleview and items can be added, this looked perfect on my PC but was very wrong when running on android. I re-built the app with buildozer to make sure it wasn't something to do with that, I put together the demo program seen below and ran it on both platforms, and experienced the same result. I have used the kivy inspector and not been able to see any values that are not what I didn't manually program. And unless I have missed one I have used dp(X) or 'Xdp' whenever needed.
Any help or tips will be apreciated, thank you :)
main.py
from random import randint
from kivy.app import App
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.graphics import Color, Rectangle
from kivy.uix.recycleview import RecycleView
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ListProperty, NumericProperty
class NewPostGrid(BoxLayout):
votes_ = StringProperty()
message_id_ = StringProperty()
text_ = StringProperty()
group_ = StringProperty()
_size = ListProperty()
class SizeLabel(Label):
pass
class RV(RecycleView):
distance_to_top = NumericProperty()
scrollable_distance = NumericProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
App.get_running_app().rv_data_list = []
def generate_post(self): # This is only to test posts with different line height
e = ['Test post ID: ', str(App.get_running_app().message_id_num)]
for i in range(randint(1, 8)): e.append('\n')
e.append('end of post')
return "".join(e)
def add(self):
l = len(App.get_running_app().rv_data_list)
text = self.generate_post()
sl = SizeLabel(text=text)
sl.texture_update()
print(sl.text)
App.get_running_app().rv_data_list.extend([{'message_id_': str(App.get_running_app().message_id_num),
'text_': text,
'_size': sl.texture_size,
'group_': str(App.get_running_app().message_id_num),
'votes_': str(20)}])
App.get_running_app().message_id_num = App.get_running_app().message_id_num + 1
def on_scrollable_distance(self, *args):
if self.scroll_y > 0:
self.scroll_y = (self.scrollable_distance - self.distance_to_top) / self.scrollable_distance
def on_scroll_y(self, *args):
self.distance_to_top = (1 - self.scroll_y) * self.scrollable_distance
#def adjust_vote_state(self, id_):
# for d in self.data:
# if d['text_'] == id_.text_:
# d['state'] == id_.ids.button_up.state
# id_.state = id_.ids.button_up.state
class DemoApp(App):
# One post format = {'message_id':0, 'text':'post_test_here','_size':[0,0], '_group':str(0), '_score':20}
# Text fromat string = [font=Nunito-Bold.ttf][color=161616]Someone:[/color][/font]\n
message_id_num = 0
rv_data_list = ListProperty()
pending_data = ListProperty()
def build(self):
#Clock.schedule_interval(self.add_log, .01)
Clock.schedule_interval(self.flush_pending_data, .250)
def up_vote(self, button, mode): # Not part of the problem
if button.state == 'down':
if mode == 'all':
print("+1 upvote for message index:" + str(button.parent.parent.message_id) + ' in all posts')
else:
print("+1 upvote for message index:" + str(button.parent.parent.message_id) + ' in top posts')
def down_vote(self, button, mode): # Not part of the problem
if button.state == 'down':
if mode == 'all':
print("-1 upvote for message index:" + str(button.parent.parent.message_id) + ' in all posts')
else:
print("-1 upvote for message index:" + str(button.parent.parent.message_id) + ' in top posts')
def flush_pending_data(self, *args):
if self.pending_data:
pending_data, self.pending_data = self.pending_data, []
self.rv_data_list.extend(pending_data)
def generate_post(self): # This is only to test posts with different line height
e = ['Test post ID: ', str(self.message_id_num)]
for i in range(randint(1, 8)): e.append('\n')
e.append('end of post')
return "".join(e)
def add_log(self, dt):
for i in range(10):
text = self.generate_post()
sl = SizeLabel(text=text)
sl.texture_update()
self.pending_data.append({'message_id_': str(self.message_id_num),
'text_': text,
'_size': sl.texture_size,
'group_': str(self.message_id_num),
'votes_': str(20)})
self.message_id_num = self.message_id_num + 1
if __name__ == '__main__':
DemoApp().run()
demo.kv
<SizeLabel>:
padding: "10dp", "12dp"
size_hint: 0.9, None
height: self.texture_size[1]
font_size: "12dp"
text_size: self.width, None
color: 0,0,0,1
multiline: True
markup: True
<NewPostGrid>:
spacing: "6dp"
message_id: root.message_id_
BoxLayout:
id: voting_menu
orientation: "vertical"
spacing: "2dp"
size_hint: .2, None
height: label.height
ToggleButton:
id: button_up
on_state: app.up_vote(self, 'all')
group: root.group_
#state: root.vote_state_up
text: "UP"
color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
font_size: "10dp"
size_hint: 1, .3
background_color: .2, .2, .2, 0
#on_release: app.root.ids.rv.adjust_vote_state(root)
canvas.before:
Color:
rgba: (.1,.1,.1,1)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [6,]
canvas:
Color:
rgba: (.2,.2,.2,1)
Line:
width: 1.4
rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
Label:
id: vote_count
text: root.votes_
size_hint: 1, .4
multiline: False
ToggleButton:
id: button_down
on_state: app.down_vote(self, 'all')
group: root.group_
#state: root.vote_state_down
text: "DOWN"
color: (1,1,1,1) if self.state=='normal' else (.8,0,0,1)
font_size: "10dp"
size_hint: 1, .3
background_color: .2, .2, .2, 0
canvas.before:
Color:
rgba: (.1,.1,.1,1)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [6,]
canvas:
Color:
rgba: (.2,.2,.2,1)
Line:
width: 1.4
rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
Label:
id: label
text: root.text_
padding: "10dp", "12dp"
size_hint: .9, None
height: self.texture_size[1]
font_size: "12dp"
text_size: self.width, None
color: 0,0,0,1
multiline: True
markup: True
#on_texture_size: root.update_message_size(root.message_id, self.texture_size)
pos: self.x, self.y
canvas.before:
Color:
rgba: (0.8, 0.8, 0.8, 1)
RoundedRectangle:
size: self.texture_size
radius: [5, 5, 5, 5]
pos: self.x, self.y
canvas:
Color:
rgba:0.8,0,0,1
Line:
width: 1.4
rounded_rectangle:(self.x,self.y,self.width,self.height, 5)
BoxLayout:
orientation: 'vertical'
BoxLayout:
size_hint: 1, .1
orientation: 'horizontal'
Button:
text: 'Add widget to RV list'
on_release: rv.add()
ToggleButton:
id: active
text: 'active'
on_state: app.add_log(self)
RV:
id: rv
viewclass: 'NewPostGrid' # The view class is TwoButtons, defined above.
data: app.rv_data_list # the data is a list of dicts defined below in the RV class.
scroll_type: ['bars', 'content']
#on_scroll_y: app.fill_data(self, box)
scrollable_distance: box.height - self.height
bar_width: dp(2)
RecycleBoxLayout:
id: box
# This layout is used to hold the Recycle widgets
# default_size: None, dp(48) # This sets the height of the BoxLayout that holds a TwoButtons instance.
key_size: '_size'
default_size_hint: 1, None
size_hint_y: None
spacing: '16dp'
height: self.minimum_height # To scroll you need to set the layout height.
orientation: 'vertical'
padding: ['10dp', '20dp']
Image of app running on windows
Video of app running on android
Video of full app running (The code for the recycleview is identical)
On the first screen, I have a checkbox that asks the user if they want their image in landscape mode. On the second screen, my code currently uses a canvas and rotates the image 90 degrees. If that box is not checked, that means the user wants the image to be portrait mode, and I need that canvas to be cleared.
my.kv
WindowManager:
MainWindow:
SecondWindow:
<MainWindow>:
id: main_window
name: "main"
BoxLayout:
orientation: "vertical"
size: root.width, root.height
padding: 50
Label:
text: "Email"
color: 0,0,0,1
font_size: 32
BoxLayout:
orientation: "horizontal"
Label:
text: "Email Address:"
color: 0,0,0,1
TextInput:
size_hint_y: None
pos_hint: {'center_y': .5}
height: 38
multiline: True
padding: 10
BoxLayout:
orientation: "horizontal"
Label:
text: "Display Landscape Mode?"
color: 0,0,0,1
CheckBox:
id: checkbox_confirm_mode
on_active:
root.checkbox_click_mode(self, self.active)
pos_hint: {'center_x': .5}
BoxLayout:
orientation: "horizontal"
Label:
text: "Stretch image to fill screen?"
color: 0,0,0,1
CheckBox:
id: checkbox_confirm_stretch
on_active:
root.checkbox_click_stretch(self, self.active)
pos_hint: {'center_x': .5}
BoxLayout:
orientation: "horizontal"
Label:
text: "I double-checked that my email is typed correctly:"
color: 0,0,0,1
CheckBox:
id: checkbox_confirm_email
on_active:
root.checkbox_click(self, self.active)
root.disable_button()
pos_hint: {'center_x': .5}
BoxLayout
orientation: "vertical"
Button:
id:submit_button
text: "Submit"
disabled: True
size_hint: (0.2, None)
pos_hint: {'center_x': .5}
height: 50
on_release:
app.root.current = "second"
root.manager.transition.direction = "left"
<SecondWindow>:
id: second_window
name: "second"
canvas:
Rotate:
angle: 90
origin: self.center
Image:
source: 'Troy.png'
keep_ratio: True
allow_stretch: False
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
from kivy.properties import ObjectProperty
from kivy.config import Config
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
class MainWindow(Screen):
def on_pre_enter(self):
Window.size = (750,400)
Window.clearcolor = (1, 1, 1, 1)
def checkbox_click(self, instance, value):
return value
def checkbox_click_mode(self, instance, value):
return value
def checkbox_click_stretch(self, instance, value):
return value
def clear_canvas(self):
self.canvas.clear()
return
def disable_button(self):
if self.ids.checkbox_confirm_email.active == False:
self.ids.submit_button.disabled = True
else:
self.ids.submit_button.disabled = False
class SecondWindow(Screen):
def on_pre_enter(self):
Window.size = (500,500)
Window.clearcolor = (0,0,0,0)
pass
class WindowManager(ScreenManager):
pass
class MyMainApp(App):
def build(self):
return kv
kv = Builder.load_file("my.kv")
if __name__ == "__main__":
MyMainApp().run()
For your current design you can control it from outside as,
First set properties in SecondWindow for setting the angle, stretch instruction etc. and then control them depending on the state of those (weak reference of the check boxes) checkbox_confirm_mode etc. from the MainWindow as,
class SecondWindow(Screen):
angle = NumericProperty(0)
allow_strech = BooleanProperty(False)
def on_pre_enter(self):
screen = self.manager.get_screen("main")
angle_value = screen.ids.checkbox_confirm_mode.active
stretch_value = screen.ids.checkbox_confirm_stretch.active
self.angle = 90*angle_value # Less calculation.
self.allow_strech = stretch_value # Less calculation.
Window.size = (500,500)
...
Now in the kvlang of SecondWindow,
<SecondWindow>:
id: second_window
name: "second"
canvas:
Rotate:
angle: root.angle
origin: self.center
Image:
source: 'Troy.png'
keep_ratio: True
allow_stretch: root.allow_strech
I'm trying to make a game in kivy and I'm facing an issue. Whenever I click on my "start game"
button on my gamescreen it says
line 46, in move_ball
ball = self.root.ids.ball
File "kivy\properties.pyx", line 864, in kivy.properties.ObservableDict.__getattr__
AttributeError: 'super' object has no attribute '__getattr__'
My main.py
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.image import Image
from kivy.core.audio import SoundLoader
from kivy.clock import Clock
from kivy.properties import NumericProperty
class HomeScreen(Screen):
pass
def play_sound(self):
sound = SoundLoader.load('button press sound.wav.')
if sound:
sound.play()
sound = SoundLoader.load('Crowd sound effect.wav')
sound.loop = True
sound.play()
class GameScreen(Screen):
pass
class Ball(Image):
velocity = NumericProperty(0)
def on_touch_down(self, touch):
self.source = "icons/ball1.png"
self.velocity = 275
super().on_touch_down(touch)
def on_touch_up(self, touch):
self.source = "icons/ball1.png"
super().on_touch_up(touch)
class MainApp(App):
GRAVITY = 300
def move_ball(self, time_passed):
ball = self.root.ids.ball
ball.y = ball.y + ball.velocity * time_passed
ball.velocity = ball.velocity - self.GRAVITY * time_passed
def start_game(self):
Clock.schedule_interval(self.move_ball, 1/60.)
def change_screen(self, screen_name):
# Get the screen manager from the kv file
screen_manager = self.root.ids['screen_manager']
screen_manager.current = screen_name
MainApp().run()
homescreen.kv
#:import utils kivy.utils
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
<HomeScreen>:
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#39B3F2")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .9
Image:
source: "icons/keepyup.png"
FloatLayout:
Button:
font_size: dp(20)
font_name: 'SackersGothicStd-Medium.otf'
text: "PLAY"
color: "gold"
pos_hint: { "center_x": .5, "center_y": .3}
size: 80, 55
size_hint: None, None
background_normal: ''
background_color: (57/255.0, 179/255.0, 242/255.0, .15)
on_press:
on_release:
root.play_sound()
root.manager.transition = FadeTransition()
app.change_screen("game_screen")
gamescreen.kv
#:import utils kivy.utils
<GameScreen>:
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#39B3F2")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .1
Image:
source: "icons/sun.png"
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .2
Image:
source: "icons/clouds.png"
GridLayout:
rows: 1
pos_hint: {"bottom": 1, "left": 1}
size_hint: 1, .5
Image:
source: "icons/Field4.png"
allow_stretch: True
keep_ratio: False
pos: self.pos
Button:
text: "Start game"
size_hint: None, None
on_release:
app.start_game()
Ball:
source: "icons/ball1.png"
size_hint: None, None
size: 100, 100
pos: 20, (root.height - 90) / 2.0
id: ball
main.kv
#:include kv/homescreen.kv
#:include kv/gamescreen.kv
GridLayout:
cols: 1
ScreenManager:
id: screen_manager
HomeScreen:
name: "home_screen"
id: home_screen
GameScreen:
name: "game_screen"
id: game_screen
I made another kivy project and tried the same thing without the screens and it doesn't show any errors! here's my code
My main.py
from kivy.app import App
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.properties import NumericProperty
class Ball(Image):
velocity = NumericProperty(0)
def on_touch_down(self, touch):
self.source = "ball1.png"
self.velocity = 275
super().on_touch_down(touch)
def on_touch_up(self, touch):
self.source = "ball1.png"
super().on_touch_up(touch)
class MainApp(App):
GRAVITY = 300
def move_ball(self, time_passed):
ball = self.root.ids.ball
ball.y = ball.y + ball.velocity * time_passed
ball.velocity = ball.velocity - self.GRAVITY * time_passed
def start_game(self):
Clock.schedule_interval(self.move_ball, 1/60.)
MainApp().run()
main.kv
#:import utils kivy.utils
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#39B3F2")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .1
Image:
source: "sun.png"
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .2
Image:
source: "clouds.png"
GridLayout:
rows: 1
pos_hint: {"bottom": 1, "left": 1}
size_hint: 1, .5
Image:
source: "Field4.png"
allow_stretch: True
keep_ratio: False
pos: self.pos
Button:
text: "Start game"
size_hint: None, None
on_release:
app.start_game()
Ball:
source: "ball1.png"
size_hint: None, None
size: 100, 100
pos: 20, (root.height - 90) / 2.0
id: ball
What's wrong with my first project code? because my second one seems to work without getting any errors.
Although the traceback isn't fully shown in this question, this should be what it looks like at the end:
File "C:\Users\weebi\Desktop\TestingZone\test1\kv\homescreen.kv", line 37, in <module>
app.change_screen("game_screen")
File "C:\Users\weebi\Desktop\TestingZone\test1\main.py", line 53, in change_screen
screen_manager = self.root.ids.screen_manager
File "kivy\properties.pyx", line 964, in kivy.properties.ObservableDict.__getattr__
AttributeError: 'super' object has no attribute '__getattr__'
The reason for the AttributeError: 'super' object has no attribute '__getattr__' exception usually has to do with a non-existence id in a .ids list. Here's a better version of your change_screen method that works as intended:
def change_screen(self, screen_name):
self.root.current = screen_name
Also fixed your main.kv so that it switches between screens properly
#:include kv/homescreen.kv
#:include kv/gamescreen.kv
ScreenManager:
id: screen_manager
HomeScreen:
name: "home_screen"
id: home_screen
GameScreen:
name: "game_screen"
id: game_screen
By the way, while this isn't necessarily related to the question, before trying your code, I noticed this line:
ball = self.root.ids.ball
I went ahead and checked the main.kv file and to my surprise, there's no root widget with the id of ball but there's one in the game_screen object, so my OCD acted up and I fixed it for you:
def move_ball(self, time_passed):
ball = self.root.ids.game_screen.ids.ball
ball.y = ball.y + ball.velocity * time_passed
ball.velocity = ball.velocity - self.GRAVITY * time_passed
The main code <main.py>
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.uix.behaviors import ButtonBehavior
class Background(Widget):
cloud_texture = ObjectProperty(None)
tree1_texture = ObjectProperty(None)
tree2_texture = ObjectProperty(None)
grass_texture = ObjectProperty(None)
bush_texture = ObjectProperty(None)
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.cloud_texture = Image(source="cloud.png").texture
self.cloud_texture.wrap = 'repeat'
self.cloud_texture.uvsize = (Window.width/ self.cloud_texture.width, -1)
self.tree1_texture = Image(source="tree_1.png").texture
self.tree1_texture.wrap = 'repeat'
self.tree1_texture.uvsize = (Window.width/ self.tree1_texture.width, -1)
self.tree2_texture = Image(source="tree_2.png").texture
self.tree2_texture.wrap = 'repeat'
self.tree2_texture.uvsize = (Window.width/ self.tree2_texture.width, -1)
self.grass_texture = Image(source="grass.png").texture
self.grass_texture.wrap = 'repeat'
self.grass_texture.uvsize = (Window.width/ self.grass_texture.width, -1)
self.bush_texture = Image(source="bush.png").texture
self.bush_texture.wrap = 'repeat'
self.bush_texture.uvsize = (Window.width/ self.bush_texture.width, -1)
def on_size(self, *args):
self.cloud_texture.uvsize = (self.width / self.cloud_texture.width, -1)
self.tree1_texture.uvsize = (self.width / self.tree1_texture.width, -1)
self.tree2_texture.uvsize = (self.width / self.tree2_texture.width, -1)
self.grass_texture.uvsize = (self.width / self.grass_texture.width, -1)
self.bush_texture.uvsize = (self.width / self.grass_texture.width, -1)
def scroll_textures(self, time_passed):
self.cloud_texture.uvpos = ((self.cloud_texture.uvpos[0] + time_passed/8.0)% Window.width , self.cloud_texture.uvpos[1])
self.tree1_texture.uvpos = ((self.tree1_texture.uvpos[0] + time_passed/2.0)% Window.width , self.tree1_texture.uvpos[1])
self.tree2_texture.uvpos = ((self.tree2_texture.uvpos[0] + time_passed/2.0)% Window.width , self.tree2_texture.uvpos[1])
self.grass_texture.uvpos = ((self.grass_texture.uvpos[0] + time_passed/8.0)% Window.width , self.grass_texture.uvpos[1])
self.bush_texture.uvpos = ((self.bush_texture.uvpos[0] + time_passed/8.0)% Window.width , self.bush_texture.uvpos[1])
texture = self.property('cloud_texture')
texture.dispatch(self)
texture = self.property('tree1_texture')
texture.dispatch(self)
texture = self.property('tree2_texture')
texture.dispatch(self)
texture = self.property('grass_texture')
texture.dispatch(self)
texture = self.property('bush_texture')
texture.dispatch(self)
print("scroll")
pass
def on_stop(self):
pass
class StartWindow(Screen):
def __init__(self, **kwargs):
super(StartWindow,self).__init__(**kwargs)
pass
class GameWindow(Screen):
def __init__(self, **kwargs):
super(GameWindow,self).__init__(**kwargs)
pass
class OptionsWindow(Screen):
def __init__(self, **kwargs):
super(OptionsWindow,self).__init__(**kwargs)
pass
class WindowManager(ScreenManager):
pass
class MainApp(App):
def __getattr__(self, attr):
return super().__getattr__(attr)
def on_start(self):
Clock.schedule_interval(self.root.ids.background.scroll_textures,1/60.)
pass
MainApp().run()
The line "Clock.schedule_interval(g.ids.background.scroll_textures,1/60.)" is not working. I am not understanding the fundamental way to call the id from the kv file.
main.kv file
WindowManager:
StartWindow:
GameWindow:
OptionsWindow:
<StartWindow>:
name: "start"
id: startwindow
FloatLayout:
cols:4
Image:
source: 'Flying_whale.png'
allow_stretch: True
keep_ratio: False
Button:
pos_hint:{"x":0,"y":0.50}
background_color: 0,0,0,0
color: 0,0,0,1
size_hint: 0.4, 0.1
font_size: (root.width**2 + root.height**2) / 17**3.3
text: "Start!"
id: set_event
on_press:
root.manager.transition.direction = "down"
root.on_start()
Button:
pos_hint:{"x":0.15,"y":0.25}
background_color: 0,0,0,0
color: 0,0,0,1
size_hint: 0.4, 0.1
font_size: (root.width**2 + root.height**2) / 17**3.5
text: "Options"
on_release:
root.manager.transition.direction = "right"
app.root.current = "options"
Button:
pos_hint:{"x":0.15,"y":0.10}
background_color: 0,0,0,0
color: 0,0,0,1
size_hint: 0.4, 0.1
font_size: (root.width**2 + root.height**2) / 17**3.5
text: "Exit"
<GameWindow>
name:"game"
id: gamewindow
FloatLayout:
Background:
id: background
canvas.before:
Rectangle:
size: self.size
pos: self.pos
source: "sky.png"
Rectangle:
size: self.width, 150
pos: self.pos[0],self.pos[1] + self.height - 138
texture: self.cloud_texture
Rectangle:
size: self.width, 160
pos: self.pos[0],self.pos[1]
texture: self.grass_texture
Rectangle:
size: self.width, 100
pos: self.pos[0]-10,self.pos[1] + 145
texture: self.tree1_texture
Rectangle:
size: self.width, 100
pos: self.pos[0]+30,self.pos[1] + 160
texture: self.tree2_texture
Rectangle:
size: self.width, 20
pos: self.pos[0],self.pos[1] + 155
texture: self.bush_texture
Label:
id: score
size_hint_y : None
height : 96
text: "0"
font_size: 40
Button:
pos_hint:{"x":0,"y":0.25}
background_color: 0,0,0,0
color: 0,0,0,1
size_hint: 0.4, 0.1
font_size: (root.width**2 + root.height**2) / 17**3.3
text: "Back"
id: set_event
on_release:
<OptionsWindow>:
name: "options"
FloatLayout:
cols:4
Image:
source: 'Flying_whale.png'
allow_stretch: True
keep_ratio: False
Button:
pos_hint:{"x":0.15,"y":0.20}
background_color: 0,0,0,0
color: 0,0,0,1
size_hint: 0.4, 0.1
font_size: (root.width**2 + root.height**2) / 17**3.5
text: "Back"
on_release:
root.manager.transition.direction = "left"
app.root.current = "start"
Button:
pos_hint:{"x":0.15,"y":0.35}
background_color: 0,0,0,0
color: 1,1,1,1
size_hint: 0.4, 0.1
font_size: (root.width**2 + root.height**2) / 17**3.5
text: "Mode : Night / Day"
The error is given by:
AttributeError: 'super' object has no attribute 'getattr'
I know it is a simple understanding problem but I am not able to work around it. Thank you
The problem is that the id: background is defined in the GameWindow rule in the kv. According to the documentation:
When the kv file is processed, weakrefs to all the widgets tagged with
ids are added to the root widget’s ids dictionary.
While the term root widget is over used and has different meanings depending on context, here it means the rule that contains the id. So the background id will only appear in the ids of the GamWindow widget.
Knowing that, your questionable line of code can be replaced with:
Clock.schedule_interval(self.root.get_screen('game').ids.background.scroll_textures,1/60.)
The above code uses get_screen() to access the GameWindow.
Problem:
Is there a way to fire an event if a multiline = True TextInput lost its focus?
Background:
I have tried on_touch_up function. But it returns the instances of all my TextInputs, not just the current widget. I tried text_validate_unfocus = False, with the same result.
Code:
import kivy
kivy.require("1.10.1")
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty, NumericProperty
from kivy.uix.textinput import TextInput
from kivy.uix.bubble import Bubble
from kivy.lang import Builder
from kivy.storage.jsonstore import JsonStore
Builder.load_string('''
#: import Window kivy.core.window.Window
<Button>:
background_normal: ''
<Label>:
canvas.before:
Color:
rgba: (0,0.59,0.36,1)
Rectangle:
pos: self.pos
size: self.size
<TextInput>:
hint_text: 'Nuwe nota'
font_size: self.height / 4.5 if self.focus else self.height / 3
background_normal: ''
background_active: ''
foreground_color: (0,0.61,0.36,1) if self.focus else (0.71,0.75,0.71,1)
unfocus_on_touch: False
canvas.after:
Color:
rgb: (0,0,0,1)
Line:
points: self.pos[0] , self.pos[1], self.pos[0] + self.size[0], self.pos[1]
size_hint_y: None
height: Window.height / 6 if self.focus else Window.height / 12
<ChoiceBubble>:
orientation: 'horizontal'
size_hint: (None, None)
size: (160, 120)
pos_hint: {'top': 0.2, 'right': 0.8}
arrow_pos: 'top_left'
BubbleButton:
text: 'Save'
BubbleButton:
text: 'Encrypt..'
BubbleButton:
text: 'Delete'
on_release: root.del_txt_input()
<Notation>:
canvas:
Color:
rgba: (0,0.43,0.37,1)
Rectangle:
pos: self.pos
size: self.size
Label:
pos_hint: {'top': 1, 'right': 0.8}
size_hint: [0.8, None]
height: Window.height / 15
Button:
color: (0,0,0,1)
pos_hint: {'top': 1, 'right': 0.9}
size_hint: [0.1, None]
height: Window.height / 15
Image:
source: 'gear_2.png'
center_y: self.parent.center_y
center_x: self.parent.center_x
size: self.parent.width /1.5, self.parent.height/ 1.5
allow_stretch: True
Button:
color: (0,0,0,1)
pos_hint: {'top': 1, 'right': 1}
size_hint: [0.1, None]
height: Window.height / 15
on_release: root.add_input()
Image:
source: 'plus_text12354.png'
center_y: self.parent.center_y
center_x: self.parent.center_x
size: self.parent.width /1.5, self.parent.height/ 1.5
allow_stretch: True
ScrollView:
size_hint_y: None
size: Window.width, Window.height
pos_hint: {'top': 0.92, 'right': 1}
GridLayout:
id: text_holder
cols: 1
pos_hint: {'top': 0.92, 'right': 1}
padding: 4
size_hint_x: 1
size_hint_y: None
height: self.minimum_height
''')
class ChoiceBubble(Bubble):
pass
class TextInput(TextInput):
got_txt = ObjectProperty(None)
def on_touch_up(self, touch):
if not self.collide_point(*touch.pos):
self.text_validate_unfocus = False
note = Notation()
note.show_bubble
self.got_txt=note.que_txt_input(self)
return super(TextInput, self).on_touch_up(touch)
class Notation(FloatLayout):
which_txt = ObjectProperty(None)
new_txt = ObjectProperty(None)
cnt = NumericProperty(0)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.the_file=JsonStore('txt_input.json')
self.cnt = self.the_file.count()
lst = self.the_file.keys
def add_input(self):
txt_hld = self.ids.text_holder
self.cnt += 1
self.new_txt = TextInput(id=str(self.cnt))
self.the_file.put(str(self.cnt), the_id=str(self.cnt), the_input='')
txt_hld.add_widget(self.new_txt)
def que_txt_input(self, instance):
self.which_txt = instance
print(instance.text, instance)
return instance
def del_txt_input(self):
print(self.which_txt)
def the_file(self, notestore):
self.notestore = notestore
def show_bubble(self):
self.add_widget(ChoiceBubble())
def get_store(self):
the_keys = list(self.the_file.keys)
print(the_keys)
return the_keys
class theNoteApp(App):
title = 'My Notes'
def build(self):
return Notation()
if __name__ == '__main__':
theNoteApp().run()
Desired Result:
Once the focus is lost I want a bubble widget to be added to the top of my root class. This will give the user the option to save, encrypt or delete the TextInput id and text that just lost its focus, to a JSON file.
Problems
Multiple instances of object, Notation. One from build() method (return Notation()) in App class, and another instance created in on_touch_up() method (note = Notation()) whenever on_touch_up event is fired. Those instances created in on_touch_up() method does not has a visible view i.e. it won't show up in the window.
AttributeError: 'ChoiceBubble' object has no attribute 'del_txt_input'
Text in ChoiceBubble not visible i.e. the default text color is white on white background.
Solutions
Use App.get_running_app().root to get the instantiated root.
Replace root.del_txt_input() with app.root.del_txt_input()
Add color: 0, 0, 0, 1 to class rule, <Label>:
Use on_focus event to display BubbleButton
Snippets - kv
<Label>:
color: 0, 0, 0, 1
...
<ChoiceBubble>:
...
BubbleButton:
text: 'Delete'
on_release: app.root.del_txt_input()
Snippets - py file
class TextInput(TextInput):
got_txt = ObjectProperty(None)
def on_focus(self, instance, value):
if not value: # defocused
note = App.get_running_app().root
note.show_bubble()
self.got_txt = note.que_txt_input(self)
Output
The on_focus event will fire when the focus boolean changes.
I have tried on_touch_up function. But it returns the instances of all my TextInputs, not just the current widget.
This is because you didn't write any code that would limit it to the widget you care about, you can do this if you want to.