How to control more than one image position with Kivy - kivy

I am making a game with Kivy where you can move a character around the screen. Earlier, I had it working where when you move the character right, the background image would scroll to the left, and same with the right. Recently, what I tried to do was add the background image to a widget along with another image, with the intention that i could control multiple image locations with 'widget.pos', but as soon as I did this, not only does the second image (hero2) not scroll right or left but neither does the background image. I am wondering what I did wrong in my code here.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
from kivy.core.audio import SoundLoader
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import FallOutTransition
gamelayout = FloatLayout(size=(300, 300))
class Game(Screen):
pass
class IconButton(ButtonBehavior, Image):
pass
class Bg(Image):
def __init__(self, **kwargs):
super(Bg, self).__init__(**kwargs)
self.allow_stretch = True
self.size_hint = (None, None)
self.size = (1440, 1440)
class MoveableImage(Image):
def __init__(self, **kwargs):
super(MoveableImage, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(None, self)
if not self._keyboard:
return
self._keyboard.bind(on_key_down=self.on_keyboard_down)
self._keyboard.bind(on_key_up=self.on_keyboard_up)
self.size_hint = (.11, .11)
self.y = (Window.height/2.1)
self.app = App.get_running_app()
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'left':
self.source = 'selectionscreen/left.zip'
self.anim_delay=.20
if self.x < (Window.width * .25):
self.app.test.x += 4
else:
self.x -= 6
elif keycode[1] == 'right':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.x > (Window.width * .70):
self.app.test.x -= 4
else:
self.x += 6
elif keycode[1] == 'down':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.y < (Window.height * .25):
self.app.test.y += 4
else:
self.y -= 6
elif keycode[1] == 'up':
self.source = 'selectionscreen/back.zip'
self.anim_delay=.1
if self.y > (Window.height * .70):
self.app.test.y -= 4
else:
self.y += 6
else:
return False
return True
def on_keyboard_up(self, keyboard, keycode):
if keycode[1] == 'left':
self.source = 'selectionscreen/left1.png'
elif keycode[1] == 'right':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'down':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'up':
self.source ='selectionscreen/back2.png'
else:
return False
return True
class gameApp(App):
def build(self):
global sm
sm = ScreenManager()
game = Game(name='game')
sm.add_widget(game)
hero = MoveableImage(source='selectionscreen/jeezyright1.png', pos=(75, 40))
self.hero2 = Image(source='selectionscreen/hero2.zip', pos=(100, 200))
self.background=Bg(source='selectionscreen/background9.png')
self.test=Bg()
self.test.add_widget(self.background)
self.test.add_widget(self.hero2)
gamelayout.add_widget(self.test)
gamelayout.add_widget(hero)
game.add_widget(gamelayout)
return sm
if __name__ == '__main__':
gameApp().run()

Looks like the problem here is that you are using a FLoatLayout. When you move a normal layout (such as a FloatLayout), it does not automatically change the children's co-ordinates. If you want that behavior, try using a RelativeLayout.
http://kivy.org/docs/api-kivy.uix.relativelayout.html
Peace out

Related

Image in canvas with opacity using kivy module

I can see the effects from the CanvasWidget() class but not any effects from the MyApp() class. Why is it so
#Importing kivy module
import kivy
kivy.require("1.10.1")
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color
from kivy.uix.image import Image
from kivy.config import Config
#Creating the canvas
class CanvasWidget(Widget):
def __init__(self, **kwargs):
super(CanvasWidget, self).__init__(**kwargs)
with self.canvas:
Color(1, 1, 1, 1)
self.rect = Rectangle(pos = self.center, size = (self.width / 2., self.height / 2.))
self.bind(pos = self.update_rect, size = self.update_rect)
def update_rect(self, *args):
self.rect.pos = self.pos
self.rect.size = self.size
class CanvasApp(App):
def build(self):
return CanvasWidget()
CanvasApp().run()
#Putting the image
Config.set("graphics", "resizable", True)
class MyApp(App):
def build(self):
self.img = Image(source = "voithos.jpg")
self.img.allow_stretch = True
self.img.keep_ratio = False
self.img.size_hint_x = 1
self.img.size_hint_y = 1
self.img.pos = (200, 100)
self.img.opacity = 1
s = Widget()
s.add_widget(self.img)
return s
MyApp().run()
This is the output i get
The line:
CanvasApp().run()
will not return until the CanvasApp is shut down. So nothing after that line will be executed until you kill CanvasApp.

Kivy: Drawing to widget

I collect experiencies with kivy and I have problem with using widget center. My problem shows next script:
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from functools import partial
from kivy.graphics import Color, Ellipse
class CanvasApp(App):
def draw(self,wid, color):
with wid.canvas:
Color(color[0], color[1], color[2])
Ellipse(size= (100, 100), pos=(wid.center_x, wid.center_y))
return
def painter(self, wid, *largs):
self.draw(wid, (0, 1, 0))
return
def build(self):
wid = Widget()
btn_painter = Button(text='Painter',
on_press=partial(self.painter, wid))
ctrl = BoxLayout(size_hint=(1, None), height=50)
ctrl.add_widget(btn_painter)
root = BoxLayout(orientation='vertical')
root.add_widget(wid)
root.add_widget(ctrl)
self.draw(wid, (1, 0, 0))
return root
if __name__ == '__main__':
CanvasApp().run()
I want to draw circle in the widget center (black rectangle) without using button painter. I don't able obtain actually center coordinates of center. Without using button are available only default center coordinates. How is OK process?
Thank you for answers.
The problem is that the wid size and position are not yet set inside the build() method. So you need to delay the call of draw() a bit. Try replacing the call to draw() with:
Clock.schedule_once(partial(self.draw, wid, (1,0,0)))
You will also need to change the draw() method slightly to handle the dt argument that gets added:
def draw(self,wid, color, *args):
I found solution of my problem. All explains follow script:
from kivy.app import App
from kivy.graphics import Color, Rectangle, Ellipse
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class DrawArea(FloatLayout):
def __init__(self, **kwargs):
# make sure we aren't overriding any important functionality
super(DrawArea, self).__init__(**kwargs)
self.rect1 = None
self.li_item = []
return
def clear_area(self):
for item in self.li_item:
self.canvas.remove(item)
self.li_item.clear()
def draw_rect(self):
with self.canvas:
Color(0, 1, 0, 1) # green; colors range from 0-1 not 0-255
self.rect1 = Rectangle(size=self.size, pos=self.pos)
Color(1, 0, 0, 1) # green; colors range from 0-1 not 0-255
width = self.width/2
height = self.height/2
pos_x = self.center_x -width/2
pos_y = self.center_y - height/2
self.rect2 = Rectangle(size=(width, height), pos=(pos_x, pos_y))
self.li_item.append(self.rect1)
self.li_item.append(self.rect2)
return
def draw_circle(self):
Color(1, 0, 0)
d = 120
with self.canvas:
Color(0, 1, 0)
self.circle1 = Ellipse(size = (d,d), pos = (self.center_x-d/2, self.center_y-d/2))
Color(0, 0, 1)
self.circle2 = Ellipse(size = (d/2,d/2), pos = (self.center_x - self.size[0]/2, self.center_y - self.size[1]/2))
self.li_item.append(self.circle1)
self.li_item.append(self.circle2)
return
class MainApp(App):
def build(self):
self.task = 'circle'
root = BoxLayout(orientation = 'vertical')
self.dra = DrawArea()
button = Button(text = 'painter',size_hint=(1, None), height=50, on_press = self.but_paint)
root.add_widget(self.dra)
root.add_widget(button)
if self.task == 'circle':
self.dra.bind(pos = self._update_circ, size = self._update_circ)
self.dra.draw_circle()
else:
self.dra.bind(size=self._update_rect, pos=self._update_rect)
self.dra.draw_rect()
return root
def but_paint(self, instance):
#self.dra.clear_area()
if self.task == 'circle':
self.task = 'rectangle'
else:
self.task = 'circle'
if self.task == 'circle':
self.dra.bind(pos = self._update_circ, size = self._update_circ)
self.dra.draw_circle()
else:
self.dra.bind(size=self._update_rect, pos=self._update_rect)
self.dra.draw_rect()
return
def _update_circ(self, instance, value):
d = 120
pos1_x = instance.center[0] -d/2
pos1_y = instance.center[1] -d/2
self.dra.circle1.pos = (pos1_x, pos1_y)
pos2_x = instance.center[0] - instance.size[0]/2
pos2_y = instance.center[1] - instance.size[1]/2
self.dra.circle2.pos = (pos2_x, pos2_y)
return
def _update_rect(self, instance, value):
if self.dra.rect1 != None:
self.dra.rect1.pos = instance.pos
self.dra.rect1.size = instance.size
width = instance.size[0]/2
height = instance.size[1]/2
pos_x = instance.center[0] -width/2
pos_y = instance.center[1] - height/2
self.dra.rect2.pos = (pos_x, pos_y)
self.dra.rect2.size = (width, height)
def draw_rect(self):
with self.dra.canvas.before:
Color(0, 1, 0, 1) # green; colors range from 0-1 not 0-255
self.rect1 = Rectangle(size=self.dra.size, pos=self.dra.pos)
Color(1, 0, 0, 1) # green; colors range from 0-1 not 0-255
width = self.width/2
height = self.height/2
self.rect2 = Rectangle(size=(width, height), pos=(self.center_x -width/2, self.center_y - height/2))
return
if __name__ == '__main__':
MainApp().run()

Kivy : Move image following mouse movement without kv

I want to move a image following mouse movement.
Especially, I'm at a loss how do I get mouse movement amount and translate coordinates.
I tried to_local , to_window , to_parent but I could not get it well...
Here is my minimal code. How should I modify this?
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
class TestLayer(FloatLayout):
def __init__(self, **kwargs):
super(TestLayer, self).__init__(**kwargs)
self.pos_hint = {'top':1, 'x':0}
self.size_hint_x = 1
self.size_hint_y = 1
def build(self):
# specify your image dir and name
self.img = Image(source='0.png', size=(100, 100))
self.add_widget(self.img)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
if touch.button == 'left':
# Hold value of touch downed pos
pass
return super(TestLayer, self).on_touch_down(touch)
def on_touch_move(self, touch):
if self.collide_point(*touch.pos):
if touch.button == 'left':
# self.img.x = self.img.x + (mouse movements distance)
# self.img.y = self.img.y + (mouse movements distance)
pass
return super(TestLayer, self).on_touch_move(touch)
def on_touch_up(self, touch):
if self.collide_point(*touch.pos):
if touch.button == 'left':
# process after movement or something?
pass
return super(TestLayer, self).on_touch_up(touch)
class TestScreen(Screen):
def __init__(self, **kwargs):
super(TestScreen, self).__init__(**kwargs)
layer = TestLayer()
self.add_widget(layer)
layer.build()
sm = ScreenManager()
class DemoApp(App):
def build(self):
sm.add_widget(TestScreen(name='test'))
return sm
if __name__ == '__main__':
DemoApp().run()
The code is set up in the correct way. What you need to do is save where the mouse was at each touch event (touch_down or touch_move) and compare to where it is now. Then you take the difference of those two, and move your image by that much.
The x and y coordinate of a touch are in the touch.pos[0] and touch.pos[1] variables respectively.
For example:
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
if touch.button == 'left':
# Hold value of touch downed pos
self.last_touch = touch.pos # Need this line
return super(TestLayer, self).on_touch_down(touch)
and
def on_touch_move(self, touch):
if self.collide_point(*touch.pos):
if touch.button == 'left':
self.img.x = self.img.x + touch.pos[0] - self.last_touch[0] # Add the x distance between this mouse event and the last
self.img.y = self.img.y + touch.pos[1] - self.last_touch[1] # Add the y distance between this mouse event and the last
self.last_touch = touch.pos # Update the last position of the mouse
return super(TestLayer, self).on_touch_move(touch)

Increase the linewidth of a MeshLinePlot in Kivy?

Does anyone know how to increase the linewidth of a MeshLinePlot in Kivy?
Thank you
Update
I got the answer from #Ikolim to work modifying the LinePLot function from kivy.graph
class LinePlot(Plot):
'''LinePlot draws using a standard Line object.
'''
'''Args:
line_width (float) - the width of the graph line
'''
def __init__(self, line_width=1, **kwargs):
self._line_width = line_width # instead of kwargs.get('line_width', 1)
super(LinePlot, self).__init__(**kwargs)
Base on the method __init__ for Kivy garden.graph at https://github.com/kivy-garden/garden.graph/blob/master/init.py, MeshLinePlot currently does not support line width. But LinePlot do support line width.
snippet
self.plot = LinePlot(line_width=4, color=[1, 0, 0, 1])
Example
main.py
import kivy
kivy.require("1.10.0")
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.garden.graph import Graph, MeshLinePlot, MeshStemPlot, LinePlot, SmoothLinePlot, ContourPlot
import matplotlib.pyplot as plt
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvas
from kivy.utils import get_color_from_hex as rgb
from numpy import sin
kv = """
<GraphCustom>:
<Test>:
orientation: 'vertical'
GraphCustom:
size: self.parent.size
pos: self.parent.pos
"""
Builder.load_string(kv)
class GraphCustom(Graph):
def __init__(self, **kwargs):
super(GraphCustom, self).__init__(**kwargs)
self.label_options = {'color': rgb('#FF0000'), 'bold': True}
self.background_color = rgb('f8f8f2')
self.tick_color = rgb('808080')
self.border_color = rgb('808080')
self.xlabel = 'X'
self.ylabel = 'Y'
self.x_ticks_minor = 5
self.x_ticks_major = 25
self.y_ticks_major = 1
self.y_grid_label = True
self.x_grid_label = True
self.padding = 5
self.x_grid = True
self.y_grid = True
self.xmin = -0
self.xmax = 100
self.ymin = -1
self.ymax = 1
self.stop = False
self.plot = LinePlot(line_width=4, color=[1, 0, 0, 1])
self.plot.points = [(x, sin(x / 10.)) for x in range(0, 101)]
self.add_plot(self.plot)
class Test(BoxLayout):
def __init__(self, *args, **kwargs):
super(Test, self).__init__(*args, **kwargs)
fig1 = plt.figure()
ax1 = fig1.add_subplot(111)
wid = FigureCanvas(fig1)
self.add_widget(wid)
class TestApp(App):
title = "Kivy Garden Graph - LinePlot Demo"
def build(self):
return Test()
if __name__ == '__main__':
TestApp().run()
Output

Using collide_widget so widgets don't overlap, what is the best solution here?

I have a character that runs around the screen, and an image of a tree. I have collide_widget working so that when the character runs into the tree, it returns True. The problem is that I am trying to stop the character from running over the tree, the tree should be blocking the characters movement. What I have tried to do was stop the characters movement to the left, when running into the tree from the left. When I do this it works and I can then use the up, down or, right key to run away from the tree. However, if I use this code for all directions, it prevents me from being able to walk away from the tree in any direction once the collide_widget(tree) is True. I understand the problem, but I am not sure the solution. Is there a better way to do this? Here is part of the code. Is there maybe away to just stop movement before the collision happens?
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'left':
print self.pos
print self.app.tree.pos
self.source = 'selectionscreen/left.zip'
self.anim_delay=.20
if self.x < (Window.width * .25):
bglayout.x += 4
else: #collide widget code
if self.collide_widget(self.app.tree)
self.x -= 0
else:
self.x -= 6
elif keycode[1] == 'right':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.x > (Window.width * .70):
bglayout.x -= 4
else:
self.x += 6
elif keycode[1] == 'down':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.y < (Window.height * .25):
bglayout.y += 4
else:
self.y -= 6
elif keycode[1] == 'up':
self.source = 'selectionscreen/back.zip'
self.anim_delay=.1
if self.y > (Window.height * .70):
bglayout.y -= 4
else:
self.y += 6
else:
return False
return True
def on_keyboard_up(self, keyboard, keycode):
if keycode[1] == 'left':
self.source = 'selectionscreen/left1.png'
elif keycode[1] == 'right':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'down':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'up':
self.source ='selectionscreen/back2.png'
else:
return False
return True
you can copy x and y first (to previous x and previous y perhaps), move the player, check if collideWidget is true, and if so, assign the old values from before you moved and it will be like nothing happened.

Resources