Clean Canvas in kivy language from other class - python

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

Related

Updating Created Custom KivyMD Property

I'm hoping there's a way to do this but not entirely sure. I want a way to update all of my labels after a button press. This is how I'm currently doing it which throws an error but I don't quite understand it.
At the moment I'm trying to add an id to the custom label and then call it but that doesn't work.
Python Code:
from kivy.lang import Builder
from kivymd.app import MDApp
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = Builder.load_file("main.kv")
def build(self):
self.theme_cls.theme_style = "Dark"
return self.screen
def button_press(self):
self.root.ids.custom_label.text = "Two"
if __name__ == '__main__':
MainApp().run()
Kivy File:
<MDLabel>
id: custom_label
text: "One"
halign: "center"
BoxLayout:
orientation: "vertical"
MDLabel
MDLabel
MDRoundFlatButton:
text: "Press"
pos_hint: {"center_x": 0.5, "center_y": 0.5}
on_release: app.button_press()
First things first you can't name a custom label, MDLabel.
restructure your code like so. Starting with your .kv file
<CustomLabel#MDLabel>
text: "One"
halign: "center"
BoxLayout:
orientation: "vertical"
CustomLabel:
id: custom_label_1
CustomLabel:
id: custom_label_2
MDRoundFlatButton:
text: "Press"
pos_hint: {"center_x": 0.5, "center_y": 0.5}
on_release: app.button_press()
The in your .py file
from kivy.lang import Builder
from kivymd.app import MDApp
class MainApp(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = Builder.load_file("main.kv")
def build(self):
self.theme_cls.theme_style = "Dark"
return self.screen
def button_press(self):
self.root.ids.custom_label_1.text = "Two"
self.root.ids.custom_label_2.text = "Two"
if __name__ == '__main__':
MainApp().run()
Hope that works. you can't have 2 widgets using the same id. so if you wanna change the the text of 2 custom labels you gonna have to reference them individually

Why does rescheduling Clock intervals cause image positions to reset?

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()

2 classes referenced in kivy returns empty screen

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()

My self.canvas.clear() function not working but other codes in the method run

This is the .kv string:
Builder.load_string('''
<Paint>:
Button:
text: 'clear'
on_release: app.instance.cleaner()
''')
This is the class
class Paint(Widget):
def cleaner(self, *args):
self.canvas.clear()
print('Cleared')
The print('Cleared') prints perfectly fine when the button is clicked,but self.canvas.clear() doesn't do anything
class MainApppp(App):
def build(self):
self.instance = Paint()
return Background()
In the following example it illustrates using canvas.clear() function to erase a widget.
Example
main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
kv_string = '''
<Paint>:
Button:
text: 'clear'
on_release:
# root.cleaner()
app.root.erase()
<MyWidget>:
canvas:
Color:
rgb: 0.1, 0.6, 0.3
Ellipse:
size: self.size
pos: self.pos
Color:
rgb: 0.6, 0.2, 0.1
Ellipse:
size: self.size
pos: self.center
<Background>:
MyWidget:
id: mywidget
Paint:
id: btn
'''
Builder.load_string(kv_string)
class Paint(Widget):
def cleaner(self, *args):
self.canvas.clear()
print('Cleared')
class MyWidget(Widget):
pass
class Background(BoxLayout):
def erase(self):
self.ids.mywidget.canvas.clear()
class TestApp(App):
title = "Kivy Widget's canvas.clear() Demo"
def build(self):
return Background()
if __name__ == '__main__':
TestApp().run()
Output

How do I make on_touch_down widget specific?

I'm trying to create a simple drawing app in kivy but i'm having some issues with the
on_touch_down
function as it regards the entire class and not just a specific widget. So when I use the on touch down and on touch move functions to draw on the canvas it affects and effectively disables the touch down functions bound to buttons. Here's the code where the button doesn't work.
python code:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.graphics import Line
from kivy.graphics import *
from kivy.uix.widget import Widget
class MyScreenManager(ScreenManager):
pass
class MenuScreen(Screen):
pass
class DrawScreen(Screen):
def on_touch_down(self, touch):
with self.canvas.before:
Color(1, 0, 0)
touch.ud["line"] = Line(points=(touch.x, touch.y), width=5)
def on_touch_move(self, touch):
touch.ud["line"].points += (touch.x, touch.y)
class DrawApp(App):
def build(self):
return MyScreenManager()
DrawApp().run()
kivy code:
<MenuButton#Button>:
font_size: 65
size_hint: 0.4, 0.25
<MyScreenManager>:
MenuScreen:
id: menu
name: "menu"
DrawScreen:
id: draw
name: "draw"
<MenuScreen>:
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
size: self.size
pos: self.pos
MenuButton:
text: "Draw"
on_release: root.manager.current = "draw"
pos_hint:{"center_x":0.5, "center_y":0.6}
MenuButton:
text: "Quit"
on_release: app.stop()
pos_hint:{"center_x":0.5, "center_y":0.3}
<DrawScreen>:
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
size: self.size
pos: self.pos
Button:
id: but
size_hint: 0.2,0.1
pos_hint_x: 0 + self.width
font_size: 30
text: "Back"
on_release: root.manager.current = "menu"
I managed to find a simple work around by using collide_point, here's my workaround code:
class DrawScreen(Screen):
def on_touch_down(self, touch):
but = self.ids.but
if but.collide_point(touch.x, touch.y):
self.manager.current = "menu"
else:
with self.canvas.before:
Color(1, 0, 0)
touch.ud["line"] = Line(points=(touch.x, touch.y), width=5)
def on_touch_move(self, touch):
touch.ud["line"].points += (touch.x, touch.y)
But while this works it brings up a whole world of new problems like me having to manually configure every button to change source when held down and the function not running until the button is released. It also means that everything I add to the class has to be added to the if statement.
I'm quite positive that there has to be a simpler way. My first thought was that maybe one could add the on touch down to only affect one widget? My second thought was that maybe if would be better to not draw on the canvas or something?
Any help or pointers are appreciated, thanks!
when you overwrite a method, you must return the same method of the super class
something like this:
...
class DrawScreen(Screen):
def on_touch_down(self, touch):
with self.canvas.before:
Color(1, 0, 0)
touch.ud["line"] = Line(points=(touch.x, touch.y), width=5)
return super(DrawScreen, self).on_touch_down(touch)
def on_touch_move(self, touch):
touch.ud["line"].points += (touch.x, touch.y)
return super(DrawScreen, self).on_touch_move(touch)

Categories