I wanted to make a kivy game with an stickman running around the screen, and as soon as you click on it, the stickman is removed.
I tried to remove the enemy widget by using Place.remove_widget(Enemy), but the Program crashed an I got this error message:
TypeError: unbound method remove_widget() must be called with Place instance as first argument (got WidgetMetaclass instance instead)
Here is my source code:
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(FloatLayout):
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
Place.remove_widget(Enemy)
ROOT = Builder.load_string('''
Place:
Button:
text: 'Go Back'
size_hint: 0.3, 0.1
pos_hint: {"x": 0, 'y':0}
Enemy:
pos: 400, 100
<Enemy>:
Image:
pos: root.pos
id: myimage
source: 'enemy.png'
''')
class Caption(App):
def build(self):
return ROOT
if __name__ == '__main__':
Caption().run()
Place.remove_widget(Enemy)
This is the problem - you aren't trying to remove an instance of the Enemy class from an instance of the Place class, but instead trying to remove the actual class itself from the other. This is the difference between a = Place and a = Place() - the former is the instructions for how to make a Place, the latter is an actual individual Place instance.
In this case you could probably do self.parent.remove_widget(self); self.parent is the Place instance containing the Enemy instance.
Related
I am building an app using Kivy and would like to draw a circle to the middle of a Widget as soon as the app starts up. I found how to run code on start in this question (How do I run a function once the form is loaded Kivy). However, as a comment points out, widths and heights are not initialized yet when calling on_start(). Does anyone know how I could do this?
I have the following code. Using this, the circle is drawn at position 50, 50, while I would like it in the middle of the Field widget.
main.py:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Ellipse
from kivy.lang import Builder
from kivy.properties import ObjectProperty
class Field(Widget):
def __init__(self, **kwargs):
super(Field, self).__init__(**kwargs)
def init_circle(self):
with self.canvas:
Color(1,1,1,1, mode='rgba')
r = 20
self.circle = Ellipse(pos=(self.width//2 - r, self.height//2 - r), size=(2*r, 2*r))
print(self.circle)
def move_circle(self):
cx, cy = self.circle.pos
self.circle.pos = (cx + 10, cy)
class RootClass(Widget):
field = ObjectProperty(None)
def __init__(self, **kwargs):
super(RootClass, self).__init__(**kwargs)
class MyMainApp(App):
def build(self):
self.r = RootClass()
return self.r
def on_start(self, **kwargs):
self.r.field.init_circle()
if __name__ == '__main__':
Builder.load_file("my.kv")
MyMainApp().run()
my.kv:
<RootClass>
field: field_id
BoxLayout:
size: root.size
orientation: "vertical"
Field:
id: field_id
size_hint: 1, 0.9
Button:
size_hint: 1, 0.1
text: "Move"
on_press:
root.field.move_circle()
Method 1:
Using bind by binding to a callback method,
class Field(Widget):
def __init__(self, **kwargs):
super(Field, self).__init__(**kwargs)
# Bind a callback method, say here 'init_circle' to the prop. 'size' and 'pos'
# so that whenever those prop. change the method will be called.
# To prevent redrawing you may use method 'clear' or some other strategy.
self.bind(size = self.init_circle, pos = self.init_circle)
def init_circle(self, *args):
with self.canvas:
...
Method 2:
Using Clock by scheduling the process,
def on_start(self, **kwargs):
Clock.schedule_once(self.r.field.init_circle)
The problem is in the .kv file. My problem is that i would like my .kv to notice the object-property in my .py. My code works if change the color in .kv to color: 0,0,0 which gives me black text as intend.
.py
from kivy import utils
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.uix.button import ButtonBehavior
Builder.load_file("FirebaseLoginScreen/themedwidgets.kv")
class ThemedButton(ButtonBehavior,Label):
colorchanged = ObjectProperty()
color = ObjectProperty([0,0,0,1])
def __init__(self, **kwargs):
super(ThemedButton, self).__init__(**kwargs)
Clock.schedule_once(self.start_pulsing, 1)
def start_pulsing(self, *args):
anim = Animation(color=[0,1,0,1]) + Animation(color=[0,0,1,1]) + Animation(color=[1,0,0,1])
anim.repeat = True
anim.start(self)
.kv
<ThemedButton#ButtonBehavior+Label>:
colorchanged: colorchanged
id: colorchanged
markup: True
color: self.color #root.color doesn't work either but 0,0,0 does give me black.
opacity: 1 if self.state == 'normal' else .8
font_size: 38
<ThemedButton#ButtonBehavior+Label>:
This is the kv syntax to create a new widget class inheriting from ButtonBehavior and Label, so it doesn't have any of the behaviour of your Python-defined class that happens to have the same name.
You want to just do <ThemedButton>: instead.
This question already has answers here:
What is the purpose of the `self` parameter? Why is it needed?
(26 answers)
Closed 3 years ago.
I would like to change a Label-text in Python/Kivy after a swipe event has been detected. Changing the text works basically via the following line
self.Translation.text = "test"
but I have to change the text after detecting a swipe event from another class, in which I call a function to change the label text:
MyWidget.ThisDoesntWork("self_dummy")
In this function the exact same line as above gives me an error.
How can I change the Label-text from class "Swiping_class" calling function "MyWidget.ThisDoesntWork("self_dummy")"?
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.base import EventLoop
from kivy.clock import Clock
from kivy.factory import Factory
from kivy.uix.stacklayout import StackLayout
from kivy.uix.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import *
from kivy.properties import ListProperty
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from random import random
import pickle
import random
kv = '''
<ColoredLabel>:
size: (self.size_x,self.size_y)
pos: (0,0) # no effect
background_color:
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: (self.size_x,self.size_y)
'''
Builder.load_string(kv)
class ColoredLabel(Label):
background_color = ListProperty((0,0,0,1))
s_global = Window.size
size_x = s_global[0]
size_y = s_global[1]/3
class MyWidget(BoxLayout):
#init
def __init__(self, **kwargs):
super().__init__(**kwargs)
s_global = Window.size
size_x = s_global[0]
size_y = s_global[1]/3
self.ForeignLanguage = ColoredLabel(text="str_fl", size_hint=(None, None),size = (size_x,size_y), background_color=(0/255,171/255,169/255, 1))
self.Translation = ColoredLabel(text="str_tr", size_hint=(None, None),size = (size_x,size_y), background_color=(45/255,137/255,239/255, 1))
self.Example = ColoredLabel(text="str_ex", size_hint=(None, None),size = (size_x,size_y), background_color=(43/255,87/255,151/255, 1))
self.verticalBox = BoxLayout(orientation='vertical')
self.verticalBox.add_widget(self.ForeignLanguage)
self.verticalBox.add_widget(self.Translation)
self.verticalBox.add_widget(self.Example)
self.Translation.text = "test"
s=Swiping_class()
s.add_widget(self.verticalBox)
self.add_widget(s)
def ThisDoesntWork(self):
print("this is printed")
self.Translation.text = "I wanna change this via fucntion"
print("this is not printed anymore")
class Swiping_class(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.initial = 0
def on_touch_down(self, touch):
self.initial = touch.x
def on_touch_up(self, touch):
if touch.x < self.initial:
print("swiped left")
MyWidget.ThisDoesntWork("self_dummy")
else:
print("swiped right")
class BoxLayoutDemo(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
BoxLayoutDemo().run()
I think I found a solution. Within your Swiping_class, replace this line:
MyWidget.ThisDoesntWork("self_dummy")
with this line:
MyWidget.ThisDoesntWork(self.parent)
That way, instead of passing a string to your method, you pass the label object, which contains the text attribute you are trying to modify.
Struggling to pass a variable to kivy window. I have read similar threads all over the place but none of the fixes seem to work for me. Im sure this is simple to someone who knows their way around tiny, unfortunately I don't.
main.py
import kivy
from kivy.uix.togglebutton import ToggleButton
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.app import App
kivy.require('1.10.0')
from phue import Bridge
import nest
b = Bridge('xxx.xxx.x.xxx')
b.connect()
b.get_api()
lights = b.lights
class Controller(GridLayout):
print("launching")
def __init__(self):
super(Controller, self).__init__()
def KitchenSpot1(self,state):
lights[0].name
lights[0].on = state
def update(dt):
if b.get_light(1, 'on')== True:
#print("down") # When this line is commented out I get an continuous accurate update on the status of the light, showing that its working.
return 'down' # This is the part I want passed to the state criteria in the ivy window
else:
#print("up")# When this line is commented out I get an continuous accurate update on the status of the light, showing that its working.
return 'down' # This is the part I want passed to the state criteria in the ivy window
class ActionApp(App):
def build(self):
Clock.schedule_interval(Controller.update, 1.0 / 60.0)
return Controller()
myApp = ActionApp()
myApp.run()
action.kv
<Controller>:
cols: 4
rows: 3
spacing: 10
ToggleButton:
id: KitchenSpot1Toggle
text: "Kitchen Spot 1"
on_press: root.KitchenSpot1(True)
#on_release: root.KitchenSpot1(False)
#state1 = app.update.h
state: Controller.update # This is the part that is throwing up the error.
The error:
11: #on_release: root.KitchenSpot1(False)
12: #state1 = app.update.h
>> 13: state: Controller.update
14:
15:
...
NameError: name 'Controller' is not defined
Thanks in advance to anyone that can help me.
Make update an instance method and use a StringProperty to update state property in your kv:
main.py:
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.uix.gridlayout import GridLayout
from kivy.uix.togglebutton import ToggleButton
from phue import Bridge
import nest
b = Bridge('xxx.xxx.x.xxx')
b.connect()
b.get_api()
lights = b.lights
class Controller(GridLayout):
state = StringProperty('normal') # <<<<<<<<<<<<
def __init__(self, **kwargs):
super(Controller, self).__init__(**kwargs)
Clock.schedule_interval(self.update, 1.0 / 60.0)
def KitchenSpot1(self,state):
lights[0].name
lights[0].on = state
def update(self, dt):
if b.get_light(1, 'on'):
self.state = 'down' # <<<<<<<<<<<<
else:
self.state = 'normal' # <<<<<<<<<<<<
class ActionApp(App):
def build(self):
return Controller()
if __name__ == "__main__":
myApp = ActionApp()
myApp.run()
action.kv:
<Controller>:
cols: 4
rows: 3
spacing: 10
state: "normal" # <<<<<<<<<<<<
ToggleButton:
id: KitchenSpot1Toggle
text: "Kitchen Spot 1"
on_press: root.KitchenSpot1(True)
#on_release: root.KitchenSpot1(False)
#state1 = app.update.h
state: root.state # <<<<<<<<<<<<
Here is a more generic simplified answer from the kivy documentation, look for the section called "Keyword arguments and init()" because there are some other ways to do it as well.
The following code passes myvar to the build() method of MyApp. It does this by over-riding the init() of the Kivy App class by a new init() that calls App.init() and then continues with whatever extra initialisation you want. You can then store variables in the MyApp class instances and use them in build().
from kivy.app import App
from kivy.uix.label import Label
myvar = 'Hello Kivy'
class MyApp(App):
def __init__(self, myvar, **kwargs):
super(MyApp, self).__init__(**kwargs)
self.myvar = myvar
def build(self):
widget = Label(text=self.myvar)
return widget
if __name__ == '__main__':
MyApp(myvar).run()
import os,sys,random
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.uix.screenmanager import ScreenManager, Screen , SlideTransition
from kivy.animation import Animation
from kivy.properties import StringProperty
class Page(Screen):
source = StringProperty()
class Imglayout(FloatLayout):
def __init__(self,**args):
super(Imglayout,self).__init__(**args)
with self.canvas.before:
Color(0,0,0,0)
self.rect=Rectangle(size=self.size,pos=self.pos)
self.rootpath = os.path.dirname(os.path.realpath(sys.argv[0]))
self.images=[]
for img in os.listdir(self.rootpath+'/images'):
self.images.append(self.rootpath+'/images/'+img)
self.index=random.randint(0,len(self.images)-1)
self.im=Image(source=self.images[self.index])
self.im.keep_ratio= False
self.im.allow_stretch = True
#self.add_widget(self.im)
self.sm = ScreenManager(transition=SlideTransition())
self.page1=Page(name='page1', source = self.images[self.index])
self.page2=Page(name='page2', source = self.images[self.index+1])
self.sm.add_widget(self.page1)
self.sm.add_widget(self.page2)
self.bind(size=self.updates,pos=self.updates)
def updates(self,instance,value):
self.rect.size=instance.size
self.rect.pos=instance.pos
def on_touch_down(self,touch):
if self.collide_point(*touch.pos):
if(self.sm.current == 'page1'):
next='page2'
page=self.page2
else:
next='page1'
page=self.page1
self.index=(self.index+1)%len(self.images)
page.source = self.images[self.index]
page.background.scale = 1.0
self.sm.transition=SlideTransition()
self.sm.current = next
anim = Animation(
scale=page.background.scale*1.3,
duration=15.0
)
anim.start(page.background)
return True
return False
class MainTApp(App):
def build(self):
root = BoxLayout(orientation='vertical',spacing=10)
c = Imglayout()
root.add_widget(c)
cat=Button(text="Categories",size_hint=(1,.05))
cat.bind(on_press=self.callback)
root.add_widget(cat);
return root
def callback(self,value):
print "CALLBACK CAT"
if __name__ == '__main__':
MainTApp().run()
Taking some hints from here i made the above program. It says that Page does not have a background attribute in both my and the referenced code. Its kind of obvious since there is no background attribute. I thought it inherited that from Screen. I am trying to make a slideshow kind of thing. But i cant find any information on how to set the background of a screen.
And if i comment out everything with .background and run the app. click on the black space, then i start getting this error continuously on the terminal
[ERROR ] [OSC ] Address 127.0.0.1:3333 already in use, retry
in 2 second
And i still dont get any background on the app.(its all black.)
and if i print self.sm.current on the touich function. Then i find that its always page1, it never changes.
The Kivy Guide explains how to add a background to a Widget. Briefly, you can do it in Python using the following code which binds to the position and size of the widget to make sure the background moves with the widget.
with widget_instance.canvas.before:
Color(0, 1, 0, 1) # green; colors range from 0-1 instead of 0-255
self.rect = Rectangle(size=layout_instance.size,
pos=layout_instance.pos)
widget_instance.bind(size=self._update_rect, pos=self._update_rect)
...
def _update_rect(self, instance, value):
self.rect.pos = instance.pos
self.rect.size = instance.size
Or you can do it more naturally with the kv language e.g.
MyWidget:
canvas.before:
Color:
rgba: 0, 1, 0, 1
Rectangle:
# self here refers to the widget i.e screen
pos: self.pos
size: self.size