I have creating multiple buttons via for loop in Python/Kivy. I can't figureout how to implement for each button on_press and on_release function, so each button (having it's defined color) will come back to its original color after releasing.
Below You will find part of code in python and kivy.
Python:
class PrButton(GridLayout):
def __init__(self, **kwargs):
super(PrButton, self).__init__(**kwargs)
self.build_grid()
def build_grid(self):
for i in hi_cat():
btn = ButtonDD(text=i[0].upper())
btn.background_color = i[1]
self.ids[i[0]] = btn
self.add_widget(btn)
kivy file:
<ButtonDD>
bold: True
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
KV = """
<MainWidget>:
size : root.size
"""
class MainWidget(BoxLayout):
def __init__(self,**kwargs):
super().__init__(**kwargs)
for i in range(5):
btn = Button(text = str(i))
btn.bind(on_release = lambda i=i:self.clicked(i))
self.add_widget(btn)
def clicked(self,text):
print(text)
class MainApp(MDApp):
def build(self):
Builder.load_string(KV)
return MainWidget()
MainApp().run()
It is achieved by changing lambda x: self.clicked() To lambda i=i:self.clicked()
I've been stuck with this kind of problem for some time now. But I got my answer recently. So I hope this helps you
update: After discussing with others, I decided that it is a bit silly question. I wanted to animate Bezier curve with changed width, but it has no width property. with Line Bezier, I can change width, but then can't animate.
I can't change witdh of Bezier curve like Line.
here is the code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import *
class MyLayout(Widget):
def __init__(self):
super(MyLayout, self).__init__()
with self.canvas:
self.L=Bezier(points=[200,450,500,300,600,150],width=12)
self.k=Line (bezier=[100,350,400,200,500,50 ],width=12)
class MyApp(App):
def __init__(self):
super(MyApp, self).__init__()
def build(self):
return MyLayout()
if __name__=="__main__":
MyApp().run()
and outupt:
the problem is,
the upper curve isn't width=12.
I think that's because Kivy's Bezier Class has no attribute width cuz when I do it on kv lang, it gives me AttributeError: 'kivy.graphics.vertex_instructions.Bezier' object has no attribute 'width'. Well, why not using Line with bezier ? I want to use Animation on it and when I try to on Line with bezier, I get AttributeError: attribute 'bezier' of 'kivy.graphics.vertex_instructions.Line' objects is not readable.
so the question,
how can I change width of Bezier. if it is not possible, is there any way like finding the y of cruve (or ys) for given x so I can put Ellipse on these points and resize them to simulate width ?
thanks and pardon my english ♥
The line can use bezier:, example:
Line:
bezier:[n1,n2,n3,n4]
width:3
This is a Line, not a Bezier, but you get the same result...
One example:
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.boxlayout import BoxLayout
posicao=[]
class Draw(BoxLayout):
def __init__(self, **kwargs):
super(Draw, self).__init__(**kwargs)
def on_touch_down(self,touch):
posicao.append(touch.pos)
def on_touch_up(self,touch):
posicao.clear()
def on_touch_move(self,touch):
Line = Builder.load_string(
"""
FloatLayout:
canvas:
Color:
rgba:11,.1,1,1
Line:
points: {pos}
width:14
""".format(pos=(touch.pos, posicao[0])))
self.add_widget(Line)
posicao.clear()
posicao.append(touch.pos)
class Code(App):
def build(self):
return Draw()
if __name__ == '__main__':
Code().run()
Or:
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import *
posi = []
Builder.load_string(
"""
<Draw>:
""")
class Draw(BoxLayout):
def __init__(self, **kwargs):
super(Draw, self).__init__(**kwargs)
def on_touch_down(self,touch):
posi.clear()
posi.append(touch.pos)
def on_touch_up(self,touch):
posi.clear()
def on_touch_move(self,touch):
with self.canvas:
Line(points=[posi[0], touch.pos],width=14)
posi.clear()
posi.append(touch.pos)
class Code(App):
def build(self):
return Draw()
if __name__ == '__main__':
Code().run()
The Bezier has no width property, but the Line does. So you can animate that width. An easy way to do that is by animating a NumericProperty that holds the width. Here is a modified version of your code that does that:
from kivy.animation import Animation
from kivy.app import App
from kivy.clock import Clock
from kivy.properties import NumericProperty
from kivy.uix.widget import Widget
from kivy.graphics import *
class MyLayout(Widget):
line_width = NumericProperty(12)
def __init__(self):
super(MyLayout, self).__init__()
with self.canvas:
self.L=Bezier(points=[200,450,500,300,600,150],width=self.line_width)
self.k=Line (bezier=[100,350,400,200,500,50 ],width=self.line_width)
def on_line_width(self, instance, new_width):
self.k.width = new_width
class MyApp(App):
def build(self):
Clock.schedule_once(self.anim)
return MyLayout()
def anim(self, dt):
a = Animation(line_width=3)
a.start(self.root)
if __name__=="__main__":
MyApp().run()
If you build the Line in kv, then you don't even need the on_line_width() method since kivy will do the binding for you.
I have a very simplistic case of a window with a single button. With a button release I want to pop up a modal view with some text on it. In every button release I create and open an instance of ModalView and it works:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.modalview import ModalView
from kivy.uix.label import Label
class AButton(Button):
def on_release(self, *largs):
view = ModalView(size_hint=(None, None), size=[200, 200])
view.add_widget(Label(text='I am a modal view'))
view.open()
class MyApp(App):
def build(self):
return AButton()
if __name__ == '__main__':
MyApp().run()
Now let's say I want to create a subclass of ModalView so I don't have to specify size_hint and size every time I pop up a modal view. This is the code after that change:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.modalview import ModalView
from kivy.uix.label import Label
from kivy.properties import ListProperty
class AButton(Button):
def on_release(self, *largs):
view = ModalView2()
view.add_widget(Label(text='I am a modal view'))
view.open()
class ModalView2(ModalView):
size_hint = ListProperty([None, None])
size = ListProperty([200, 200])
def __init__(self, **kwargs):
super(ModalView2, self).__init__(**kwargs)
class MyApp(App):
def build(self):
return AButton()
if __name__ == '__main__':
MyApp().run()
ModalView and Label position get totally messed up. I tried with anchor_x and anchor_y in ModalView2 in an attempt to fix the label position with no luck. What am I doing wrong?
The ModalView already has size_hint and size attributes, so you don't need to create new ones, just set the existing attributes to the values you want:
class ModalView2(ModalView):
def __init__(self, **kwargs):
super(ModalView2, self).__init__(**kwargs)
self.size_hint = (None, None)
self.size = (200, 200)
I have a question about kivy. Is it possible to create screens as variables? Lets say I want to create a new screen with a button inside the kivy application. Is it possible to do that? If so how could you do it??
You just have to create a Screen object that you want to create and add it to the ScreenManager:
screen = Your_Screen(name="some_name")
your_screen_manager.add_widget(screen)
Example:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class TestScreen(Screen):
def __init__(self, **kwargs):
Screen.__init__(self, **kwargs)
layout = BoxLayout(orientation="vertical")
self.add_widget(layout)
layout.add_widget(Label(text=self.name))
button = Button(text="{}: Add Screen".format(self.name))
layout.add_widget(button)
button.bind(on_press=self.add_screen)
def add_screen(self, *args):
n = len(self.manager.screen_names)
screen = TestScreen(name="screen {}".format(n))
self.manager.add_widget(screen)
self.manager.current = screen.name
# Create the screen manager
sm = ScreenManager()
sm.add_widget(TestScreen(name='screen 0'))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
Here is a basic sample that I am working with. The label displays as I would expect, but the text never changes, even though I do see the print statements in the console showing that the Clock.schedule_interval is ticking away. Any thoughts as to what have gone wrong???
Thank you and Happy New Year!
First the .kvlang file
<Demo>:
button_text: my_button
BoxLayout:
Label:
id: my_button
text: 'Initial Text!'
And my Python.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty, StringProperty
from kivy.clock import Clock
import random
class Demo(BoxLayout):
button_text = ObjectProperty
def change_text(self, dt):
self.button_text.text = str(random.randint(1, 10))
print('Should have changed button text to {}'.format(self.button_text.text))
def start(self):
Clock.schedule_interval(self.change_text, 10)
class TutorialApp(App):
def build(self):
foo = Demo()
foo.start()
return Demo()
if __name__ == "__main__":
TutorialApp().run()
You are missing parenthesis
button_text = ObjectProperty
change to
button_text = ObjectProperty(None) # Ha! :)
Also you should return foo and not create another Demo
def build(self):
foo = Demo()
foo.start()
#return Demo() change to...
return foo
Since the later Demo will not be updated...