I'm attempting to crop an image. The basic idea is that the user will drag his mouse over the image to crop it. To that end, when you click "Crop", the user can select a lower left point, then drag the rectangle up to the top right. The rectangle should shade the outside area not selected.
I've been researching Drag behavior and simpler methods such as this answer (attempted below), but with little results. As a bonus, is it possible to limit the area the user can touch and drag?
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
from kivy.graphics import Color
from kivy.graphics import Point
from kivy.properties import NumericProperty, ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MyScreenManager>:
ThirdScreen:
id: third_screen
<ThirdScreen>:
name: '_third_screen_'
id: third_screen
BoxLayout:
orientation: "vertical"
id: third_screen_boxlayout
Label:
id: main_title
text: "Title"
size_hint: (1, 0.1)
BoxLayout:
id: image_box_layout
Image:
id: main_image
source: "C:/Users/OneDrive/0. T2/6. Kivy/4/claymore.jpg"
BoxLayout:
id: button_boxlayout
orientation: "horizontal"
padding: 10
size_hint: (1, 0.15)
Button:
id: accept_button
text: "Okay"
size_hint: (0.33, 1)
on_press: root.image_accepted_by_user(root.image_address)
Button:
id: crop_button
text: "Crop"
size_hint: (0.33, 1)
on_press: root.enable_cropping()
Button:
id: cancel_button
text: "Cancel"
size_hint: (0.33, 1)
on_press: root.manager.current = '_first_screen_'
""")
class MyScreenManager(ScreenManager):
pass
class ThirdScreen(Screen):
def enable_cropping(self):
print("\nThirdScreen:")
print(self.ids.main_image.pos)
print(self.ids.main_image.size)
print("\tAbsolute size=", self.ids.main_image.norm_image_size)
print("\tAbsolute pos_x=", self.ids.main_image.center_x - self.ids.main_image.norm_image_size[0] / 2.)
print("\tAbsolute pos_y=", self.ids.main_image.center_y - self.ids.main_image.norm_image_size[1] / 2.)
self.ids.image_box_layout.add_widget(DrawInput(size_hint=(0.00000000000001, 0.00000000000001)))
class DrawInput(Widget):
# for cropping
draw = True
rect_box = ObjectProperty(None)
t_x = NumericProperty(0.0)
t_y = NumericProperty(0.0)
x1 = y1 = x2 = y2 = 0.0
def on_touch_down(self, touch):
if self.draw:
color = (1, 1, 1, 0.4) # red, 40% shaded
win = self.get_parent_window()
touch.ud['group'] = str(touch.uid)
with self.canvas:
# variable drag
Color(*color, mode='hsv')
self.x1 = touch.x
self.y1 = touch.y
self.t_x = touch.x
self.t_y = touch.y
self.rect_box = [Rectangle(pos=(0, self.y1),
size=(win.width, -self.y1),
group=touch.ud['group']),
Rectangle(pos=(0, self.y1),
size=(self.x1, win.height),
group=touch.ud['group']),
Point(points=(self.x1, self.y1),
source='particle.png',
group=touch.ud['group']),
Rectangle(pos=(self.t_x, self.y1),
size=(win.width - self.t_x, win.height - self.y1),
group=touch.ud['group']),
Rectangle(pos=(self.t_x, self.t_y),
size=(self.x1 - self.t_x, win.height - touch.y),
group=touch.ud['group']),
Point(points=(self.t_x, self.t_y),
source='particle.png',
group=touch.ud['group'])]
touch.grab(self)
print(self.x1, self.y1)
def on_touch_move(self, touch):
if touch.grab_current is self:
# not working
self.t_x = touch.x
self.t_y = touch.y
print(self.t_x, self.t_y)
def on_touch_up(self, touch):
if touch.grab_current is self:
# only 1 draw
self.draw = False
# final position
self.x2 = touch.x
self.y2 = touch.y
print(self.x2, self.y2)
class MyApp(App):
def build(self):
return MyScreenManager()
if __name__ == '__main__':
MyApp().run()
The solution was to remove the DrawInput class and integrate the on_touch_down, on_touch_move and on_touch_up into the Screen. NumericProperty() and ObjectProperty() allow the rectangle, wrote as a widget canvas into the kv file, to be manipulated.
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
from kivy.graphics import Color
from kivy.graphics import Point
from kivy.properties import NumericProperty, ObjectProperty
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MyScreenManager>:
ThirdScreen:
id: third_screen
<ThirdScreen>:
name: '_third_screen_'
id: third_screen
BoxLayout:
orientation: "vertical"
id: third_screen_boxlayout
Label:
id: main_title
text: "Title"
size_hint: (1, 0.1)
BoxLayout:
id: image_box_layout
Image:
id: main_image
source: "C:/Users/Mark/OneDrive/0. T2/6. Kivy/4/claymore.jpg"
Widget:
id: image_canvas
size_hint: (0.0000001, 0.0000001)
canvas:
Rectangle:
id: root.rect_box
pos: (root.x1, root.y1)
size: (root.t_x, root.t_y)
BoxLayout:
id: button_boxlayout
orientation: "horizontal"
padding: 10
size_hint: (1, 0.15)
Button:
id: accept_button
text: "Okay"
size_hint: (0.33, 1)
on_press: root.image_accepted_by_user(root.image_address)
Button:
id: crop_button
text: "Crop"
size_hint: (0.33, 1)
on_press: root.enable_cropping()
Button:
id: cancel_button
text: "Cancel"
size_hint: (0.33, 1)
on_press: root.manager.current = '_first_screen_'
""")
class MyScreenManager(ScreenManager):
pass
class ThirdScreen(Screen):
rect_box = ObjectProperty(None)
t_x = NumericProperty(0.0)
t_y = NumericProperty(0.0)
x1 = y1 = x2 = y2 = NumericProperty(0.0)
def enable_cropping(self):
print("\nThirdScreen:")
print(self.ids.main_image.pos)
print(self.ids.main_image.size)
print("\tAbsolute size=", self.ids.main_image.norm_image_size)
print("\tAbsolute pos_x=", self.ids.main_image.center_x - self.ids.main_image.norm_image_size[0] / 2.)
print("\tAbsolute pos_y=", self.ids.main_image.center_y - self.ids.main_image.norm_image_size[1] / 2.)
def on_touch_down(self, touch):
self.x1 = touch.x
self.y1 = touch.y
self.t_x = touch.x
self.t_y = touch.y
touch.grab(self)
print(self.x1, self.y1)
def on_touch_move(self, touch):
if touch.grab_current is self:
# not working
self.t_x = touch.x
self.t_y = touch.y
print(self.t_x, self.t_y)
def on_touch_up(self, touch):
if touch.grab_current is self:
# final position
self.x2 = touch.x
self.y2 = touch.y
print(self.x2, self.y2)
class MyApp(App):
def build(self):
return MyScreenManager()
if __name__ == '__main__':
MyApp().run()
Related
I am facing an Attribute error in the code below. I am new to this kivy framework. When I tried to call the Clock.schedule_interval function to infinite scroll the image. Clock.schedule_interval(self.root.ids.mainwindow.scroll_textures, 1/60.)
AttributeError: 'NoneType' object has no attribute 'ids'
This error popped up.
The .py file
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.image import Image
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.uix.button import Button
from kivy.core.audio import SoundLoader
from kivy.config import Config
# You can create your kv code in the Python file
# Create a class for all screens in which you can include
# helpful methods specific to that screen
kv = Builder.load_file("main.kv")
class MainWindow(Screen):
cloud_texture = ObjectProperty(None)
floor_texture = ObjectProperty(None)
sound = ObjectProperty(None)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cloud_texture = Image(source = "cloud4.png").texture
self.cloud_texture.wrap = 'repeat'
self.cloud_texture.uvsize = (Window.width/self.cloud_texture.width,-1)
self.floor_texture = Image(source = "floor2.jpg").texture
self.floor_texture.wrap = 'repeat'
self.floor_texture.uvsize = (Window.width/self.floor_texture.width,-1)
self.sun_texture = Image(source = "sun.png").texture
self.sun_texture.uvsize = (Window.width/self.sun_texture.width,-1)
self.sound = SoundLoader.load('8bitmenu.wav')
self.sound.play()
def scroll_textures(self, time_passed):
#Update the uvpos
self.cloud_texture.uvpos = ((self.cloud_texture.uvpos[0] - time_passed/20) % Window.width, self.cloud_texture.uvpos[1])
self.floor_texture.uvpos = ((self.floor_texture.uvpos[0] + time_passed/8) % Window.width, self.floor_texture.uvpos[1])
#Redraw textures
texture = self.property('cloud_texture')
texture.dispatch(self)
texture = self.property('floor_texture')
texture.dispatch(self)
class Window1(Screen):
pass
class WindowManager(ScreenManager):
pass
#Config.write()
class MyApp(App):
def on_start(self):
Clock.schedule_interval(self.root.ids.mainwindow.scroll_textures, 1/60.)
pass
def build(self):
return kv
MyApp().run()
.kv file
<WindowManager>:
MainWindow:
Window1:
<MainWindow>:
name: "main"
id: mainwindow
canvas.before:
Rectangle:
size: self.size
pos: self.pos
source: "sky.jpg"
Rectangle:
size: self.width - 1000, 120
pos: self.pos[0] + 500, self.pos[1] + self.height - 138
texture: self.sun_texture
Rectangle:
size: self.width, 138
pos: self.pos[0], self.pos[1] + self.height - 168
texture: self.cloud_texture
Rectangle:
size: self.width, 100
pos: self.pos[0], self.pos[1]
texture: self.floor_texture
Image:
source: 'dog2.gif'
anim_delay: 0.09
size: self.width, self.height
pos: -270, -270
Image:
source: 'boy1.gif'
anim_delay: 0.09
size: self.width, self.height
pos: -100, -230
Button:
text: "Start your journey"
color: .8,.9,0,1
background_color: 1, 1, 1, 0.3
halign: 'center'
center: root.center
font_size: 12
size: 32, 32
size_hint: .2, .2
pos: 300, 250
on_release:
app.root.current = "win1"
<Window1>:
name: "win1"
Button:
text: "Go back"
on_release:
app.root.current = "main"
Can anyone help me with this?
I solved the Error here. Just declared the id in parent class and that worked
WindowManager:
MainWindow:
id: mainwindow
Window1:
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()
I am using python-2.7 and kivy .When i run test.py then i set focus on button.After that i click anywhere on window using mouse then focus does not remove . Because after click on window i press enter then it call def self.add().
Can someone tell me how to remove focus from button when click anywhere on
window ?
test.py
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ObjectProperty
from kivy.clock import Clock
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (500, 150)
class User(Screen):
name = ObjectProperty(None)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
Window.bind(on_key_down=self._on_keyboard_down)
Clock.schedule_once(self.name_focus, 1)
def name_focus(self, *args):
self.postUser.focus = True
self.postUser.background_color = [0.5, 0.5, 0.5, 1]
def _on_keyboard_down(self, instance, keyboard, keycode, text, modifiers):
if (hasattr(self.postUser, 'focus') and self.postUser.focus) and keycode == 40:
self.add()
def add(self):
print('button Event Call')
class Test(App):
def build(self):
return self.root
if __name__ == '__main__':
Test().run()
test.kv
#:kivy 1.10.0
User:
name: name
postUser : postUser
BoxLayout:
orientation: "vertical"
GridLayout:
cols: 2
padding: 20, 20
spacing: 10, 10
Label:
text: "Name"
text_size: self.size
valign: 'middle'
TextInput:
id:name
text_size: self.size
GridLayout:
cols: 2
padding: 0, 0
spacing: 5, 0
size_hint: .5, .35
pos_hint: {'x': .25, 'y': 0}
Button:
id:postUser
size_hint_x: .5
text: "Ok"
focus: False
on_release:
root.add()
You can add on_touch_up method in your User class.
def on_touch_up(self, touch):
if (hasattr(self.postUser, 'focus') and self.postUser.focus):
self.postUser.focus = False
self.postUser.background_color = [1, 1, 1, 1]
I am posting complete code.
test.py
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ObjectProperty
from kivy.clock import Clock
Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (500, 150)
class User(Screen):
name = ObjectProperty(None)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
Window.bind(on_key_down=self._on_keyboard_down)
Clock.schedule_once(self.name_focus, 1)
def name_focus(self, *args):
self.postUser.focus = True
self.postUser.background_color = [0.5, 0.5, 0.5, 1]
def _on_keyboard_down(self, instance, keyboard, keycode, text, modifiers):
if (hasattr(self.postUser, 'focus') and self.postUser.focus) and keycode == 40:
self.add()
def add(self):
print('button Event Call')
def on_touch_up(self, touch):
if (hasattr(self.postUser, 'focus') and self.postUser.focus):
self.postUser.focus = False
self.postUser.background_color = [1, 1, 1, 1]
class Test(App):
def build(self):
return self.root
if __name__ == '__main__':
Test().run()
test.kv
#:kivy 1.10.0
User:
name: name
postUser : postUser
BoxLayout:
orientation: "vertical"
GridLayout:
cols: 2
padding: 20, 20
spacing: 10, 10
Label:
text: "Name"
text_size: self.size
valign: 'middle'
TextInput:
id:name
text_size: self.size
GridLayout:
cols: 2
padding: 0, 0
spacing: 5, 0
size_hint: .5, .35
pos_hint: {'x': .25, 'y': 0}
Button:
id:postUser
size_hint_x: .5
text: "Ok"
focus: False
on_release:
root.add()
I am currently struggling with Kivy's screenmanager function. While it works in the menus it just won't show the character. I didn't find any helpful Information in the Documentation.
This works just fine so that class isn't the problem:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.core.window import Window
class Character(Widget):
pass
class Basic(Image):
def __init__(self, **kwargs):
super(Basic, 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)
print('Basic initialised')
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'left':
self.x -= 10
print('pressed left')
elif keycode[1] == 'right':
self.x += 10
print('pressed right')
elif keycode[1] == 'up':
self.y += 10
print('pressed up')
elif keycode[1] == 'down':
self.y -= 10
print('pressed down')
else:
return False
return True
class HemeApp(App):
def build(self):
wimg = Basic(source='sprite.png')
m = Character()
m.add_widget(wimg)
return m
if __name__ == '__main__':
HemeApp().run()
But when I want to display that on my GameScreen from ScreenManager it's just a Blackscreen.
This is the code:
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.config import Config
from kivy.vector import Vector
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.core.window import Window
from kivy.animation import Animation
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_file('heme.kv')
##############################################################################################################################
class HemeApp(App):
title = 'geme'
icon = 'custoicon.png'
def build(self):
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
sm.add_widget(CharacterCreationScreen(name='creationscreen'))
sm.add_widget(GameScreen(name='gameplay'))
return sm
def save(self, plrname):
fob = open('stats.txt', 'w+')
fob.write(plrname + "\n")
fob.close()
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
pass
class CharacterCreationScreen(Screen):
pass
class Character(Widget):
pass
class GameScreen(Screen):
def playy(self):
print ('playy fired')
wimg = Basic(source='sprite.png')
m = Character()
m.add_widget(wimg)
return m
def on_enter(self):
print ("on_enter fired")
self.playy()
class MyButton(ButtonBehavior, Image):
def __init__(self, **kwargs):
super(MyButton, self).__init__(**kwargs)
self.source = 'button.png'
def on_press(self):
self.source = 'button_pressed.png'
def on_release(self):
self.source = 'button.png'
class Basic(Image):
def __init__(self, **kwargs):
super(Basic, 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)
print('Basic initialised')
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
print ('on_keyboard_down active')
if keycode[1] == 'left':
self.x -= 10
elif keycode[1] == 'right':
self.x += 10
else:
return False
return True
if __name__ == '__main__':
HemeApp().run()
Plus the Kivy code:
#; kivy 1.10.0
#:import Factory kivy.factory.Factory
<MenuScreen>:
FloatLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'bg.png'
MyButton:
pos: (330, 400)
allow_stretch: True
background_color: (0,1,0,1)
size_hint: (.2,.2)
text: 'Play'
on_release:
root.manager.transition.direction = 'right'
root.manager.current = 'creationscreen'
Label:
pos: (330, 450)
text: 'Play'
color: (1,0,0)
MyButton:
pos: (330, 300)
size_hint: (.2,.2)
text: 'Goto settings'
on_release:
root.manager.transition.direction = 'right'
root.manager.current = 'settings'
MyButton:
pos: (330, 200)
background_color: (1,0,0,1)
size_hint: (.2,.2)
text: 'Quit'
on_release: exit()
<SettingsScreen>:
FloatLayout:
MyButton:
pos: (330, 400)
size_hint: (.2,.2)
text: 'My settings button'
MyButton:
pos: (330, 300)
size_hint: (.2,.2)
text: 'Back to menu'
on_release:
root.manager.transition.direction = 'left'
root.manager.current = 'menu'
<CharacterCreationScreen>:
name: name_input
FloatLayout:
MyButton:
pos: (330, 400)
name: name_input
size_hint: (.2,.2)
text: 'Start game'
on_release:
root.manager.transition.direction = 'right'
root.manager.current = 'gameplay'
MyButton:
pos: (430, 300)
text: 'Save'
on_release: app.save(name_input.text)
size_hint: (.2,.2)
TextInput:
pos: (230, 300)
id: name_input
font_size: 20
multiline: False
size_hint: (.2,.2)
MyButton:
pos: (330, 200)
size_hint: (.2,.2)
text: 'Back to menu'
on_release:
root.manager.transition.direction = 'left'
root.manager.current = 'menu'
<GameScreen>:
game: game
Character:
id: game
pos: self.pos
Problem
The character was not displayed because no location was given to add the character.
Solution
Replaced return m with self.add_widget(m)
Snippet
class GameScreen(Screen):
def playy(self):
print('playy fired')
wimg = Basic(source='sprite.png')
m = Character()
m.add_widget(wimg)
self.add_widget(m)
Output
i just started using Kivy for my application and i got a problem.
i try to clean my canvas but i cant relate the button to the canvas
class DrawInput(Widget):
def on_touch_down(self, touch):
print(touch)
with self.canvas:
touch.ud["line"] = Line(points=(touch.x, touch.y), width=100)
def on_touch_move(self, touch):
#print(touch)
touch.ud["line"].points += (touch.x, touch.y)
def on_touch_up(self, touch):
self.export_to_png("roy.png")
print("RELEASED!", touch)
def cleaner(self):
self.canvas.clear()
class AnotherScreen(Screen):
pass
presentation = Builder.load_file("main2.kv")
class MainApp(App):
def build(self):
return presentation
def clear_canvas(self, obj):
MainApp().run()
and here is the main2.kv
GridLayout:
cols: 2
Button:
on_release: root.change_text()
color: 0,1,0,1
font_size: 25
size_hint: 0.3,0.2
text: root.random_number
pos_hint: {"right":1, "top":1}
DrawInput
Button:
on_release: root.clean()
color: 0,1,0,1
font_size: 25
size_hint: 0.3,0.2
text: "Clear"
my problem is that i need to call the Clean Method from other class, but when i try it its says that i need to send "Self", does anyone can help me?
just trying to clean the canvas that relate to the DrawInput
You have to use ObjectProperty to hook up to the DrawInput child widget to reference the cleaner method. Please refer to the example below for details.
Example
main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Line
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
class DrawInput(Widget):
def on_touch_down(self, touch):
print(touch)
with self.canvas:
touch.ud["line"] = Line(points=(touch.x, touch.y), width=100)
def on_touch_move(self, touch):
#print(touch)
touch.ud["line"].points += (touch.x, touch.y)
def on_touch_up(self, touch):
# self.export_to_png("roy.png")
self.export_to_png("kivy-logo-black-64.png")
print("RELEASED!", touch)
def cleaner(self):
print("cleaner: ", self)
self.canvas.clear()
class FirstScreen(Screen):
draw = ObjectProperty()
class AnotherScreen(Screen):
pass
class Manager(ScreenManager):
mgr = ObjectProperty()
def change_text(self):
pass
def random_number(self):
pass
def clean(self):
print("clean: ", self)
self.mgr.draw.cleaner()
class TestApp(App):
def build(self):
return Manager()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
<DrawInput>:
<Manager>:
mgr: first_screen
FirstScreen:
id: first_screen
AnotherScreen:
<FirstScreen>:
draw: draw_input
GridLayout:
cols: 2
Button:
on_release: app.root.change_text()
color: 0,1,0,1
font_size: 25
size_hint: 0.3, 0.2
# text: app.root.random_number()
text: "123"
pos_hint: {"right":1, "top":1}
DrawInput:
id: draw_input
Button:
on_release: app.root.clean()
color: 0,1,0,1
font_size: 25
size_hint: 0.3, 0.2
text: "Clear"
<AnotherScreen>:
Output