I want to dispatch events from a LayoutDemoApp to a child widget. The goal is to have a websocket listener dispatch events, allowing any widget to listen to them (e.g. updating labels and so forth). I am probably misunderstanding how events works here and the Kivy examples. Is the register_event_type simply local to the LayoutDemoApp object, and this not possible?
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class MyButton(Button):
def __init__(self, **kwargs):
super(MyButton, self).__init__(**kwargs)
self.bind(on_foo=self.on_foo)
def on_foo(self, *args, **kwargs):
print("MyButton.on_foo")
class LayoutDemoApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.register_event_type("on_foo")
def build(self):
root = BoxLayout(orientation='vertical')
r1 = BoxLayout()
button12 = MyButton(text='B1')
button12.bind(on_press=self.on_bar)
r1.add_widget(button12)
root.add_widget(r1)
return root
def on_bar(self, *args, **kwargs):
print("LayoutDemoApp.on_bar")
self.dispatch("on_foo")
def on_foo(self, *args, **kwargs):
pass
if __name__ == '__main__':
LayoutDemoApp().run()
Calling dispatch_children method on the root layout rather than the App itself and registering the event within the target widget as suggested by #ApuCoder should give the desired result.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class MyButton(Button):
def __init__(self, **kwargs):
super(MyButton, self).__init__(**kwargs)
self.register_event_type("on_foo")
def on_foo(self, *args, **kwargs):
(caller,) = args
print(f"{self.text}'s on_foo called by {caller}")
class LayoutDemoApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self):
root = BoxLayout(orientation="vertical")
r1 = BoxLayout()
button1 = MyButton(text="B1")
button1.bind(on_press=self.on_bar)
button2 = MyButton(text="B2")
button2.bind(on_press=self.on_bar)
r1.add_widget(button1)
r1.add_widget(button2)
root.add_widget(r1)
return root
def on_bar(self, instance):
print("LayoutDemoApp.on_bar")
self.root.dispatch_children("on_foo", instance.text)
if __name__ == "__main__":
a = LayoutDemoApp().run()
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 am writing an app with two screens using ScreenManager. In one screen I have a text input, and a button that reads such text input. If a certain condition from the input is satisfied, the second screen is activated. From this screen, I want to be able to grab the content of the text input from the first screen.
I have made multiple attempts and looked at many similar questions (this one for example), but none of them really seemed to work.
Below is a minimal non-working version of my code:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class RootWindow(Screen):
def __init__(self, **kwargs):
super(RootWindow, self).__init__(**kwargs)
box = BoxLayout()
self.add_widget(box)
self.searchInput = TextInput(multiline=False)
box.add_widget(self.searchInput)
self.searchButton = Button(text="Search")
self.searchButton.bind(on_press=self.searchRecipe)
box.add_widget(self.searchButton)
def searchRecipe(self, instance):
src = self.searchInput.text
if not src == "Go":
pass
else:
WMan.current = 'result'
class ResultWindow(Screen):
def __init__(self, **kwargs):
super(ResultWindow, self).__init__(**kwargs)
titleBox = BoxLayout()
self.add_widget(titleBox)
print(src)
WMan = ScreenManager()
WMan.add_widget(RootWindow(name='root'))
WMan.add_widget(ResultWindow(name='result'))
class RecipApp(App):
def build(self):
return WMan
if __name__ == "__main__":
RecipApp().run()
If you save a reference to titlebox like this:
class ResultWindow(Screen):
def __init__(self, **kwargs):
super(ResultWindow, self).__init__(**kwargs)
self.titleBox = BoxLayout()
self.add_widget(self.titleBox)
Then you can access the titlebox via the WMan, like this:
def searchRecipe(self, instance):
src = self.searchInput.text
if not src == "Go":
pass
else:
WMan.current = 'result'
WMan.current_screen.titlebox.add_widget(Label(text=src))
I am new to Kivy.
When I run the below kivy script on Sublime Text, I get only a Blank Black screen.
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
kivy.require("1.10.1")
class LoginScreen(GridLayout):
def __init_(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.add_widget(Label(text="Username :"))
self.username = TextInput(multiline=False)
self.add_widget(self.username)
class SimpleKivy(App):
def build(self):
return LoginScreen()
if __name__ == "__main__":
SimpleKivy().run()
The __init__() method of LoginScreen is spelled incorrectly. It should be:
def __init__(self, **kwargs):
not:
def __init_(self, **kwargs):
Note the number of underscores.
I want to be able to access the variable values assigned within other classes. This may be only related to the methods in Kivy but it is in general related to my confusion over the class communication. My feeling is I need to send the instances down the tree such as in this example but I can't seem to get it working.
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.uix.textinput import TextInput
class LevelTwo(BoxLayout):
def __init__(self, **kwargs):
super(LevelTwo, self).__init__(**kwargs)
self.wid = TextInput(id='var_id',text='string I want')
self.layout = BoxLayout(id='box_layout_two')
self.layout.add_widget(self.wid)
class LevelOne(BoxLayout):
def __init__(self, **kwargs):
super(LevelOne, self).__init__(**kwargs)
self.layout_outer = LevelTwo(id='box_layout_one')
class RootWidget(BoxLayout):
def __init__(self):
super(RootWidget, self).__init__()
self.widone = LevelOne()
print(self.widone.box_layout_one.box_layout_two.var_id.text)
--> stdout: '> string I want'
class MainApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
MainApp().run()