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()
Related
I am developing a game in which users must match images by their initial letter (in Spanish), so that when they drag to a point (the cauldron) an image that begins with the correct letter (in this case the igloo, the Indian and the magnet) this image disappears.Example screen
In other words, basically, an image disappears when dragged to a specific point.
*.kv
#:import win kivy.core.window
<Picture#Scatter>:
source: None
on_size: self.center = win.Window.center
size: image.size
size_hint: None, None
do_scale: False
do_rotation: False
Image:
id: image
source: root.source
size: 250, 250 / self.image_ratio
<Relaciona3x2>:
AnchorLayout:
Image:
source: 'data/img/fondobosque.jpg'
allow_stretch: True
keep_ratio: False
FloatLayout:
size_hint: 1, 1
Image:
id: img003
source: 'data/img/caldero.png'
size_hint: 0.55, 0.55
pos_hint: {"center_x": 0.5, "center_y": 0.20}
Button:
size_hint:.06, 0.1
text: "Volver al menú"
on_release: app.root.current = 'menu'
Picture:
id: potionscatter
source: "data/img/letra_i/iglú.png"
pos: 175, 680
Picture:
source: "data/img/letra_i/indio.png"
pos: 835, 680
Picture:
source: "data/img/letra_m/moto.png"
pos: 1495, 680
Picture:
source: "data/img/letra_u/uña.png"
pos: 175, 420
Picture:
source: "data/img/letra_i/imán_1.png"
pos: 835, 420
Picture:
source: "data/img/letra_u/urraca.png"
pos: 1495, 420
<Relaciona4x2Screen>:
AnchorLayout:
Image:
source: 'data/img/fondobosque.jpg'
allow_stretch: True
keep_ratio: False
FloatLayout:
size_hint: 1, 1
Image:
id: img003
source: 'data/img/caldero.png'
size_hint: 0.55, 0.55
pos_hint: {"center_x": 0.5, "center_y": 0.20}
Button:
size_hint:.06, 0.1
text: "Volver al menú"
on_release: app.root.current = 'menu'
<Relaciona5x2Screen>:
AnchorLayout:
Image:
source: 'data/img/fondobosque.jpg'
allow_stretch: True
keep_ratio: False
FloatLayout:
size_hint: 1, 1
Image:
id: img003
source: 'data/img/caldero.png'
size_hint: 0.55, 0.55
pos_hint: {"center_x": 0.5, "center_y": 0.20}
Button:
size_hint:.06, 0.1
text: "Volver al menú"
on_release: app.root.current = 'menu'
relaciona.py
__all__ = ('Relaciona3x2', 'Relaciona4x2', 'Relaciona5x2')
import kivy
kivy.require('1.0.6')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_file('relaciona.kv')
class Relaciona3x2(Screen):
pass
class Relaciona4x2(Screen):
pass
class Relaciona5x2(Screen):
pass
main.py
import kivy
kivy.require('1.0.6')
from kivy.uix.popup import Popup
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.animation import Animation
from kivy.uix.button import Button
from kivy.uix.scatter import Scatter
from kivy.logger import Logger
from kivy.uix.gesturesurface import GestureSurface
from letras import DrawGame, ImageEx, SoundEx, ContainerBox
from explota import BubblePop, BubbleGame
from relaciona import *
from borra import Borra
from caza import Caza
from config import Config
from kivy.core.window import Window
Builder.load_file('design.kv')
class MyScreenManager(ScreenManager):
def __init__(self):
super (MyScreenManager, self).__init__()
class HomeMenu(Screen):
pass
class LetrasScreen(Screen):
pass
class ExplotaScreen(Screen):
pass
class Relaciona3x2Screen(Screen):
pass
class Relaciona4x2Screen(Screen):
pass
class Relaciona5x2Screen(Screen):
pass
class BorraScreen(Screen):
pass
class CazaScreen(Screen):
pass
class ConfigScreen(Screen):
pass
class myApp(App):
def build(self):
Window.fullscreen = 'auto'
return MyScreenManager()
def on_pause(self):
return True
def on_resume(self):
pass
myApp().run()
I have used DragNDropWidget to solve this problem. It's quite simple to use but now I don't know how to change the size of the buttons, I would like them to be bigger and somewhat separated from each other.
DragNDropWidget.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from kivy.core.window import Window
from kivy.animation import Animation
import copy
from kivy.uix.widget import Widget
from kivy.properties import (
ListProperty, NumericProperty, BooleanProperty, ObjectProperty)
class DragNDropWidget(Widget):
# let kivy take care of kwargs and get signals for free by using
# properties
droppable_zone_objects = ListProperty([])
bound_zone_objects = ListProperty([])
drag_opacity = NumericProperty(1.0)
drop_func = ObjectProperty(None)
drop_args = ListProperty([])
remove_on_drag = BooleanProperty(True)
def __init__(self, **kw):
super(DragNDropWidget, self).__init__(**kw)
self.register_event_type("on_drag_start")
self.register_event_type("on_being_dragged")
self.register_event_type("on_drag_finish")
self.register_event_type("on_motion_over")
self.register_event_type("on_motion_out")
self._dragged = False
self._dragable = True
self._fired_already = False
def set_dragable(self, value):
self._dragable = value
def set_remove_on_drag(self, value):
"""
This function sets the property that determines whether the dragged widget is just copied from its parent or taken from its parent
#param value: either True or False. If True then the widget will disappear from its parent on drag, else the widget will jsut get copied for dragging
"""
self.remove_on_drag = value
def set_bound_axis_positions(self):
for obj in self.bound_zone_objects:
try:
if self.max_y < obj.y+obj.size[1]-self.size[1]:
self.max_y = obj.y+obj.size[1]-self.size[1]
except AttributeError:
self.max_y = obj.y+obj.size[1]-self.size[1]
try:
if self.max_x < obj.x+obj.size[0]-self.size[0]:
self.max_x = obj.x + obj.size[0]-self.size[0]
except AttributeError:
self.max_x = obj.x+obj.size[0]-self.size[0]
try:
if self.min_y > obj.y:
self.min_y = obj.y
except AttributeError:
self.min_y = obj.y
try:
if self.min_x > obj.x:
self.min_x = obj.x
except AttributeError:
self.min_x = obj.x
def on_touch_down(self, touch):
if self.collide_point(touch.x, touch.y) and self._dragable:
# detect if the touch is short - has time and end (if not dispatch drag)
if abs(touch.time_end - touch.time_start) > 0.2:
self.dispatch("on_drag_start")
def on_touch_up(self, touch):
if self._dragable and self._dragged:
self.short_touch = True
self.dispatch("on_drag_finish")
self.short_touch = False
def on_touch_move(self, touch):
if self._dragged and self._dragable:
x = touch.x
y = touch.y
try:
if touch.x < self.min_x:
x = self.min_x
if touch.x > self.max_x:
x = self.max_x
if touch.y < self.min_y:
y = self.min_y
if touch.y > self.max_y:
y = self.max_y
except AttributeError:
pass
self.pos = (x, y)
def easy_access_dnd(self, function_to_do, function_to_do_out, arguments = [], bind_functions = []):
"""
This function enables something that can be used instead of drag n drop
#param function_to_do: function that is to be called when mouse_over event is fired on the widget
#param bind_functions: what is really to be done - background function for GUI functionality
"""
Window.bind(mouse_pos=self.on_motion)
self.easy_access_dnd_function = function_to_do
self.easy_access_dnd_function_out = function_to_do_out
self.easy_access_dnd_function_aguments = arguments
self.easy_access_dnd_function_binds = bind_functions
def on_motion(self, etype, moutionevent):
if self.collide_point(Window.mouse_pos[0], Window.mouse_pos[1]):
if not self._fired_already:
self.dispatch("on_motion_over")
else:
self.dispatch("on_motion_out")
def on_motion_over(self):
self.easy_access_dnd_function(
self.easy_access_dnd_function_aguments,
self.easy_access_dnd_function_binds)
self._fired_already = True
def on_motion_out(self):
try:
self.easy_access_dnd_function_out()
except AttributeError:
pass
self._fired_already = False
def on_drag_start(self):
print ('drag start')
self.opacity = self.drag_opacity
self.set_bound_axis_positions()
self._old_drag_pos = self.pos
self._old_parent = self.parent
self._old_index = self.parent.children.index(self)
self._dragged = True
if self.remove_on_drag:
self.reparent(self)
else:
#create copy of object to drag
self.reparent(self)
# the final child class MUST implement __deepcopy__
# IF self.remove_on_drag == False !!! In this case this is
# met in DragableArhellModelImage class
copy_of_self = copy.deepcopy(self)
self._old_parent.add_widget(copy_of_self, index=self._old_index)
def on_drag_finish(self):
print ('drag finish')
if self._dragged and self._dragable:
self.opacity = 1.0
dropped_ok = False
for obj in self.droppable_zone_objects:
if obj.collide_point(*self.pos):
dropped_ok = True
if dropped_ok:
self.drop_func(*self.drop_args)
anim = Animation(opacity=0, duration=0.7, t="in_quad")
anim.bind(on_complete=self.deparent)
anim.start(self)
else:
anim = Animation(pos=self._old_drag_pos, duration=0.7, t="in_quad")
if self.remove_on_drag:
anim.bind(on_complete = self.reborn)
else:
anim.bind(on_complete = self.deparent)
anim.start(self)
self._dragged = False
def deparent(self, widget="dumb", anim="dumb2"):
self.get_root_window().remove_widget(self)
def on_being_dragged(self):
print ('being dragged')
def reborn(self, widget, anim):
self.deparent()
self._old_parent.add_widget(self, index=self._old_index)
def reparent(self, widget):
parent = widget.parent
orig_size = widget.size
if parent:
parent.remove_widget(widget)
parent.get_root_window().add_widget(widget)
widget.size_hint = (None, None)
widget.size = orig_size
DragableButton.py
Created on Oct 24, 2012
#author: Pavel Kostelnik
'''
from DragNDropWidget import DragNDropWidget
from kivy.uix.button import Button
class DragableButton(Button, DragNDropWidget):
'''
classdocs
'''
def __init__(self, **kw):
'''
Constructor
'''
#Button.__init__(self, **kw)
super(DragableButton, self).__init__(**kw)
self.size_hint = (None, None)
def __deepcopy__(self, dumb):
return DragableButton(text=self.text,
droppable_zone_objects=self.droppable_zone_objects,
bound_zone_objects=self.bound_zone_objects,
drag_opacity=self.drag_opacity,
drop_func=self.drop_func,
remove_on_drag=self.remove_on_drag)
arrastrar.py
__all__ = ('Relaciona3x2', 'Relaciona4x2', 'Relaciona5x2')
import kivy
kivy.require('1.0.6')
from DragableButton import DragableButton
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_file('arrastrar.kv')
class Relaciona3x2(Screen):
pass
class Relaciona4x2(Screen):
pass
class Relaciona5x2(Screen):
pass
class ArrastraApp(App):
def build(self):
#Window.fullscreen = 'auto'
return Relaciona3x2()
def greet(self):
print('Draggin done!')
if __name__ == "__main__":
ArrastraApp().run()
arrastrar.kv
#:kivy 1.9.0
<Relaciona3x2>:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'data/img/fondobosque.jpg'
BoxLayout:
orientation: 'vertical'
BoxLayout:
id: from_box
DragableButton:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'data/img/letra_i/iglu.png'
bound_zone_objects: [from_box, to_box ]
droppable_zone_objects: [to_box, ]
drop_func: app.greet
DragableButton:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'data/img/letra_i/indio.png'
bound_zone_objects: [from_box, to_box, ]
droppable_zone_objects: [to_box, ]
drop_func: app.greet
DragableButton:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'data/img/letra_m/moto.png'
bound_zone_objects: [from_box, to_box, ]
#droppable_zone_objects: [to_box, ]
drop_func: app.greet
BoxLayout:
id: from_box
DragableButton:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'data/img/letra_u/una.png'
bound_zone_objects: [from_box, to_box, ]
#droppable_zone_objects: [to_box, ]
drop_func: app.greet
DragableButton:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'data/img/letra_i/iman_1.png'
bound_zone_objects: [from_box, to_box, ]
droppable_zone_objects: [to_box, ]
drop_func: app.greet
DragableButton:
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'data/img/letra_u/urraca.png'
bound_zone_objects: [from_box, to_box, ]
#droppable_zone_objects: [to_box, ]
drop_func: app.greet
Image:
id: to_box
source: "data/img/caldero.png"
<Relaciona4x2>:
AnchorLayout:
Image:
source: "data/img/fondobosque.jpg"
allow_stretch: True
keep_ratio: False
<Relaciona5x2>:
AnchorLayout:
Image:
source: "data/img/fondobosque.jpg"
allow_stretch: True
keep_ratio: False
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:
As proposed in my last thread I ask the question because the problem changed. I now have the python code before the builder and now I does show a blank screen in the color of wordApp
I use 2 classes defined in one python file. I want to reference them inside the builder.load_string so that their contents are shown in the respective Screen.
Any help would be greatly appreciated as I am close to the finish of my project.
class Word(Widget):
def __init__(self, **kwargs):
self.textlabel = Label(text="labeltext", pos=(300, 300))
self.add_widget(self.textlabel)
def gonext(self ,btn_inst):
sm.current = "settings"
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
pass
class Favorites(Screen):
pass
class ScreenManager(ScreenManager):
pass
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
sm.add_widget(Favorites(name='favs'))
class WordApp(App):
def build(self):
Window.clearcolor = (1,0,0.3,1)
return sm
if __name__ == '__main__':
WordApp().run()
class WordFile(Widget):
def __init__(self, **kwargs):
self.textlabeldef = Label(text="labeltextdef", pos=(300, 100))
self.add_widget(self.textlabeldef)
class WordFileApp(App):
def build(self):
Window.clearcolor = (0,1,0.3,1)
return sm
Builder.load_string("""
#:kivy 1.9.0
<MenuScreen>:
Word:
Button:
text: 'change word'
font_size: 30
width: root.width
center_x: 400
center_y: root.width / 2
<SettingsScreen>:
WordFile:
Label:
width: root.width
height: 30
top: root.height
id: entry
text:"ac"
font_size:18
multiline:True
center_x: root.width/2
canvas.before:
Color:
rgba: 1, 0, 1, 1
Rectangle:
pos: self.pos
size: self.size
<Favorites>:
Word:
""")
There are a lot of error in your code:
class ScreenManager(ScreenManager):
pass
This does nothing
You declare some classes you use, after the start of the App
You created and build two Apps
There is no WordFile rule in the kv part, although you use it
Some classes had a wrong __init__
Maybe more, that I forgot :o)
This is some code that runs:
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.widget import Widget
Builder.load_string("""
#:kivy 1.9.0
<MenuScreen>:
Word:
Button:
text: 'change word'
font_size: 30
width: root.width
center_x: 400
center_y: root.width / 2
<SettingsScreen>:
WordFile:
Label:
width: root.width
height: 30
top: root.height
id: entry
text:"ac"
font_size:18
multiline:True
center_x: root.width/2
canvas.before:
Color:
rgba: 1, 0, 1, 1
Rectangle:
pos: self.pos
size: self.size
<Favorites>:
Word:
<WordFile>:
""")
class SettingsScreen(Screen):
pass
class Favorites(Screen):
pass
class MenuScreen(Screen):
pass
class Word(Widget):
def __init__(self, **kwargs):
super(Word, self).__init__(**kwargs)
self.textlabel = Label(text="labeltext", pos=(300, 300))
self.add_widget(self.textlabel)
def gonext(self, btn_inst):
sm.current = "settings"
class WordFile(Widget):
def __init__(self, **kwargs):
super(WordFile, self).__init__(**kwargs)
self.textlabeldef = Label(text="labeltextdef", pos=(300, 100))
self.add_widget(self.textlabeldef)
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
sm.add_widget(Favorites(name='favs'))
class WordApp(App):
def build(self):
Window.clearcolor = (1, 0, 0.3, 1)
return sm
if __name__ == '__main__':
WordApp().run()
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()
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