I'm using Kivy for the first time and I'm getting a little bit confused between the different classes required for the widget and how to access their variables. I have a main module from where I am launching another module that contains Kivy. I am then trying to retrieve a list of points from the on_touch methods.
Main module:
if __name__ == '__main__':
global graphInput
graphInput=graphInputKivy.GraphInputKivyApp()
graphInput.run()
graphInput.graphListOfXY = graphInput.canvasDrawing.pointsXY
print(graphInput.graphListOfXY)
'Kivy' module:
class CanvasDrawing(Widget):
pointsXY=[]
def on_touch_down(self, touch):
with self.canvas:
Color(1, 1, 0)
touch.ud['line'] = Line(points=(touch.x, touch.y))
self.pointsXY=touch.ud['line'].points
def on_touch_move(self, touch):
touch.ud['line'].points += [touch.x, touch.y]
self.pointsXY+= [touch.x, touch.y]
class GraphInputKivyApp(App):
graphListOfXY=[]
def build(self):
layout = Widget()
self.canvasDrawing=CanvasDrawing()
clearCanvasButton = Button(text='Clear')
clearCanvasButton.bind(on_release=self.clear_canvas)
layout.add_widget(self.canvasDrawing)
layout.add_widget(clearCanvasButton)
return layout
def clear_canvas(self, obj):
self.canvasDrawing.canvas.clear()
if __name__ == '__main__':
GraphInputKivyApp().run()
I'm able to access the list of points from the on_touch_down method (when I close the Kivy window) with graphInput.canvasDrawing.pointsXY but how can I update graphInput.graphListOfXY after the on_touch method is called?
Thanks,
py - Kivy module
Remove class attribute, pointsXY=[]
Access app object using App.get_running_app()
Replace all references of pointsXY=[] with App.get_running_app().graphListOfXY
graphInputKivy.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.graphics import Color, Line
class CanvasDrawing(Widget):
def on_touch_down(self, touch):
with self.canvas:
Color(1, 1, 0)
touch.ud['line'] = Line(points=(touch.x, touch.y))
App.get_running_app().graphListOfXY.append([touch.x, touch.y])
def on_touch_move(self, touch):
touch.ud['line'].points += [touch.x, touch.y]
App.get_running_app().graphListOfXY.append([touch.x, touch.y])
class GraphInputKivyApp(App):
graphListOfXY = []
def build(self):
layout = Widget()
self.canvasDrawing = CanvasDrawing()
clearCanvasButton = Button(text='Clear')
clearCanvasButton.bind(on_release=self.clear_canvas)
layout.add_widget(self.canvasDrawing)
layout.add_widget(clearCanvasButton)
return layout
def clear_canvas(self, obj):
self.canvasDrawing.canvas.clear()
def on_stop(self):
print(f"\GraphInputKivyApp.non_stop: self.graphListOfXY")
print(self.graphListOfXY)
if __name__ == '__main__':
GraphInputKivyApp().run()
py - Main module
Remove graphInput.graphListOfXY = graphInput.canvasDrawing.pointsXY
main.py
import graphInputKivy
if __name__ == '__main__':
global graphInput
graphInput = graphInputKivy.GraphInputKivyApp()
graphInput.run()
print(f"\nmain: graphInput.graphListOfXY")
print(graphInput.graphListOfXY)
Related
Please help, I'm trying to make an application for a child to learn the alphabet, I'm just learning programming and working with classes.
I need to pass to the SecondScreen class the text that the button_press function returns from the ScreenMain class.
`
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from itertools import chain
from kivy.uix.screenmanager import ScreenManager, Screen
class ScreenMain(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.alphabet = [chr(i) for i in chain(range(1040, 1046), range(1025, 1026), range(1046, 1069), range(32, 33),
range(1069, 1072))]
gr = GridLayout(cols=5, padding=[35], spacing=3)
for i in self.alphabet:
gr.add_widget(Button(text=i, on_press=self.button_press))
self.add_widget(gr)
def button_press(self, instance):
self.manager.transition.direction = 'left'
self.manager.current = 'second_screen'
print(instance.text) # I output to the console, everything is ok
return instance.text
class SecondScreen(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
get = ScreenMain.button_press # I'm trying to pass a letter from a function to a class button_press
layout = GridLayout(cols=5, rows=5, padding=[35], spacing=10)
layout.add_widget(Button(
text=str(get))) # Trying to create a button on the second page with the text of the letter pressed on the first screen
self.add_widget(layout)
def _on_press_button_new_layout(self, *args):
self.manager.transition.direction = 'right'
self.manager.current = 'main_screen'
class MyApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(ScreenMain(name='main_screen'))
sm.add_widget(SecondScreen(name='second_screen'))
return sm
if __name__ == '__main__':
MyApp().run()
`
While I run this script rippleexample2.py with rippleexample2.kv, the buttons should have ripple effects upon pressing, but it doesn't work.
I know the RippleButton class is working fine in ctmbtn.py, when a button is pressed here, there is ripple effect. I don't know what is wrong here. Perhaps binding function?
rippleexample2.py
from kivy.app import App
from kivy.uix.touchripple import TouchRippleBehavior
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import (StringProperty, NumericProperty, ObjectProperty, ListProperty, DictProperty, BooleanProperty)
class RippleButton(TouchRippleBehavior, Button):
isRippled = BooleanProperty(False)
def __init__(self, **kwargs):
super(RippleButton, self).__init__(**kwargs)
def on_touch_down(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point and not self.isRippled:
self.isRippled = True
self.ripple_show(touch)
return super(RippleButton, self).on_touch_down(touch)
def on_touch_up(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point and self.isRippled:
self.isRippled = False
self.ripple_fade()
return super(RippleButton, self).on_touch_up(touch)
class Login(Screen):
pass
class MainScreen(Screen):
pass
class ScreenManager(ScreenManager):
pass
Login = Builder.load_file("rippleexample2.kv")
class SimpleKivy4(App):
def build(self):
return Login
if __name__ == "__main__":
SimpleKivy4().run()
rippleexample2.kv
ScreenManager:
Login:
MainScreen:
<Login>:
name:"login"
RippleButton:
text:'Login'
font_size: 24
size_hint: (.4,.25)
on_release: app.root.current = "main"
<MainScreen>:
name: "main"
Button:
text: 'back'
on_release: app.root.current = "login"
ctmbtn.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import (StringProperty, NumericProperty, ObjectProperty,
ListProperty, DictProperty, BooleanProperty)
from kivy.uix.touchripple import TouchRippleBehavior
class RippleButton(TouchRippleBehavior, Button):
isRippled = BooleanProperty(False)
def __init__(self, **kwargs):
super(RippleButton, self).__init__(**kwargs)
def on_touch_down(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point and not self.isRippled:
self.isRippled = True
self.ripple_show(touch)
return super(RippleButton, self).on_touch_down(touch)
def on_touch_up(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point and self.isRippled:
self.isRippled = False
self.ripple_fade()
return super(RippleButton, self).on_touch_up(touch)
class RootWidget(BoxLayout):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
self.add_widget(RippleButton(text='btn 1'))
cb = CustomBtn()
cb.bind(pressed=self.btn_pressed)
self.add_widget(cb)
self.add_widget(RippleButton(text='btn 2'))
def btn_pressed(self, instance, pos):
print ('pos: printed from root widget: {pos}'.format(pos=pos))
class CustomBtn(Widget):
pressed = ListProperty([0, 0])
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.pressed = touch.pos
# we consumed the touch. return False here to propagate
# the touch further to the children.
return True
return super(CustomBtn, self).on_touch_down(touch)
def on_pressed(self, instance, pos):
print ('pressed at {pos}'.format(pos=pos))
class TestApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
TestApp().run()
The Touch Ripple code is still experimental, and it is not visible when using it in kv language for Button widget.
Actually, the Button touch ripple animation on interaction works when using kv language. It is just not visible because the background_color (r, g, b, a) of Button widget defaults to [1, 1, 1, 1] or grey color with no transparency. Whereby 'a' (alpha compositing or transparency) is 1 i.e. not transparent.
The ripple_color defaults to [1., 1., 1., .5] or grey color with half transparency.
Solution
The temporary work around is to change the alpha compositing or transparency of Button widget to 0.5.
Please refer to my example for Kivy RippleButton
I'm not really sure as to why the canvas isn't clearing.
The first build(self) implementation that has the parent variable is the one that works. The only thing I see different is that the second implementation is adding the Button widget to the MyPaintWidget instead of both of those widgets getting added to a default Widget class.
Very new to kivy i'm semi-familiar with python. I'd love an explanation.
from random import random
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.gridlayout import GridLayout
from kivy.graphics import Color, Ellipse, Line
'''
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols=2
self.add_widget(Label(text='User Name'))
self.username=TextInput(multiline=False)a
self.add_widget(self.username)
self.add_widget(Label(text='password'))
self.password=TextInput(password=True, multiline=False)
self.add_widget(self.password)
class MainApp(App):
def build(self):
return LoginScreen()
'''
class MyPaintWidget(Widget):
def on_touch_down(self, touch):
color = (random(), 1, 1)
with self.canvas:
Color(*color)
d = 30.
Ellipse(pos=(touch.x - d / 2, touch.y - d / 2), size=(d, d))
touch.ud['line'] = Line(points=(touch.x, touch.y))
print(touch)
def on_touch_move(self, touch):
touch.ud['line'].points += [touch.x, touch.y]
class MyPaintApp(App):
#WHY ARE THESE TWO IMPLEMENTATIONS OF BUILD SO DIFFERENT?????
'''
def build(self):
parent = Widget()
self.painter = MyPaintWidget()
clearbtn = Button(text='Clear')
clearbtn.bind(on_release=self.clear_canvas)
parent.add_widget(self.painter)
parent.add_widget(clearbtn)
return parent
'''
def build(self):
self.painter = MyPaintWidget()
clearbtn = Button(text='Clear')
clearbtn.bind(on_release=self.clear_canvas)
self.painter.add_widget(clearbtn)
return self.painter
def clear_canvas(self, obj):
self.painter.canvas.clear()
if __name__ == '__main__':
MyPaintApp().run()
Touches are dispatched down the widget tree, they enter at the root widget which must pass the touch down to its children (or fail to do so, if it wants).
Your MyPaintWidget class overrides on_touch_down but fails to pass the touch to its children, so the Button never receives the touch and never gets a chance to become pressed.
Add return super(MyPaintWidget, self).on_touch_down(touch) to the MyPaintWidget.on_touch_down to call the parent class method that automatically handles this for you.
I'm testing Kivy v1.10.0, and don't understand why the location where I set a Kivy property makes a difference.
This code works:
from kivy.app import App
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
class CustomBtn(Widget):
pressed = ListProperty([0, 0])
def __init__(self, **kwargs):
super(CustomBtn, self).__init__(**kwargs)
# self.pressed = ListProperty([0, 0])
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.pressed = touch.pos
return True
return super(CustomBtn, self).on_touch_down(touch)
class RootWidget(BoxLayout):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
cb = CustomBtn()
self.add_widget(cb)
cb.bind(pressed=self.btn_pressed)
def btn_pressed(self, instance, pos):
print(pos)
class MyApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
MyApp().run()
However, if I replace the line currently at class level:
pressed = ListProperty([0, 0])
by the equivalent in CustomBtn.__init__():
self.pressed = ListProperty([0, 0])
I get an error in instruction cb.bind(pressed=self.btn_pressed):
File "kivy\_event.pyx", line 438, in kivy._event.EventDispatcher.bind (kivy\_event.c:6500)
KeyError: 'pressed'
I believe declaring (assigning) an attribute at class level out of any method and doing the same in __init__() were equivalent. Kivy properties are not Python attributes, and maybe the sequence in which objects are built is different and makes a difference for Kivy?
I believe declaring (assigning) an attribute at class level out of any
method and doing the same in __init__() were equivalent.
Nope. Kivy's properties - are descriptors (way it works). Descriptor object should be stored in class to work. It's Python thing - nothing Kivy specific.
How can I change line color in the kivy Paint app that I have made. I am able to change width of line, but I couldn't find anything for changing color of line.
My code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Line
class DrawRandom(Widget):
def on_touch_down(self, touch):
with self.canvas:
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 PaintApp(App):
def build(self):
return DrawRandom()
if __name__ == "__main__":
PaintApp().run()
You simply add Color to your canvas.
In your imports import Color too.
from kivy.graphics import Line, Color
And in your Painter class add Color to canvas. In this example I try red.
Its rgba values.
def on_touch_down(self, touch):
with self.canvas:
Color(1,0,0,1)
touch.ud["line"] = Line( points = (touch.x, touch.y))