Why does my game window not work in Kivy? - python

i am trying to build a main menu in my app that then links you to the game, i have the game working the issue is that when you press play it takes you to the game that does not move however i used print statements to check and in fact the game is running in the background from the moment you start the app.
Here is the Py Code:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty
from kivy.vector import Vector
from kivy.uix.floatlayout import FloatLayout
from kivy.uix import label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.config import Config
from kivy.uix.boxlayout import BoxLayout
class WindowManager(ScreenManager):
def StartUp(self):
self.current = "Menu"
pass
class MenuWindow(Screen):
pass
class PongBall(Widget):
velocity_y= NumericProperty(0)
velocity_x= NumericProperty(0)
velocity=ReferenceListProperty(velocity_x,velocity_y)
def move(self):
self.pos = Vector(self.velocity) +self.pos
class PongPad(Widget):
score=NumericProperty(0)
def Check_bounce(self,ball):
if self.collide_widget(ball) :
vx,vy= ball.velocity
bounced= Vector(-1* vx, vy )
speedup= abs(((ball.velocity_x *0.1) -(ball.center_y - self.center_y)) *0.002)
vel = bounced * (speedup +1.1)
offset=(((ball.center_y - self.center_y)/2) - (ball.velocity_x /2)) *0.1
if (ball.center_y - self.center_y) > 0:
ball.velocity=vel.x,vel.y
ball.velocity_y= 2
else:
ball.velocity= vel.x,vel.y
ball.velocity_y= -2
class Game(Screen):
ball = ObjectProperty(None)
player_1 = ObjectProperty(None)
player_2 = ObjectProperty(None)
def serve_ball(self,vel=(1,0.5)):
print("234Served")
self.ball.velocity= vel
self.ball.center = self.center
def Check_Top_Bottom(self):
#Check bottom collion
if self.ball.y <0:
self.ball.velocity_y= abs(self.ball.velocity_y)
#Check top colision
if self.ball.y+50> self.height:
self.ball.velocity_y = -abs(self.ball.velocity_y)
def Check_if_score(self):#Score
if self.ball.x >self.width:
self.player_1.score +=1
self.serve_ball()
if self.ball.x+50 <0:
self.player_2.score += 1
self.serve_ball()
def update(self,dt):
self.ball.move()
self.Check_Top_Bottom()
self.Check_if_score()
self.player_1.Check_bounce(self.ball)
self.player_2.Check_bounce(self.ball)
print(WindowManager().children)
def on_touch_move(self,touch):
if touch.x > self.width/2:
self.player_2.center_y = touch.y
else:
self.player_1.center_y= touch.y
kv= Builder.load_file("myMenu.kv")
class myMenuApp(App):
def build(self):
game=Game()
print(WindowManager().current_screen)
game.serve_ball()
Clock.schedule_interval(game.update, 1.0/60.0)
return game
if __name__ == "__main__":
myMenuApp().run()
And here is the KV code:
WindowManager:
MenuWindow:
Game:
<MenuWindow>:
name: "Menu"
FloatLayout:
Button:
size_hint: 0.2,0.05
pos_hint: {"x":0.39,"y":0.75}
text:"Play"
on_release:
app.root.current= "Game"
root.manager.transition.direction= "left"
Button:
size_hint: 0.2,0.05
pos_hint: {"x":0.39,"y":0.7}
text:"Settings"
Button:
size_hint: 0.2,0.05
pos_hint: {"x":0.39,"y":0.65}
text:"High Score"
Button:
size_hint: 0.2,0.05
pos_hint: {"x":0.39,"y":0.6}
text:"Quit"
<PongBall>
size_hint: None, None
size: 50,50
canvas:
Ellipse:
pos:self.pos
size:self.size
<PongPad>
size_hint: None, None
size:25,150
canvas:
Rectangle:
pos:self.pos
size:self.size
<Game>:
name:"Game"
ball: Pong_ball
player_1: Player1
player_2: Player2
canvas:
Rectangle:
pos:self.center_x -5,0
size: 15,root.height
PongPad:
id: Player2
pos:root.width-25,root.center_y-75
PongPad:
id: Player1
pos:0,root.center_y-75
PongBall:
id: Pong_ball
center: self.parent.center
I have tried without the Builder but pretty much same issue, i also tried calling the Screen Manager in the return functions but that just gives me a blank screen.
Another thing is i tried checking the current screen when on the main menu and it outputs "NONE"
Thank you

A few issues:
When you execute print(WindowManager().current_screen) you are creating a new WindowManager and printing its current_screen. That is unrelated to any WindowManager in your GUI.
You are loading your kv file, which builds a GUI with a WindowManager that it returns. But you ignore that returned WindowManager and the build() method returns a Game instance, which becomes the root of your GUI (with no WindowManager).
You can use the on_enter() method of Screen to start the Game when it becomes the current Screen, like this:
class Game(Screen):
ball = ObjectProperty(None)
player_1 = ObjectProperty(None)
player_2 = ObjectProperty(None)
def on_enter(self, *args):
# start the game
self.serve_ball()
Clock.schedule_interval(self.update, 1.0 / 60.0)
And in the build() method of your App, just return the GUI that is build by the Builder:
class myMenuApp(App):
def build(self):
return kv

Related

How to handle clock module with multiple screens in Kivy?

I tried to move a ball around an origin (like the moon in its orbit around the earth). At first I created the ball object inside GameScreen. Then I got the AttributeError that my object does not have "move" attribute. So i created another widget called MainGame as a parent of the ball object. Now the error is gone but the ball does not actively moving. I could not figure out what i'm missing since I'm pretty new to Kivy. I guess it's about Clock module and my custom update function. All answers and helps are appreciated, thanks!
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.lang import Builder
import math
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty
from kivy.clock import Clock
class GameBall(Widget):
org_x, org_y = 300, 300
dist = 100
ang_deg = 0
pos_x = NumericProperty(org_x+dist)
pos_y = NumericProperty(org_y)
pos = ReferenceListProperty(pos_x, pos_y)
def move(self):
self.ang_deg += 5
self.ang_rad = math.radians(self.ang_deg)
self.pos_x, self.pos_y = self.org_x + self.dist*math.cos(self.ang_rad), self.org_y + self.dist*math.sin(self.ang_rad)
class MainGame(Widget):
game_ball = GameBall()
def update(self, dt):
self.game_ball.move()
class ScreenManagement(ScreenManager):
pass
class MenuScreen(Screen):
pass
class GameScreen(Screen):
pass
GUI = Builder.load_file("gui.kv")
class MobileGameApp(App):
def build(self):
game = MainGame()
Clock.schedule_interval(game.update, 1.0/60.0)
return GUI
if __name__ == "__main__":
MobileGameApp().run()
---- Kv file:
ScreenManagement:
MenuScreen:
GameScreen:
<MenuScreen>:
name: "menu_screen"
Button:
size_hint: .2, .2
pos: root.width/2 - self.width/2, root.height/2 - self.height/2
text: "Play Game"
on_release:
root.manager.transition.direction = "left"
root.manager.current = "game_screen"
<GameScreen>:
game_ball: main_game.game_ball
name: "game_screen"
Button:
size_hint: .2, .2
pos_hint: {'top': 1}
text: "Go to menu"
on_release:
root.manager.transition.direction = "right"
root.manager.current = "menu_screen"
MainGame:
id: main_game
GameBall:
id: game_ball
center: self.parent.center
canvas:
Ellipse:
size: 50, 50
pos: self.pos
Several problems with your code. One important thing I see in your code is creating new object instances when you actually want to reference existing instances:
game = MainGame()
and
game_ball = GameBall()
are creating new instances of MainGame and GameBall. Neither of those new instances are in your GUI, so any changes you make to those new instances will have no effect on your GUI.
So, you need to get references to the existing MainGame and GameBall. Those are the instances created by your kv file. One way to do that is to make the following changes to your code.
You can simplify GameBall as:
class GameBall(Widget):
org_x, org_y = 300, 300
dist = 100
ang_deg = 0
def move(self):
self.ang_deg += 5
self.ang_rad = math.radians(self.ang_deg)
self.pos = (self.org_x + self.dist*math.cos(self.ang_rad), self.org_y + self.dist*math.sin(self.ang_rad))
And MainGame can become:
class MainGame(Widget):
game_ball = ObjectProperty(None)
def update(self, dt):
self.game_ball.move()
And starting the animation now looks like:
class MobileGameApp(App):
def build(self):
Clock.schedule_once(self.start_updates)
return GUI
def start_updates(self, dt):
Clock.schedule_interval(self.root.get_screen('game_screen').ids.main_game.update, 1.0/60)
And then, to simplify accessing the GameBall, I added a game_ball to the kv as:
MainGame:
game_ball: game_ball
id: main_game
GameBall:
id: game_ball
center: self.parent.center
canvas:
Ellipse:
size: 50, 50
pos: self.pos

Why does rescheduling Clock intervals cause image positions to reset?

Within the program, one can see that as the ball bounces around, one has the ability to open settings page from the top right corner. Doing so pauses the ball's motion and opens the settings page.
Sorry if this is an obvious question, but I am again stumped by the strange inner workings of Kivy, and the docs aren't much use on these types of issues.
Problem
Ball always start at centre position. Want ball to continue / resume from previous position before switching screen?
Steps to recreate problem
Click on label, "Tap to start". Ball started bouncing from centre position
Click on cogwheel image. Settings screen is displayed
Click on "x" to close Settings screen. Ball started bouncing from centre position.
Code:
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy import Config
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition,\
SlideTransition
from kivy.uix.widget import Widget
from kivy.animation import Animation
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.clock import Clock
from kivy.vector import Vector
from random import randint
Builder.load_string('''
<Ball>:
size_hint: None, None
source: '58-Breakout-Tiles.png'
pos: self.pos
size: 15, 15
<SettingsScreen>:
close: close
AnchorLayout:
anchor_x: 'left'
anchor_y: 'top'
Image:
id: close
size_hint: .03, .03
source: 'grey_crossGrey.png'
GridLayout:
cols: 2
Label:
font_name: 'vgafix.fon'
text: 'Music: '
Switch:
active: True
Label:
font_name: 'vgafix.fon'
text: 'Sounds: '
Switch:
active: True
<MenuScreen>:
cog: cog
AnchorLayout:
anchor_x: 'right'
anchor_y: 'top'
Image:
id: cog
size_hint: .03, .03
source: 'settings-cog.png'
BoxLayout:
orientation: 'vertical'
Image:
source: 'brickbreaker log.png'
Label:
font_name: 'vgafix.fon'
text: 'Tap to start'
<GameScreen>:
ball: ball
cog: cog
AnchorLayout:
anchor_x: 'right'
anchor_y: 'top'
Image:
id: cog
size_hint: .03, .03
source: 'settings-cog.png'
Ball:
id: ball
size_hint: None, None
center: self.parent.center
''')
Config.set('graphics', 'multisamples', '0')
class Ball(Image):
velocityX, velocityY = NumericProperty(0), NumericProperty(0)
velocity = ReferenceListProperty(velocityX, velocityY)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
class Player(Widget):
pass
class Brick(Widget):
pass
class SettingsScreen(Screen):
def __init__(self, **kwargs):
super(SettingsScreen, self).__init__(**kwargs)
self.previous = False
def on_touch_down(self, touch):
if self.close.collide_point(*touch.pos):
sm.transition = SlideTransition(direction = 'right')
if self.previous == 'game':
sm.get_screen('game').interval()
sm.current = self.previous
class MenuScreen(Screen):
def __init__(self, **kwargs):
super(MenuScreen, self).__init__(**kwargs)
def on_touch_down(self, touch):
if self.cog.collide_point(*touch.pos):
sm.transition = SlideTransition(direction = 'left')
sm.get_screen('settings').previous = 'menu'
sm.current = 'settings'
else:
sm.transition = FadeTransition()
sm.current = 'game'
class GameScreen(Screen):
def __init__(self, **kwargs):
super(GameScreen, self).__init__(**kwargs)
self.initBall()
self.interval = Clock.schedule_interval(self.update, 1.0/60.0)
def on_touch_down(self, touch):
if self.cog.collide_point(*touch.pos):
sm.transition = SlideTransition(direction = 'left')
sm.get_screen('settings').previous = 'game'
self.interval.cancel()
sm.current = 'settings'
def initBall(self):
self.ball.center = self.center
self.ball.velocity = Vector(0, 4).rotate(randint(0, 360))
def update(self, dt):
self.ball.move()
if (self.ball.y < 0) or (self.ball.y > self.height-15):
self.ball.velocityY *= -1
# bounce off left and right
if (self.ball.x < 0) or (self.ball.x > self.width-15):
self.ball.velocityX *= -1
sm = ScreenManager(transition = FadeTransition())
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(GameScreen(name='game'))
sm.add_widget(SettingsScreen(name='settings'))
class BrickBreakerInsanityApp(App):
def build(self):
return sm
if __name__ == '__main__':
BrickBreakerInsanityApp().run()
Code assets (required):
https://drive.google.com/open?id=1GAnv5DfjNUuAXTybmsan90Dm0OuSVOfb
https://i.stack.imgur.com/rR799.png
https://i.stack.imgur.com/ngYvL.png
https://i.stack.imgur.com/AuxI3.png
https://i.stack.imgur.com/ypd7C.png
Root cause
The SlideTransition and direction is causing the ball to start at the centre.
Solution
Remove all references to SlideTransition.
Example
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.image import Image
from kivy import Config
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition,
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty, StringProperty
from kivy.clock import Clock
from kivy.vector import Vector
from random import randint
Builder.load_string('''
<Ball>:
size_hint: None, None
source: './assets/icons/58-Breakout-Tiles.png'
pos: self.pos
size: 15, 15
<SettingsScreen>:
close: close
AnchorLayout:
anchor_x: 'left'
anchor_y: 'top'
Image:
id: close
size_hint: .03, .03
source: './assets/icons/grey_crossGrey.png'
GridLayout:
cols: 2
Label:
font_name: "./assets/fonts/vgafix.fon"
text: 'Music: '
Switch:
active: True
Label:
font_name: "./assets/fonts/vgafix.fon"
text: 'Sounds: '
Switch:
active: True
<MenuScreen>:
cog: cog
AnchorLayout:
anchor_x: 'right'
anchor_y: 'top'
Image:
id: cog
size_hint: .03, .03
source: './assets/icons/settings-cog.png'
BoxLayout:
orientation: 'vertical'
Image:
source: "./assets/icons/brickbreaker log.png"
Label:
font_name: "./assets/fonts/vgafix.fon"
text: 'Tap to start'
<GameScreen>:
ball: ball
cog: cog
AnchorLayout:
anchor_x: 'right'
anchor_y: 'top'
Image:
id: cog
size_hint: .03, .03
source: './assets/icons/settings-cog.png'
Ball:
id: ball
size_hint: None, None
center: self.parent.center
''')
Config.set('graphics', 'multisamples', '0')
class Ball(Image):
velocityX, velocityY = NumericProperty(0), NumericProperty(0)
velocity = ReferenceListProperty(velocityX, velocityY)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
class Player(Widget):
pass
class Brick(Widget):
pass
class SettingsScreen(Screen):
def __init__(self, **kwargs):
super(SettingsScreen, self).__init__(**kwargs)
self.previous = StringProperty('')
def on_touch_down(self, touch):
if self.close.collide_point(*touch.pos):
self.manager.current = self.previous
class MenuScreen(Screen):
def on_touch_down(self, touch):
if self.cog.collide_point(*touch.pos):
self.manager.get_screen('settings').previous = self.manager.current
self.manager.current = 'settings'
else:
self.manager.transition = FadeTransition()
self.manager.current = 'game'
class GameScreen(Screen):
def __init__(self, **kwargs):
super(GameScreen, self).__init__(**kwargs)
self.initBall()
def on_pre_enter(self, *args):
self.interval = Clock.schedule_interval(self.update, 1.0 / 60.0)
def on_touch_down(self, touch):
if self.cog.collide_point(*touch.pos):
self.manager.get_screen('settings').previous = self.manager.current
self.manager.current = 'settings'
def initBall(self):
self.ball.center = self.center
self.ball.velocity = Vector(0, 4).rotate(randint(0, 360))
def update(self, dt):
self.ball.move()
if (self.ball.y < 0) or (self.ball.y > self.height - 15):
self.ball.velocityY *= -1
# bounce off left and right
if (self.ball.x < 0) or (self.ball.x > self.width - 15):
self.ball.velocityX *= -1
def on_pre_leave(self, *args):
self.interval.cancel()
sm = ScreenManager(transition=FadeTransition())
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(GameScreen(name='game'))
sm.add_widget(SettingsScreen(name='settings'))
class BrickBreakerInsanityApp(App):
def build(self):
return sm
if __name__ == '__main__':
BrickBreakerInsanityApp().run()

How to switch a widget's Animation transition in Kivy

Summary:
I would like to test all the possible transitions of Kivy's Animation function.
In my code, I call a method to switch the buttons' animations. The method is successfully called because I used print to confirm the change; however, the buttons' transitions don't accept the change. They keep using the first animation but do not take on the next animation in the list.
What am I doing wrong?
Code:
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from random import random
from kivy.uix.button import Button
from kivy.animation import Animation
from kivy.clock import Clock
class LittleButtons(Button):
transition = 'in_back'
dur = 2
num = 0
def change_transition(self):
list = ['in_back','in_bounce','in_circ','in_cubic','in_elastic','in_expo','in_out_back',
'in_out_bounce','in_out_cubic','in_out_elastic','in_out_expo',
'in_out_quad','in_out_quart','in_out_quint','in_out_sine','in_quad','in_quart',
'in_quint','in_sine','linear','out_back','out_bounce','out_circ','out_cubic',
'out_elastic','out_expo','out_quad','out_quart','out_quint','out_sine']
self.num += 1
self.transition = list[self.num]
self.reup()
print(self.transition)
if self.num == len(list) - 1:
self.num = -1
def reup(self, *args):
Animation.cancel_all(self)
anim = Animation(pos_hint = {'center_x': random(), 'center_y': random()},
duration = self.dur, t = self.transition)
anim.start(self)
def __init__(self, **kwargs):
super(LittleButtons, self).__init__(**kwargs)
self.pos_hint = {'center_x': random(), 'center_y': random()}
self.size_hint = None, None
self.width = random() * (Window.width / 20)
self.height = random() * (Window.width / 20)
self.background_color = [0,0,0,.05]
Animation(pos_hint = {'center_x': random(), 'center_y': random()},
duration = self.dur).start(self)
Clock.schedule_interval(self.reup, self.dur)
KV = Builder.load_string("""
#:import Factory kivy.factory.Factory
Screen:
FloatLayout:
on_parent:
(lambda ltext: [self.add_widget(Factory.LittleButtons(text=ltext)) for i in range (150)])('hi!')
LittleButtons:
id: base
Button:
background_color: 0,0,0,0
canvas:
Color:
rgba: 0,1,1,1
Rectangle:
pos: self.pos
size:self.size
on_release:
base.change_transition()
""")
class MyApp(App):
def build(self):
return KV
if __name__ == '__main__':
MyApp().run()
Your code is working, but not doing what you expect. Your on_release calls base.change_transition(), which refer to the base id. That id is the one LittleButtons in your KV string that is not built by the on_parent event. The animation transition of that one LittleButtons is successfully modified, but the others are unaffected. Also, that particular LittleButtons is unseen, since the big Button hides it. So, you are successfully changing the transition of one LittleButtons, but it doesn't show.

Kivy: undesirable behavior of custom mouse cursor when crossing left or top edge of app window

I want to make a custom mouse cursor in kivy.
This is what I have at the moment:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scatter import Scatter
from kivy.core.window import Window
#Window.show_cursor = False
KV = """
FloatLayout
BoxLayout
MyTextInput
MyMouse
<MyTextInput>:
font_size: 40
text: 'Some text'
<MyMouse>:
mouse_im_size: mouse_im.size
auto_bring_to_front: True
do_rotation:False
do_scale:False
do_translation_y:False
Image
id: mouse_im
size: 100, 100 / self.image_ratio
source: 'cursor-pink.png'
"""
class MyTextInput(TextInput):
pass
class MyMouse(Scatter):
def __init__(self, **kwargs):
Window.bind(mouse_pos=self.on_mouse_pos)
super(MyMouse, self).__init__(**kwargs)
def on_touch_down(self, *touch):
return
def on_mouse_pos(self, *args):
x,y = args[1]
self.pos = [x,y-self.mouse_im_size[1]]
class MyApp(App):
def build(self):
self.root = Builder.load_string(KV)
MyApp().run()
the problem is that when I move the mouse beyond the left or upper edge of application, the cursor image remains within the app, and I want the mouse image to disappear just like when I move the mouse beyond the right or lower edge.
It seems the problem is that on_mouse_pos() only works when the mouse is inside the window.
I found a way to get the position of the mouse when it is outside the window, but I do not know how this can be used in my task. And maybe there is a better way to do this.
You can accomplish this by using the Window events on_cursor_enter and on_cursor_leave and making the cursor visible/invisible by using the opacity property:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scatter import Scatter
from kivy.core.window import Window
#Window.show_cursor = False
KV = """
FloatLayout
BoxLayout
MyTextInput
MyMouse
id: themouse
<MyTextInput>:
font_size: 40
text: 'Some text'
<MyMouse>:
mouse_im_size: mouse_im.size
auto_bring_to_front: True
do_rotation:False
do_scale:False
do_translation_y:False
Image
id: mouse_im
size: 100, 100 / self.image_ratio
source: 'cursor-pink.png'
"""
class MyTextInput(TextInput):
pass
class MyMouse(Scatter):
def __init__(self, **kwargs):
Window.bind(mouse_pos=self.on_mouse_pos)
Window.bind(on_cursor_leave=self.on_cursor_leave)
Window.bind(on_cursor_enter=self.on_cursor_enter)
super(MyMouse, self).__init__(**kwargs)
def on_touch_down(self, *touch):
return
def on_mouse_pos(self, *args):
x,y = args[1]
self.pos = [x,y-self.mouse_im_size[1]]
def on_cursor_leave(self, *args):
App.get_running_app().root.ids.themouse.opacity = 0
def on_cursor_enter(self, *args):
App.get_running_app().root.ids.themouse.opacity = 1
class MyApp(App):
def build(self):
self.root = Builder.load_string(KV)
MyApp().run()
I added the themouse id to the MyMouse widget to accomplish this.
Here is another approach, but it requires a border around your basic layout:
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scatter import Scatter
from kivy.core.window import Window
#Window.show_cursor = False
KV = """
FloatLayout
canvas.before:
Color:
rgba: (1, 0, 0, 1)
Rectangle:
size: self.size
pos: self.pos
FloatLayout
id: mainlayout
size_hint: (None, None)
#pos: (50, 50)
#pos_hint: {'center_x': 0.5, 'center_y': 0.5}
canvas.before:
Color:
rgba: (0, 1, 0, 1)
Rectangle:
size: self.size
pos: self.pos
BoxLayout
size_hint: (1.0, 0.2)
pos_hint: {'center_x': 0.5, 'top': 1.0}
MyTextInput
StencilView:
size_hint: (1.0, 1.0)
pos_hint: {'x': 0, 'y': 0}
MyMouse
id: themouse
<MyTextInput>:
font_size: 40
text: 'Some text'
<MyMouse>:
mouse_im_size: mouse_im.size
auto_bring_to_front: True
do_rotation:False
do_scale:False
do_translation_y:False
Image
id: mouse_im
size: 100, 100 / self.image_ratio
source: 'cursor-pink.png'
"""
class MyTextInput(TextInput):
pass
class MyMouse(Scatter):
def __init__(self, **kwargs):
Window.bind(mouse_pos=self.on_mouse_pos)
super(MyMouse, self).__init__(**kwargs)
def on_touch_down(self, *touch):
return
def on_mouse_pos(self, *args):
x,y = args[1]
self.pos = [x,y-self.mouse_im_size[1]]
class MyApp(App):
def build(self):
self.mainlayout = None
self.mymouse = None
self.root = Builder.load_string(KV)
Window.bind(size=self.do_size)
Clock.schedule_once(self.do_size)
def do_size(self, *args):
if self.mainlayout is None:
self.mainlayout = self.root.ids.mainlayout
if self.mymouse is None:
self.mymouse = self.root.ids.themouse
self.mainlayout.size = (self.root.width - 2.0 * self.mymouse.mouse_im_size[0], self.root.height - 2.0 * self.mymouse.mouse_im_size[1])
self.mainlayout.pos = self.mymouse.mouse_im_size
MyApp().run()
This uses a StencilView to clip the drawing of the cursor to the inside of the man layout.

kivy adding widget without kivy language

I wanted to make a small game with an enemy-widget that disappears when you click on it.
A added the Enemy to an widget using kivy-language and it worked fine, but I wanted to add multiple enemys and I don't want to add more and more Enemys, so I wanted to use the add_widget command to andd the enemy witget to the place widget, but I got the Error:
TypeError: unbound method add_widget() must be called with place instance as first argument (got WidgetMetaclass instance instead)
Here is the sourcecode:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.clock import Clock
from kivy.animation import Animation
class place(Widget):
pass
class Enemy(Widget):
velocity = NumericProperty(1)
def __init__(self, **kwargs):
super(Enemy, self).__init__(**kwargs)
Clock.schedule_interval(self.Update, 1/60.)
def Update(self, *args):
self.x -= self.velocity
if self.x < 1:
self.velocity = 0
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
print 'es geht'
self.velocity = 0
self.parent.remove_widget(self)
ROOT = Builder.load_string('''
FloatLayout:
Button:
text: 'Go Back'
size_hint: 0.3, 0.1
pos_hint: {"x": 0, 'y':0}
place:
<place>:
Enemy:
pos: 400, 100
<Enemy>:
Image:
pos: root.pos
id: myimage
source: 'enemy.png'
''')
class Caption(App):
def build(self):
place.add_widget(Enemy)
return ROOT
if __name__ == '__main__':
Caption().run()
There are a couple problems with your code. First the python problem:
The error you are getting is telling you that you are trying to call in instance method on a class (as opposed to an object of that class).
place.add_widget(Enemy)
place is a class (or "type" if you prefer, and it might be helpful to name it Place to follow a constistent naming convention), and you need an object of type 'place' to call .add_widget on. Similarly, Enemy is a class, not an object, so you need to create a new object of type Enemy:
obj.add_widget(Enemy(pos=(400,300))
Where obj is an instance of place, and Enemy() creates an instance of enemy.
Now the kivy problem:
You can't access a widget in kv from python by it's name. You have to tag it with an id and then reference that id:
ROOT = Builder.load_string('''
FloatLayout:
Button:
text: 'Go Back'
size_hint: 0.3, 0.1
pos_hint: {"x": 0, 'y':0}
place:
id: place
<place>:
Enemy:
pos: 400, 100
<Enemy>:
Image:
pos: root.pos
id: myimage
source: 'enemy.png'
''')
class Caption(App):
def build(self):
obj = ROOT.ids.place
obj.add_widget(Enemy(pos=(400,300)))
return ROOT
See here for more info

Categories