I am trying to create a custom kivy widget, but when I render it in my test app, a blank screen shows up. I've tried tracing code calls and it looks like all of the initialization and drawing methods are being called (I have included the terminal output from the print statements below the widget code. I am also including my test app, but this is very simple and is the same shell I've used for several working apps I've already created). My application main loop begins, but nothing is displayed. Why am I not seeing my widget?
[Edit]So I've gotten the NodeEnd objects to render with some major structural changes, bringing a lot of the code into .kv language. I'm replacing the code on the post with the newer version. Unfortunately, the label between them is still not rendering
nodewidget.kv
#: kivy 1.9.0
<NodeEnd>
canvas:
Color:
rgba: self.color[0], self.color[1], self.color[2], self.color[3]
Ellipse:
pos: self.center
size: self.width / 2, self.height / 2
angle_start: self.degree_range[0]
angle_end: self.degree_range[1]
<NodeWidget>
left: left
body: body
right: right
GridLayout:
cols: 3
NodeEnd:
id: left
degree_range: 180, 360
on_pressed: self.PressedLeft(self, args[0])
on_moved: self.MovedLeft(self, args[0])
on_released: self.ReleasedLeft(self, args[0])
on_color: self.LeftSwitch(self, args[0])
NodeBody:
id: body
text: self.title
on_pressed: self.PressedBody(self, args[0])
on_moved: self.MovedBody(self, args[0])
on_released: self.ReleasedBody(self, args[0])
on_pressed_flag: self.BodySwitch(self, args[0])
NodeEnd:
id: right
degree_range: 0, 180
on_pressed: self.PressedRight(self, args[0])
on_moved: self.MovedRight(self, args[0])
on_released: self.ReleasedRight(self, args[0])
on_color: self.RightSwitch(self, args[0])
NodeWidget3.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ListProperty, StringProperty, BooleanProperty, ObjectProperty
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.graphics import *
from kivy.lang import Builder
Builder.load_file('nodewidget.kv')
class NodeWidget(GridLayout):
title = StringProperty('')
pressed_body = ListProperty([0, 0])
released_body = ListProperty([0, 0])
moved_body = ListProperty([0, 0])
switch_body = BooleanProperty(False)
pressed_left = ListProperty([0, 0])
released_left = ListProperty([0, 0])
moved_left = ListProperty([0, 0])
switch_left = BooleanProperty(False)
pressed_right = ListProperty([0, 0])
released_right = ListProperty([0, 0])
moved_right = ListProperty([0, 0])
switch_right = BooleanProperty(False)
left = ObjectProperty(None)
body = ObjectProperty(None)
right = ObjectProperty(None)
def __init__(self, **kwargs):
super(NodeWidget, self).__init__(**kwargs)
def PressedBody(self, *args):
self.pressed_body = args
def MovedBody(self, *args):
self.moved_body = args
def ReleasedBody(self, *args):
self.released_body = args
def BodySwitch(self, *args):
pass
def PressedLeft(self, *args):
self.pressed_left = args
def MovedLeft(self, *args):
self.moved_left = args
def ReleasedLeft(self, *args):
self.released_left = args
def LeftSwitch(self, *args):
pass
def PressedRight(self, *args):
self.pressed_right = args
def MovedRight(self, *args):
self.moved_right = args
def ReleasedRight(self, *args):
self.released_right = args
def RightSwitch(self, *args):
pass
class NodeBody(Label):
#on_color is triggered on down or up on the button and move off or on the button
#it will return the initial values when the button is not pressed
#when the button is pressed, this will return True
pressed_flag = BooleanProperty(False)
#on_pressed is triggered when the button is initially hit
pressed = ListProperty([0, 0])
#on_released is triggered when the button is released
released = ListProperty([0, 0])
#on_moved is triggered when the touch is moved and the touch is not taken outside the button
moved = ListProperty([0, 0])
title = StringProperty('')
def __init__(self, **kwargs):
super(NodeBody, self).__init__(**kwargs)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.pressed = touch.pos
self.pressed_flag = True
return touch
return super(NodeBody, self).on_touch_down(touch)
def on_touch_move(self, touch):
if self.collide_point(*touch.pos):
self.moved = touch.pos
self.pressed_flag = True
return touch
self.pressed_flag = False
return super(NodeBody, self).on_touch_move(touch)
def on_touch_up(self, touch):
if self.collide_point(*touch.pos):
self.released = touch.pos
self.pressed_flag = False
return touch
return super(NodeBody, self).on_touch_up(touch)
class NodeEnd(Widget):
#on_color is triggered on down or up on the button and move off the button
#it will return the initial values when the button is not pressed
#when the button is pressed, this will return [0.8, 0.2, 0.2, 1]
#Not meant to be updated via code, only generate events
color = ListProperty([0.7, 0.1, 0.1, 0.8])
#[90, 270] for left
#[270, 90] for right
degree_range = ListProperty([0, 0])
#on_pressed is triggered when the button is initially hit
pressed = ListProperty([0, 0])
#on_released is triggered when the button is released
released = ListProperty([0, 0])
#on_moved is triggered when the button is moved and the touch is not taken outside the button
moved = ListProperty([0, 0])
def __init__(self, **kwargs):
super(NodeEnd, self).__init__(**kwargs)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.pressed = touch.pos
self.color = [0.8, 0.2, 0.2, 1]
return touch
return super(NodeEnd, self).on_touch_down(touch)
def on_touch_move(self, touch):
if self.collide_point(*touch.pos):
self.moved = touch.pos
self.color = [0.8, 0.2, 0.2, 1]
return touch
self.color = [0.7, 0.1, 0.1, 0.8]
self.released = touch.pos
return super(NodeEnd, self).on_touch_move(touch)
def on_touch_up(self, touch):
if self.collide_point(*touch.pos):
self.released = touch.pos
self.color = [0.7, 0.1, 0.1, 0.8]
return touch
return super(NodeEnd, self).on_touch_up(touch)
The test app:
nodeeditor.kv
#kivy 1.9.0
<NodeEditorWidget>:
GridLayout:
size: root.size
cols: 3
NodeWidget:
title: 'Test 1'
on_pressed_body: app.SelectNode(self, args[0])
on_moved_body: app.MoveNode(self, args[0])
on_released_body: app.DeselectNode(self, args[0])
on_pressed_left: app.SelectLeft(self, args[0])
on_moved_left: app.MoveLeft(self, args[0])
on_released_left: app.DeselectLeft(self, args[0])
on_pressed_right: app.SelectRight(self, args[0])
on_moved_right: app.MoveRight(self, args[0])
on_released_right: app.DeselectRight(self, args[0])
NodeTest.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.properties import ObjectProperty, StringProperty
from kivy.lang import Builder
from NodeWidget3 import NodeWidget
Builder.load_file('nodeeditor.kv')
class NodeEditorWidget(Widget):
pass
class NodeEditorApp(App):
def build(self):
return NodeEditorWidget()
print('Node Editor initialized')
def SelectNode(self, *args):
pass
def MoveNode(self, *args):
pass
def DeselectNode(self, *args):
pass
def SelectLeft(self, *args):
pass
def MoveLeft(self, *args):
pass
def DeselectLeft(self, *args):
pass
def SelectRight(self, *args):
pass
def MoveRight(self, *args):
pass
def DeselectRight(self, *args):
pass
if __name__ == '__main__':
NodeEditorApp().run()
I'm new to Kivy and Python so i hope this helps. When declaring and adding the widgets to your layout try adding 'self' at the beginning. Also in your original app i noticed you imported App but i don't see an App class.
In your test I added self to all the things and I also added a button. The button appeared in the window but the rest was black. So I have to ask when you are adding your widgets did you pick a color for them? It may be they are appearing black by default or/and might be the self thing. Hope this helps.
Related
I have a screen with multiple widgets within it. I need to detect if the user clicked the label - 'home'
Whenever I click the screen the on_touch_down and on_touch_up is triggered. how can I determine the label that I clicked..
class WordScreen(Screen):
def __init__(self, **kwargs):
label_text = kwargs['label_text']
del kwargs['label_text']
super(WordScreen, self).__init__(**kwargs)
main_layout = BoxLayout(id='test', orientation='vertical')
navigation_layout = BoxLayout(orientation='horizontal',
size_hint_y=.1)
navigation_layout.add_widget(Label(text='home'))
navigation_layout.add_widget(Label(text='audio'))
navigation_layout.add_widget(Label(text='favorite'))
text_layout = BoxLayout(orientation='vertical')
text_layout.add_widget(Label(id='sight_text', text=label_text))
main_layout.add_widget(navigation_layout)
main_layout.add_widget(text_layout)
self.add_widget(main_layout)
def on_touch_down(self, touch):
self.initial = touch.x
def on_touch_up(self, touch):
print('on_touch_up - ', touch.x)
print(self.collide_point(*touch.pos))
print(touch.button)
s = None
try:
s = self.manager.get_screen('settings')
except ScreenManagerException:
pass
print('screen - ', s)
if s is not None:
self.manager.clear_widgets([s])
print('screen removed')
if touch.x < self.initial:
self.manager.transition = SlideTransition(direction="right")
self.manager.current = self.manager.next()
elif touch.x > self.initial:
self.manager.transition = SlideTransition(direction="right")
self.manager.current = self.manager.previous()
Based on link from John Anderson comment I created clickable Label.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
class MyLabel(ButtonBehavior, Label):
def on_press(self):
print("press:", self.text)
def on_release(self):
print("release:", self.text)
class MyApp(App):
def build(self):
return MyLabel(text="Hello")
MyApp().run()
But problem can be that on_touch_down and on_touch_up may block other events and then even standard Button will not work. You may have to check on_touch_down and on_touch_up only in widget which doesn't need on_press, on_release
Probably it could be resolved using only on_touch_move without on_touch_down, on_touch_up
EDIT:
Minimal working code.
It uses clickable Label and on_touch_move without on_touch_down, on_touch_up to slide screen.
BTW: I use if touch.dx < -3: instead of if touch.dx < 0: to skip very small moves. I tested it only with mouse but for findgers it may need different value.
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from functools import partial
from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition
from kivy.uix.behaviors import ButtonBehavior
class MyLabel(ButtonBehavior, Label):
def on_press(self):
print("pressed:", self.text)
def on_release(self):
print("release:", self.text)
class WordScreen(Screen):
def __init__(self, **kwargs):
label_text = kwargs['label_text']
del kwargs['label_text']
super(WordScreen, self).__init__(**kwargs)
main_layout = BoxLayout(id='test', orientation='vertical')
navigation_layout = BoxLayout(orientation='horizontal',
size_hint_y=.1)
navigation_layout.add_widget(MyLabel(text='home'))
navigation_layout.add_widget(MyLabel(text='audio'))
navigation_layout.add_widget(MyLabel(text='favorite'))
text_layout = BoxLayout(orientation='vertical')
#text_layout.add_widget(Label(id='sight_text', text=label_text))
text_layout.add_widget(Label(id='sight_text', text=self.name))
main_layout.add_widget(navigation_layout)
main_layout.add_widget(text_layout)
self.add_widget(main_layout)
def on_touch_move(self, touch):
#print('touch variable:\n', "\n".join(dir(touch)))
print('touch.dx:', touch.dx)
print('touch.dsx:', touch.dsx)
#print('touch.distance:', touch.distance)
#print('touch.move:', touch.move)
if touch.dx < -3: # using `touch.dx < 0` it slides even on very small moves
self.manager.transition = SlideTransition(direction="right")
self.manager.current = self.manager.next()
elif touch.dx > 3: # using `touch.dx > 0` it slides even on very small moves
self.manager.transition = SlideTransition(direction="right")
self.manager.current = self.manager.previous()
sm = ScreenManager()
sm.add_widget(WordScreen(name='menu', label_text="Example"))
sm.add_widget(WordScreen(name='settings', label_text="Example"))
class MyApp(App):
def build(self):
return sm
if __name__ == '__main__':
MyApp().run()
EDIT:
Problem with on_touch_down, on_touch_up which block on_press, on_release can be resolved using super() to execute original code on_touch_down, on_touch_up
def on_touch_down(self, touch):
# ... code ...
return super(WordScreen, self).on_touch_down(touch)
def on_touch_up(self, touch):
# ... code ...
return super(WordScreen, self).on_touch_up(touch)
Based on example in documentation: Events - Dispatching a Property event
I made a code where you can draw lines with a kivy canva and I wanted to add a Textinput. But the problem is that when I trie clicking on the Textinput, kivy wants to draw a line and it gives me an error :
" File "/myfolders/test.py", line 45, in on_touch_up
touch.ud['line'].points += [touch.x, touch.y]
KeyError: 'line' "
Here is my code :
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Ellipse, Line, Rectangle
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.uix.textinput import TextInput
from time import sleep
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scatter import Scatter
from kivy.uix.boxlayout import BoxLayout
from math import *
isLine=False
length=0
class MyBackground(Widget):
def __init__(self, **kwargs):
super(MyBackground, self).__init__(**kwargs)
with self.canvas:
self.bg = Rectangle(source='chouettes3.jpg', pos=self.pos, size=self.size)
self.bind(pos=self.update_bg)
self.bind(size=self.update_bg)
def update_bg(self, *args):
self.bg.pos = self.pos
self.bg.size = self.size
class MyPaintWidget(Widget):
def on_touch_down(self, touch):
#if self.collide_point(*touch.pos):
with self.canvas:
self.canvas.clear()
d=15
Rectangle(pos=(touch.x-d/2,touch.y-d/2),size=(d,d))
touch.ud['line'] = Line(points=(touch.x, touch.y), width=3)
def on_touch_up(self, touch):
#if self.collide_point(*touch.pos):
touch.ud['line'].points += [touch.x, touch.y]
points=touch.ud['line'].points
length=sqrt(((points[0]-points[2])**2)+((points[1]-points[3])**2))
print (length)
with self.canvas:
d=15
Rectangle(pos=(touch.x-d/2,touch.y-d/2),size=(d,d))
class MyPaintApp(App):
def build(self):
f = FloatLayout()
s = Scatter()
b = BoxLayout(orientation='vertical')
parent = MyBackground()
painter = MyPaintWidget(pos_hint={'x': 100, 'center_y': 100}, size_hint=(None, None))
textinput = TextInput(text='Hello world')
parent.add_widget(painter)
parent.add_widget(textinput)
f.add_widget(parent)
sleep(0.1)
return f
if __name__ == '__main__':
MyPaintApp().run()
I tried the self.collide_point technique, where something only works when you click on the widget in question but that just doesn't draw a line anymore.
Thanks in advance for your answers!
One problem is your use of the Widget class as a container for other Widgets. You should use a Layout class to contain other Widgets, otherwise, things like pos_hint have no effect. So I have modified your code to make MyBackground extend FloatLayout. Also, using collide_point() is a good idea, but usually, when you override a class method, you should also call the overridden method using super(). With that in mind, here is your modified code, that I think will work as you intended:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Color, Line, Rectangle
from kivy.uix.textinput import TextInput
from kivy.uix.floatlayout import FloatLayout
from math import *
isLine=False
length=0
class MyBackground(FloatLayout):
def __init__(self, **kwargs):
super(MyBackground, self).__init__(**kwargs)
with self.canvas:
self.bg = Rectangle(source='chouettes3.jpg', pos=self.pos, size=self.size)
self.bind(pos=self.update_bg)
self.bind(size=self.update_bg)
def update_bg(self, *args):
self.bg.pos = self.pos
self.bg.size = self.size
class MyPaintWidget(Widget):
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
with self.canvas:
self.canvas.clear()
d=15
Color(1, 0, 0, 1)
Rectangle(pos=(touch.x-d/2,touch.y-d/2),size=(d,d))
touch.ud['line'] = Line(points=(touch.x, touch.y), width=3)
return super(MyPaintWidget, self).on_touch_down(touch)
def on_touch_up(self, touch):
if 'line' not in touch.ud:
return super(MyPaintWidget, self).on_touch_up(touch)
if self.collide_point(*touch.pos):
touch.ud['line'].points += [touch.x, touch.y]
points=touch.ud['line'].points
length=sqrt(((points[0]-points[2])**2)+((points[1]-points[3])**2))
print (length)
with self.canvas:
d=15
Rectangle(pos=(touch.x-d/2,touch.y-d/2),size=(d,d))
return super(MyPaintWidget, self).on_touch_up(touch)
class MyPaintApp(App):
def build(self):
parent = MyBackground()
painter = MyPaintWidget(pos_hint={'x':0., 'center_y':0.5}, size_hint=(1, 1))
self.textinput = TextInput(text='Hello world', pos=(0,0), size_hint=(0.1,0.1))
parent.add_widget(painter)
parent.add_widget(self.textinput)
return parent
if __name__ == '__main__':
MyPaintApp().run()
I have also eliminated some unused code in the build() method.
As the code stands, clicking in the TextInput will not start a new line, but you can end a line behind the TextInput. If you don't want that to happen, you can add a collision test in the on_touch_up() method to handle it as shown below:
def on_touch_up(self, touch):
if 'line' not in touch.ud:
return super(MyPaintWidget, self).on_touch_up(touch)
ti = App.get_running_app().textinput
if self.collide_point(*touch.pos) and not ti.collide_point(*touch.pos):
touch.ud['line'].points += [touch.x, touch.y]
points=touch.ud['line'].points
length=sqrt(((points[0]-points[2])**2)+((points[1]-points[3])**2))
print (length)
with self.canvas:
d=15
Rectangle(pos=(touch.x-d/2,touch.y-d/2),size=(d,d))
else:
self.canvas.clear()
return super(MyPaintWidget, self).on_touch_up(touch)
I'm trying to make a screen that has two rows, one of those rows are the actual game screen and the another one is a button to clear the table above. But the problem here is that the first row is blank when I run the app. That's the code that I am stuck till now:
import kivy
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
kivy.require('1.9.2')
class VelhaGame(GridLayout, Screen):
def __init__(self, **kwargs):
super(VelhaGame, self).__init__(**kwargs)
self.cols = 3
self.rows = 3
self.font_size = 100
self.buttons = []
for i in range(9):
button = Button(text='', font_size=self.font_size)
button.bind(on_release=self.player_turn)
self.add_widget(button)
self.buttons.append(button)
self.player1 = True
def player_turn(self, button, *args):
if self.player1 and button.text == '':
self.player1 = False
button.text = 'X'
elif not self.player1 and button.text == '':
self.player1 = True
button.text = 'O'
class MainMenu(GridLayout, Screen):
def __init__(self, **kwargs):
super(MainMenu, self).__init__(**kwargs)
self.cols = 1
self.rows = 2
self.button_play = Button(text='PLAY', font_size=40)
self.button_play.bind(on_release=self.play)
self.add_widget(self.button_play)
self.button_exit = Button(text='EXIT', font_size=40)
self.button_exit.bind(on_release=self.exit)
self.add_widget(self.button_exit)
def play(self, *args):
screen_manager.current = 'velhaPage'
def exit(self, *args):
App.get_running_app().stop()
game_screen_manager = ScreenManager()
game_screen_manager.add_widget(VelhaGame(name='velhaGame'))
class VelhaScreen(GridLayout, Screen):
def __init__(self, **kwargs):
super(VelhaScreen, self).__init__(**kwargs)
self.rows = 2
self.add_widget(game_screen_manager)
self.clear_button = Button(text='Clear', font_size=40)
self.add_widget(self.clear_button)
screen_manager = ScreenManager()
screen_manager.add_widget(MainMenu(name='menu'))
screen_manager.add_widget(VelhaScreen(name='velhaPage'))
class VelhaGameApp(App):
def build(self):
return screen_manager
if __name__ == '__main__':
VelhaGameApp().run()
Do you know what I could do to accomplish this task?
The answer to you question, is "You cannot do that".
Screenmanager only shows one screen at a time.
you can read about that in the docs https://kivy.org/docs/api-kivy.uix.screenmanager.html
But to acheive something like you are trying to, you could do like this:
Instead of screens inside another screen, you could put the gridlayout, and button inside a vertical orientated boxlayout like this:
import kivy
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.properties import ListProperty
kivy.require('1.9.1')
class VelhaGame(Screen):
buttons = ListProperty([])
def __init__(self, **kwargs):
super(VelhaGame, self).__init__(**kwargs)
self.game_window = BoxLayout(orientation="vertical")
self.add_widget(self.game_window)
self.game_table = GridLayout(cols=3,rows=3)
for i in range(9):
button = Button(text='', font_size="100sp")
button.bind(on_release=self.player_turn)
self.game_table.add_widget(button)
self.buttons.append(button)
self.player1 = True
self.clear_button = Button(on_press=self.clear,text='Clear', font_size="40sp")
self.game_window.add_widget(self.game_table)
self.game_window.add_widget(self.clear_button)
def clear(self,*args):
for button in self.buttons:
button.text = ""
def player_turn(self, button, *args):
if self.player1 and button.text == '':
self.player1 = False
button.text = 'X'
elif not self.player1 and button.text == '':
self.player1 = True
button.text = 'O'
class MainMenu(GridLayout, Screen):
def __init__(self, **kwargs):
super(MainMenu, self).__init__(**kwargs)
self.cols = 1
self.rows = 2
self.button_play = Button(text='PLAY', font_size="40sp")
self.button_play.bind(on_release=self.play)
self.add_widget(self.button_play)
self.button_exit = Button(text='EXIT', font_size=40)
self.button_exit.bind(on_release=self.exit)
self.add_widget(self.button_exit)
def play(self, *args):
screen_manager.current = 'velhaPage'
def exit(self, *args):
App.get_running_app().stop()
screen_manager = ScreenManager()
screen_manager.add_widget(MainMenu(name='menu'))
screen_manager.add_widget(VelhaGame(name='velhaPage'))
class VelhaGameApp(App):
def build(self):
return screen_manager
if __name__ == '__main__':
VelhaGameApp().run()
I tried to make by own coockie-clicker, so I creaded an kivy widget and declared an image of an coockie as part of it.
Everytime you click on the wiget, a counter goes up and the number is displayed on an label.
Everything went fine, after I got help here on stack overflow, but now I am faced with the problem, that the widet is to big, so even if I click on the right upper corner, to counter goes up, aldoug I do not clicked on the coockie.
Here is the sourcecode:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.animation import Animation
from kivy.core.text.markup import *
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import NumericProperty
from kivy.properties import StringProperty
Builder.load_string('''
<Root>:
Kecks:
pos: 300, 300
size: 100, 100
<Kecks>:
Image:
pos: root.pos
id: my_image
source: root.weg
Label:
id: my_Label
font_size: 50
text: root.txt
center_x: 345
center_y: 200
''')
class Root(FloatLayout):
def __init__(self, *args, **kwargs):
super(Root, self).__init__(*args, **kwargs)
class Kecks(Widget):
count = NumericProperty(0)
amount = NumericProperty(1)
txt = StringProperty()
level = NumericProperty(1)
weg = StringProperty('piernik.png')
def __init__(self, *args, **kwargs):
super(Kecks, self).__init__(*args, **kwargs)
#self.txt = str(self.count)
Clock.schedule_interval(self.Update, 1/60.)
def Update(self, *args):
self.txt = str(self.count)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.count += self.amount
class app(App):
def build(self):
Window.clearcolor = (10, 0, 0, 1)
return Root()
if __name__ == "__main__":
app().run()
The problem is you haven't defined your collide_points on which area you want that event to be triggered.
Consider if you want your collide_points on your my_image to trigger on_touch_down event, you need to adjust like this:
def on_touch_down(self, touch):
# to check if touch.pos collides on my_image
if self.ids.my_image.collide_point(*touch.pos):
self.count += self.amount
Also perhaps consider using pos_hint and size_hint as these will help you with consistency with your app running in different devices (size of screen, for instance), rather than using absolute size and/or position.
Hope this helps.
I am trying to refactor the last code sample so that the Button is actually its own class with an on_release action. But my code is failing.
I want to not only refactor it (per my attempt below) but I also need to set the text of the Button to "Clear"
from random import random
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.graphics import Color, Ellipse, Line
class MyPaintWidget(Widget):
def on_touch_down(self, touch):
userdata = touch.ud
userdata['color'] = c = (random(), 1, 1)
with self.canvas:
Color(*c, mode='hsv')
d = 30
Ellipse(pos=(touch.x - d/2, touch.y - d/2), size=(d, d))
userdata['line'] = Line(points=(touch.x, touch.y))
def on_touch_move(self, touch):
touch.ud['line'].points += [touch.x, touch.y]
class ClearButton(Button):
def __init__(self, paint_widget):
self.paint_widget=paint_widget
def on_release(self, button):
self.paint_widget.canvas.clear()
class MyPaintApp(App):
def build(self):
parent = Widget()
painter = MyPaintWidget()
clearbtn = ClearButton(painter)
parent.add_widget(painter)
parent.add_widget(clearbtn)
return parent
if __name__ == '__main__':
MyPaintApp().run()
Without subclassing, you could just do:
class MyPaintWidget(Widget):
# ... put your previous methods here
def clear_canvas(self, *largs):
self.canvas.clear()
class MyPaintApp(App):
def build(self):
root = FloatLayout()
painter = MyPaintWidget()
cleanbtn.bind(on_release=self.painter.clear_canvas)
root.add_widget(painter)
root.add_widget(clearbtn)
return root
With subclassing, i would prefer to go with Kv langage:
from kivy.lang import Builder
Builder.load_string('''
<ClearButton>:
text: "Clear"
on_release: app.painter.canvas.clear()
''')
class ClearButton(Button):
pass
class MyPaintApp(App):
def build(self):
parent = Widget()
painter = MyPaintWidget()
clearbtn = ClearButton()
parent.add_widget(painter)
parent.add_widget(clearbtn)
return parent