Kivy, opacity around mouse, revealing picture - python

I would like to make a puzzle where someone has to follow a maze and collect letters which have to be typed in later as an answer. (Not collected by the program but just remembering which letters are passed and typing it in later while exiting the maze.)
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.config import Config
Config.set('graphics', 'width', '806')
Config.set('graphics', 'height', '706')
class Touch(Widget):
def __init__(self, **kwargs):
super(Touch, self).__init__(**kwargs)
def on_touch_down(self, touch):
pass
def on_touch_down(self, touch):
pass
def on_touch_down(self, touch):
pass
class MyGrid(GridLayout):
pass
class DrawingWindow(App):
def build(self):
return MyGrid()
if __name__ == "__main__":
DrawingWindow().run()
<MyGrid>:
GridLayout:
cols:1
size:1000,706
canvas:
Rectangle:
size: 808,706
source: 'Puzzle.png'
I have found out how to draw a circle where your mouse is (not added to the code as I think a different method is probably going to be used), but how to make everything around a certain radius black so that it only reveals a small circle within this radius. Maybe using opacity within a radius is a sollution. If someone could push me in the right direction, that would be really helpful.
It does not have to stay revealed, rather not actually. Hopefully that makes it somewhat easier as well.
I hope I've stated clearly what the problem is. Thank you in advance for the time and effort.

You can use Stencil Instructions to accomplish that. Here is a simple example:
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.properties import NumericProperty, ListProperty
from kivy.uix.widget import Widget
kv = '''
RelativeLayout:
ImageWithHole:
id: hole
canvas:
StencilPush
Ellipse: # draw the hole
pos: self.hole_pos[0] - self.radius, self.hole_pos[1] - self.radius
size: self.radius*2, self.radius*2
StencilUse
Color:
rgba: 1,1,1,1
Rectangle:
source: 'Puzzle.png' # draw the maze
pos: 0,0
size: self.size
StencilUnUse
Ellipse: # erase the hole (must be identical to original draw above)
pos: self.hole_pos[0] - self.radius, self.hole_pos[1] - self.radius
size: self.radius*2, self.radius*2
StencilPop
'''
class ImageWithHole(Widget):
radius = NumericProperty(50)
hole_pos = ListProperty([400, 300])
class TestApp(App):
def build(self):
Window.bind(mouse_pos=self.on_motion)
return Builder.load_string(kv)
def on_motion(self, src, mouse_pos):
hole = self.root.ids.hole
hole.hole_pos = mouse_pos
TestApp().run()

Related

Why does % Window.width with (self.object.uvpos[0] + time) in clock_interval make moving textures work correctly in Kivy

I'm doing a tutorial about making a game, and still in the background period, making moving textures. But I just wonder why the wrapped problem fixed by %Window.width in the line self.cloud_texture.uvpos = {(self.cloud_texture.uvpos[0] + time_passed)%Window.width ,self.cloud_texture.uvpos[1]}
I see no problem when I just do self.cloud_texture.uvpos[0] + time_passed only, but the result told me the difference when it's changing the self.cloud_texture.uvpos[1] too, and starting to get some glitch when resizing window after few seconds.
Can someone give me an explanation so I can apply it to my other apps?
.py file
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.properties import ObjectProperty
from kivy.core.window import Window
class Background(Widget):
cloud_texture = ObjectProperty(None)
def __init__(self, **kwargs):
super().__init__(**kwargs)
#create texture
self.cloud_texture = Image(source="assets/cloud.png").texture
self.cloud_texture.wrap = "repeat"
self.cloud_texture.uvsize = {Window.width / self.cloud_texture.width, -1}
def scroll_textures(self,time_passed):
self.cloud_texture.uvpos = {(self.cloud_texture.uvpos[0] + time_passed)%Window.width ,self.cloud_texture.uvpos[1]}
texture = self.property("cloud_texture")
texture.dispatch(self)
from kivy.clock import Clock
class MainApp(App):
def on_start(self):
Clock.schedule_interval(self.root.ids.background.scroll_textures, 1/60)
pass
if __name__ == "__main__":
MainApp().run()
.kv file
FloatLayout:
Background:
id: background
canvas.before:
Rectangle:
size: self.size
pos: self.pos
source: "assets/sky.png"
Rectangle:
size: self.width, 138
pos: self.pos[0], self.pos[1] + self.height - 138
texture: self.cloud_texture
You can find the images I'm using here

Kivy - How to drag and drop items from ScrollView to another layout?

I'm attempting to drag some images arranged inside a GridLayout (which is indeed inside a ScrollView) to outer Layout.
The image to be dragged has its on_touch_down event defined, when image is clicked parent is changed from WidgetMenu to MainLayout so it can be dragged between those widgets. The current problem is when I touch the image, DragBehavior is lost.
The full code:
import kivy
kivy.require("1.9.1")
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.scrollview import ScrollView
from kivy.uix.widget import Widget
from kivy.uix.behaviors import DragBehavior
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.base import runTouchApp
from kivy.lang import Builder
Builder.load_string('''
<WidgetMenu>:
canvas.before:
Color:
rgb: 0.9,0.5,0.3
RoundedRectangle:
pos:self.pos
size: self.size
radius: [20,]
orientation: "vertical"
padding:30
ScrollView:
GridLayout:
cols:1
size_hint_y:None
row_default_height:root.height*.15
height:self.minimum_height
DragImage:
DragImage:
DragImage:
<DragImage>:
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 100000000
drag_distance: 0
size_hint:None,None
size:234,34
canvas:
Color:
rgb:1,0,1
Rectangle:
pos: self.pos
size: self.size
<MainLayout>:
canvas:
Color:
rgb:1,1,1
Rectangle:
size: self.size
pos: self.pos
WidgetMenu:
size_hint: 0.35,0.9
''')
class MainLayout(FloatLayout):
pass
class WidgetMenu(BoxLayout):
pass
class DragImage(DragBehavior,FloatLayout):
def on_touch_down(self,touch):
workspace = self.parent.parent.parent.parent
grid = self.parent
menu = self.parent.parent.parent
if "MainLayout" in str(workspace):
grid.remove_widget(self)
workspace.remove_widget(menu)
self.pos = Window.mouse_pos
workspace.add_widget(self)
return True
class ScrollApp(App):
def build(self):
return MainLayout()
ScrollApp().run()
Please help.
Two problems with your code are that you are not calling the super method in your on_touch_down, and placing the DragImage in the top level MainLayout changes the pos of the DragImage, thus changing its DragRectangle. That change in DragRectangle leaves the touch.pos outside of it, so DragBehavior thinks the touch is not on the DragImage.
I have fixed both those problems by calling the super method and changing the touch.pos before passing it to the super method. I also added some code to keep the DragImage in the same position relative to the mouse when it is clicked. Also added a call to self.collide_point() in order to ignore clicks not on the DragImage.
class DragImage(DragBehavior,FloatLayout):
def on_touch_down(self,touch):
if not self.collide_point(*touch.pos):
return False
workspace = self.parent.parent.parent.parent
grid = self.parent
menu = self.parent.parent.parent
if "MainLayout" in str(workspace):
grid.remove_widget(self)
workspace.remove_widget(menu)
# the following code assumes that workspace is the entire Window
self.x = Window.mouse_pos[0] - (touch.pos[0] - self.x)
self.y = Window.mouse_pos[1] - (touch.pos[1] - self.y)
workspace.add_widget(self)
touch.pos = Window.mouse_pos
return super(DragImage, self).on_touch_down(touch)

Python Kivy Float Layout

I have very little experience with Python and Kivy. I am helping my little nephew with his quest to build a game using the two. We are stuck at something that seems simple.
Consider following code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
from random import randint
from kivy.graphics import Color, Ellipse, Rectangle
from kivy.uix.floatlayout import FloatLayout
import math
class Fly(Widget):
pass
class Stick(Widget):
pass
class FlyBox(Widget):
pass
class GameArea(FloatLayout):
pass
class FlyApp(App):
def build(self):
gameArea = GameArea()
flyBox = FlyBox()
flyBox.canvas.add(Rectangle(pos_hint={'x': 0, 'y': .3}, size_hint=(1, .3)))
flyBox.canvas.add(Color(1, 0, 0))
gameArea.add_widget(flyBox)
return gameArea
if __name__ == '__main__':
FlyApp().run()
it produces following output:
What we were trying to achieve was:
The FlyBox's height should be 1/3rd the height of the GameArea's height.
The FlyBox's width should be same as GameArea's width.
The FlyBox's x coordinate should be 0 and its y coordinate should be 1/3rd the height of GameArea.
i.e. if the game area were to be divided into 3 equal sized horizontal bands, the FlyBox should be the middle band. And as the size of game area changes (due to resizing of window), the FlyBox's size and position should change relatively.
We are trying to avoid using a .kv file and instead do everything in Python.
You only need to use size_hint_y property to set the correct height (1/3rd the parent height) and pos_hint property to position it in the center.
Using kivy languaje:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
kv_text = '''
<GameArea>:
Widget:
id: flybox
size_hint_y: 1/3
pos_hint: {'x': 0, 'center_y': .5}
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
'''
class GameArea(FloatLayout):
pass
class FlyApp(App):
def build(self):
Builder.load_string(kv_text)
return GameArea()
if __name__ == '__main__':
FlyApp().run()
Output:

Kivy non-GUI events

i am trying to start an animation when the app is first loaded. I.E. immediately after the loading screen has closed. I have tired the "on_enter" event but it doesn't seem to work, any help would be much appreciated.
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.animation import Animation
from kivy.properties import ListProperty
from kivy.core.window import Window
from random import random
from kivy.graphics import Color, Rectangle
Builder.load_string('''
<Root>:
AnimRect:
pos: 500, 300
<AnimRect>:
on_enter: self.start_animation
canvas:
Color:
rgba: 0, 1, 0, 1
Rectangle:
pos: self.pos
size: self.size
''')
class Root(Widget):
pass
class AnimRect(Widget):
def anim_to_random_pos(self):
Animation.cancel_all(self)
random_x = random() * (Window.width - self.width)
random_y = random() * (Window.height - self.height)
anim = Animation(x=random_x, y=random_y,
duration=4,
t='out_elastic')
anim.start(self)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.anim_to_random_pos()
def start_animation(self, touch):
if self.collide_point(*touch.pos):
self.anim_to_random_pos()
runTouchApp(Root())
The on_enter method is defined in the Screen, not in the Widget. You should put that rectangle on a screen (the Root widget should be a screen here), and once the screen's on_enter event fires, start the rectangle animation.
Also, you are calling it incorrectly; function call should contain brackets, i.e. on_enter: self.start_animation()
Does it look like something you wanted to have?
I just deleted the "on_enter" line in your kv, and corrected your indentation.
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.animation import Animation
from kivy.properties import ListProperty
from kivy.core.window import Window
from random import random
from kivy.graphics import Color, Rectangle
Builder.load_string('''
<Root>:
AnimRect:
pos: 500, 300
<AnimRect>:
canvas:
Color:
rgba: 0, 1, 0, 1
Rectangle:
pos: self.pos
size: self.size
''')
class Root(Widget):
pass
class AnimRect(Widget):
def anim_to_random_pos(self):
Animation.cancel_all(self)
random_x = random() * (Window.width - self.width)
random_y = random() * (Window.height - self.height)
anim = Animation(x=random_x, y=random_y,
duration=4,
t='out_elastic')
anim.start(self)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.anim_to_random_pos()
def start_animation(self, touch):
if self.collide_point(*touch.pos):
self.anim_to_random_pos()
runTouchApp(Root())

Kivy Screen class bug

For some reason in kivy when you create a screen class and add a widget to it, specifically an image you get an image 10x bigger than the original for some reason compared to when you make a widget class and add that widget class as a child to the screen class. Here is my code for the kv file:
<StartScreen>
# Start Screen
name:'Start'
orientation: 'vertical'
FloatLayout:
id: Start_Layout
Image:
id: Start_Background
source: r'Images\Battle.jpg'
keep_ratio: True
allow_stretch: True
size: root.size
<MainScreen>
name: 'Main'
orientation: 'vertical'
FloatLayout:
Image:
source: r'Images\Button.png'
allow_stretch: True
keep_ratio: False
size: 100, 100
and for the python gui file...
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.animation import Animation
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import *
from kivy.core.window import Window
from kivy.app import App
from kivy.lang import Builder
import kivy
kivy.require('1.9.1')
VERSION = '1.9.1'
class GenericButton(Widget):
Builder.load_file('Button.kv')
def __init__(self, **kwargs):
super(GenericButton, self).__init__(**kwargs)
self.Button = self.ids['Button']
self.size = Window.size
def on_touch_down(self, touch):
self.Button.source = r'Images\ButtonPressed.png'
def on_touch_up(self, touch):
self.Button.source = r'Images\Button.png'
class wid(Widget):
def __init__(self, **kwargs):
super(wid, self).__init__(**kwargs)
self.Button = Image(source='Images\Animatio\glow.gif', allow_stretch=False, keep_ratio=True) (pretend its indented cus im new and it wouldn't let me add it to the code block)
self.add_widget(self.Button)
class StartScreen(Screen):
def __init__(self, **kwargs):
super(StartScreen, self).__init__(**kwargs)
#self.Layout = self.ids['Start_Layout']
#self.size = Window.size
#self.Layout.add_widget(GenericButton())
#self.ids['Start_Button'] = self.Layout.children[0]
print self.ids
#print self.ids.Start_Button.size
print self.size[0]/2, self.size[1]/2
class MainScreen(Screen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.size = Window.size
def on_touch_down(self, touch):
self.Button.source = r'Images\ButtonPressed.png'
def on_touch_up(self, touch):
self.Button.source = r'Images\Button.png'
class ScreenManager(ScreenManager):
def __init__(self, **kwargs):
super(MCGMScreenManager, self).__init__(**kwargs)
Builder.load_file('Gui.kv')
self.add_widget(StartScreen())
self.add_widget(MainScreen())
And the app runs in the main file which i dont see a need to post. But an important thing might be that the app root class is ScreenManager
edit: i messed around a bit and i did this in python but i cleared the children of GenericButton and added the button that GenericButton used to own as a child of StartScreen and same result, a huge unresponsive image.
<MainScreen>
name: 'Main'
orientation: 'vertical'
FloatLayout:
Image:
source: r'Images\Button.png'
allow_stretch: True
keep_ratio: False
size: 100, 100
I didn't check if it's causing your particular issue, but the Image here doesn't take size 100, 100 because its parent (FloatLayout) is a layout class that automatically sets the size and position of its children. In this case, Image will automatically be resized to fill the FloatLayout.
To prevent this, add size_hint: None, None to the Image, to disable automatic resizing in both the horizontal and vertical directions. This generally applies whenever you add something to a Layout.

Categories

Resources