Kivy : Move image following mouse movement without kv - kivy

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)

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 DragBehavior Custom Widget

I am trying to use DragBehavior to help in moving my custom widget across RelativeLayout. Find below sample code. Why my widget is not moving on Drag action please. For simplicity I had included only rectangle in my custom widget MyPaintWidget
from kivy.app import App
from kivy.graphics import Line
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
from kivy.graphics import Color, Rectangle
Builder.load_string("""
<MyPaintWidget>:
# Define the properties for the DragLabel
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
""")
class MyPaintWidget(DragBehavior, Scatter):
def __init__(self, **kwargs) :
self.selected = None
self.touched = False
super(MyPaintWidget, self).__init__(**kwargs)
def create_figure(self, **kwargs):
print ('position is {}'.format(self.pos))
print ('width Height {}'.format(self.to_parent(self.width, self.height)))
self.canvas.add(Rectangle(pos = self.pos, size = self.size))
return self
def on_touch_move(self, touch):
print('Started to move x: {} y: {}'.format(touch.x, touch.y))
return super(MyPaintWidget, self).on_touch_move(touch)
class MyPaintApp(App):
def build(self):
parent = RelativeLayout()
self.painter = MyPaintWidget(pos_hint={"center_x": 0.5, 'center_y':0.5}, size_hint=(.2,.1))
parent.add_widget(self.painter.create_figure())
return parent
if __name__ == '__main__':
MyPaintApp().run()
The DragBehavior works by adjusting the pos of your MyPaintWidget, but you have set pos_hint on the MyPaintWidget. The pos_hint takes precedence over pos, so while the drag changes pos, it is ignored because there is a pos_hint. Also, the Rectangle that you draw in create_figure has its size and pos set when that method is called, and there is no mechanism to change it when the MyPaintWidget is moved. So, even if the Widget was being dragged, the Rectangle would not move.
Here is a version of your code with those problems corrected:
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
Builder.load_string("""
<MyPaintWidget>:
# Define the properties for the DragLabel
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
canvas:
Color:
rgba: 1,0,0,1
Rectangle:
pos: 0,0 # only do this for RelativeLayout
size: self.size
""")
class MyPaintWidget(DragBehavior, Scatter):
def __init__(self, **kwargs) :
self.selected = None
self.touched = False
super(MyPaintWidget, self).__init__(**kwargs)
def on_touch_move(self, touch):
print('Started to move x: {} y: {}'.format(touch.x, touch.y))
return super(MyPaintWidget, self).on_touch_move(touch)
class MyPaintApp(App):
def build(self):
parent = RelativeLayout()
self.painter = MyPaintWidget( pos=(240, 200), size_hint=(.2,.1))
parent.add_widget(self.painter)
return parent
if __name__ == '__main__':
MyPaintApp().run()

Removed widget remains on parent

I have parent window that is Widget where places another widgets (parent.add_widget(...)) by right mouse click. Also children widget can be removed by middle click (parent.remove_widget(...)). And here I faced strange issue - child actually removed from children collection of parent but remains visible (but inactive. i.e. all interactions with removed widget image are impossible). I do not using any layouts because children are movable to any point and thus should not be aligned somehow.
Question: how to redraw parent after remove_widget?
Example code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.config import Config
from kivy.lang import Builder
from kivy.uix.behaviors import DragBehavior
from kivy.uix.relativelayout import RelativeLayout
Config.set('input', 'mouse', 'mouse,disable_multitouch')
Builder.load_string(f'''
<Node#RelativeLayout>:
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 10
canvas.before:
Color:
rgba: self.bcolor
RoundedRectangle:
pos: 0, 0
size: self.size
radius: [10,]
''')
class Node(DragBehavior, RelativeLayout):
def __init__(self, **kwargs):
self.bcolor = (0, 0, 1, 1)
super(Node, self).__init__(size=(100, 100), **kwargs)
def placement_pos(self, x, y):
return (x - self.node_width() / 2, y - self.node_height() / 2)
Builder.load_string(f'''
<GrapherUI#Widget>:
canvas.before:
Color:
rgba: {.5, 1, .5, 1}
Rectangle:
pos: self.pos
size: self.size
''')
class GrapherUI(Widget):
def __init__(self, graph=None, **kwargs):
super(GrapherUI, self).__init__(**kwargs)
def on_touch_down(self, touch):
touched_children = [x for x in self.walk(restrict=True) if x is not self and x.collide_point(*touch.pos)]
has_touched_children = any(touched_children)
if touch.button == 'right':
if not has_touched_children:
self.add_node(touch)
elif touch.button == 'middle':
self.remove_node(touched_children, touch)
super(GrapherUI, self).on_touch_down(touch)
def add_node(self, touch):
print('add node')
with self.canvas:
node = Node(pos=(touch.x, touch.y))
self.add_widget(node)
def remove_node(self, touched_children, touch):
for node in touched_children:
print('remove node')
self.remove_widget(node)
class Main(App):
def build(self):
return GrapherUI()
if __name__ == '__main__':
Main().run()
Well, as usual with Kivy nobody except yourself could help (even documentation). Here issue caused by with self.canvas: line under add_node method. If it removed widget disappears after removing.

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

How to control more than one image position with 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

Resources