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.
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()
`
I am trying update Label on canvas , but it changed only if I call update() method from MyApp(APP).
And what is interesting that method obj Slider (on touch) works perfectly
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import (Color, Ellipse)
from kivy.uix.slider import Slider
import random
from functools import partial
class Canvas_w(Widget):
def __init__(self, **kwargs):
self.i = 0
super(Canvas_w, self).__init__(**kwargs)
self.label_text =Label(text= "0",color = (255,0,0))
def update_text(self, text):
self.label_text.text = text
def on_touch_down(self, touch):
with self.canvas:
d=30
self.i+=1
Color(1., 0, 0)
Ellipse(pos=(touch.x - d/2, touch.y - d/2), size=(20,20))
self.label_text=str(self.i)
class Label_l(GridLayout):
bones = 0
def __init__(self, **kwargs):
super(Label_l, self).__init__(**kwargs)
self.label_text =Label(text= "0")
self.add_widget(self.label_text)
#self.label_text.text=self.C.i
def update_text(self, text):
Label_l.bones=text
self.label_text.text = str(Label_l().bones)#
Label_l.text=str(Label_l().bones)
class WidgetContainer(GridLayout):
sld_value = 0.00001
def __init__(self, **kwargs):
super(WidgetContainer, self).__init__(**kwargs)
self.cols = 1
self.speedControl = Slider(min=0.00001, max=3, step=0.0001)
self.add_widget(Label(text="Speed Iteration"))
self.add_widget(self.speedControl)
self.add_widget(Label(text=''))
self.speedValue = Label(text="0.00001")
self.add_widget(self.speedValue)
self.speedControl.bind(value=self.on_value)
def on_value(self, instance, speed):
self.sld_value = speed
WidgetContainer.sld_value = self.sld_value
self.speedValue.text = str(round(speed,5))
class MyApp(App):
def build(self):
b3 = BoxLayout(orientation="horizontal")
b1 = FloatLayout()
self.painter = Canvas_w()
self.label_l= Label_l()
b1.add_widget(self.label_l)
b1.add_widget(self.painter)
b1.add_widget(WidgetContainer())
b3.add_widget(b1)
return(b3)
if __name__=="__main__":
MyApp().run()
I am trying update Label on canvas , but it changed only if I call update() method from MyApp(APP).
And what is interesting that method obj Slider (on touch) works perfectly
A few problems with your code:
The color attribute of a Label requires a list of four values with each value in the range 0 to 1. Change self.label_text =Label(text= "0",color = (255,0,0)) to self.label_text =Label(text= "0",color = (1,0,0,1)) in the Canvas_w class.
The above Label is never getting drawn. You need to add self.add_widget(self.label_text) to the __init__() method of Canvas_w.
You have another Label in the Label_l class that is being drawn on top of the Label in the Canvas_w.
After fixing the above problems, I believe your code will work as you expect.
If you call self.canvas.clear(), it appears as though the label_text child of Canvas_w is removed. Actually it is not removed, but is just not visible (perhaps overdrawn). One way to make it visible is to just make sure it is drawn last by removing it and then re-adding it (last added child is drawn last), like this:
self.canvas.clear()
self.remove_widget(self.label_text)
self.add_widget(self.label_text)
After a bit more research, I believe that the canvas.clear() is removing the drawing of the Label from the Canvas. You can, however, do your drawing on canvas.before by using:
with self.canvas.before:
in the on_touch_down() method. And rather than using:
self.canvas.clear()
use:
self.canvas.before.clear()
I believe that your Label will be displayed correctly after doing the above.
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)
I want to place image on button. Button's size depends on size of screen. I want to select ideal image's size according to button's size. Buttons are defined in class ButtonTools. With my knowledges I cann obtain size only in fuction of class ButtonTools. How do I obtain size of screen?
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.image import Image
class Paint(Widget):
pass
class ButtonTools(BoxLayout):
def __init__(self, **kwargs):
super(ButtonTools, self).__init__(**kwargs)
self.size_hint = (1, None)
self.height = 50
but = Button(on_press = self.DrawAbscissa)
but.background_normal = 'abscissa.png'
self.add_widget(but)
but = Button( on_press = self.DrawCurve)
but.background_normal ='curve.png'
self.add_widget(but)
def DrawAbscissa(self, obj):
size = self.parent.size
p=1
pass
def DrawCurve(self, obj):
pass
class WorkShop(BoxLayout):
def __init__(self, **kwargs):
super(WorkShop, self).__init__(**kwargs)
self.orientation = "vertical"
self.paint = Paint()
self.tools = ButtonTools()
self.add_widget(self.paint)
self.add_widget(self.tools)
class MyPaintApp(App):
def build(self):
return WorkShop()
if __name__ == '__main__':
MyPaintApp().run()
You can obtain the screen size using the following:
from kivy.core.window import Window
...
print("Window.size={}".format(Window.size))
print("Window.height={}".format(Window.height))
print("Window.width={}".format(Window.width))
I am using kivy as my GUI tools for my python program.
when I want to create a table,in which there is a column containing dropdown list to make select value easier.
However, I can not make it works correctly.
The following is my code.
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
from kivy.uix.gridlayout import GridLayout
sel =["A","B","C"]
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.redraw()
def redraw(self):
self.clear_widgets()
self.rows = 5
self.cols =2
for i in range(5):
label = Label(text="cell"+str(i+1))
self.add_widget(label)
drpName = DropDown()
btnName = Button(text="B",size_hint=(None, None))
for e in sel:
btn=Button(text=e, size_hint_y=None, height=btnName.height)
btn.bind(on_release=lambda btn:drpName.select(btn.text))
drpName.add_widget(btn)
btnName.bind(on_release=drpName.open)
drpName.bind(on_select=lambda instance, x: setattr(btnName, 'text', x))
self.add_widget(btnName)
class testApp(App):
def build(self):
return MyGrid()
if __name__=="__main__":
testApp().run()
Only a part of button open the pull down list and all of the selected value will replace the text of last button.
Could you help me out. Thanks in advance.
After reading post Python Lambda in a loop[Building Dropdowns Dynamically in Kivy, I can make my program works.
Thanks for valuable post.
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
from kivy.uix.gridlayout import GridLayout
sel =["A","B","C"]
class MyGrid(GridLayout):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.redraw()
def redraw(self):
self.clear_widgets()
self.rows = 5
self.cols =2
drpName = []
for i in range(5):
label = Label(text="cell"+str(i+1))
self.add_widget(label)
drpName.append(DropDown())
btnName=Button(text="B",size_hint=(None, None))
for e in sel:
btn=Button(text=e, size_hint_y=None, height=btnName.height)
btn.bind(on_release=lambda btn=btn,dropdown=drpName[i]:dropdown.select(btn.text))
drpName[i].add_widget(btn)
btnName.bind(on_release=drpName[i].open)
drpName[i].bind(on_select=lambda instance, x,btn=btnName: setattr(btn, 'text', x))
self.add_widget(btnName)
class testApp(App):
def build(self):
return MyGrid()
if __name__=="__main__":
testApp().run()