Kivy - Dispatching custom events to child widgets - python

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()

Related

Kivy, ScreenManager: how to access a variable defined in another screen?

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))

Kivy Script Does not show Labels in Sublime Text

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.

__init__ arguments in Python/Kivy

I'm trying to call an init method, and it's parents in a function. I'm probably doing something obviously dumb, but I can't figure it out. I get an error when I have this code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty, ListProperty
from kivy.clock import Clock
from kivy.graphics import Color
class FieldCommand(Widget):
def __init__(self, **kwargs):
super(FieldCommand, self).__init__(**kwargs)
self.myBoard = Board(col=2, rows=2)
for c in range(4):
self.add_widget(Button(text = c))
class Board(Widget):
def __init__(self, **kwargs):
super(Board, self).__init__(**kwargs)
class FieldCommandApp(App):
def build(self):
return FieldCommand()
if __name__ == '__main__':
FieldCommandApp().run()
Specifically, my error reads:
File "/Users/.../PycharmProjects/FieldCommand/venv/lib/python3.7/site-packages/kivy/uix/widget.py", line 350, in __init__
super(Widget, self).__init__(**kwargs)
File "kivy/_event.pyx", line 243, in kivy._event.EventDispatcher.__init__
TypeError: object.__init__() takes exactly one argument (the instance to initialize
)
I was following the example here:
class Container(BoxLayout):
def __init__(self, **kwargs):
super(Container, self).__init__(**kwargs)
self.previous_text = open(self.kv_file).read()
parser = Parser(content=self.previous_text)
widget = Factory.get(parser.root.name)()
Builder._apply_rule(widget, parser.root, parser.root)
self.add_widget(widget)
Why is my application getting an error?
Your call to self.myBoard = Board(col=2, rows=2) includes kwargs for col and rows that are passed to the Widget __init__(), which does not accept those kwargs. So the fix is to extract those kwargs in your Board class. You can do that by simply adding Properties in your Board class like this:
class Board(Widget):
# default values for `row` and `cols`
col = NumericProperty(1)
rows = NumericProperty(1)
def __init__(self, **kwargs):
super(Board, self).__init__(**kwargs)

accessing variables within nested classes

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()

Bind function to Kivy button

I'm trying to bind the following function to a Button in Kivy.
def auth(self):
print(self.username)
if self.username == "Hendricko":
print("self.username == Hendricko")
popup = Popup(title="success",
content=Label(text="Howdy !"),
size=(100, 100),
size_hint=(0.3, 0.3),
auto_dismiss=False)
popup.open()
I've tried
class Foo():
def initUI(self):
self.add_widget(Button(text="Auth User and Password", on_press=self.auth))
but this doesn't work. What am I doing wrong?
here is my whole code
from kivy.uix.popup import Popup
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.stacklayout import StackLayout
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.row = 2
self.add_widget(Label(text='User Name'))
self.username = TextInput(multiline=False)
self.add_widget(self.username)
self.add_widget(Label(text='password'))
self.password = TextInput(password=True, multiline=False)
self.add_widget(self.password)
self.hello = Button(text="hello", on_press=self.auth)
self.add_widget(self.hello)
def auth(self):
if self.username == "Hendricko":
popup = Popup(title="success",
content=Label(text="Howdy !"),
size=(100, 100),
size_hint=(0.3, 0.3),
auto_dismiss=False)
popup.open()
class MyApp(App):
def build(self):
return LoginScreen()
if __name__ == '__main__':
MyApp().run()
I don't think any of the answers are very clear. Neither explains that problem is that the callback given to on_press gets called with a parameter, the instance of button, so LoginScreen.auth must accept a parameter after the self:
def auth(self, button):
print('button pressed:', instance)
The problem is not that on_press must be given via Button.bind or that the callback must be a function, it can be a bound method, and the docs cited by other answer and by comments link to ButtonbBhavior which indicates that OP use of on_press in constructor was fine:
self.hello = Button(text="hello", on_press=self.auth)
would have worked if auth had been as described above.
If you read the Button documentation, the key seems to be to use the bind function:
def callback(instance):
print('The button <%s> is being pressed' % instance.text)
btn1 = Button(text='Hello world 1')
btn1.bind(on_press=callback)
Replace line
self.hello = Button(text="hello", on_press=lambda a:self.auth())
of your code and use this :
self.hello = Button(text="hello", on_press=lambda a:self.auth())
Also add below line in auth function to see if its called :)
print "auth called"
and There are many ways to perform a particular task .Above code will be to fix your code in minimum effort , However If you would like to do it in another way . Just use code below .
from kivy.uix.popup import Popup
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.stacklayout import StackLayout
class LoginScreen(GridLayout):
def __init__(self, **kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols = 2
self.row = 2
self.add_widget(Label(text='User Name'))
self.username = TextInput(multiline=False)
self.add_widget(self.username)
self.add_widget(Label(text='password'))
self.password = TextInput(password=True, multiline=False)
self.add_widget(self.password)
self.hello = Button(text="hello")
self.hello.bind(on_press=self.auth)
self.add_widget(self.hello)
def auth(self,instance):
print "auth called"
if self.username == "Hendricko":
popup = Popup(title="success",
content=Label(text="Howdy !"),
size=(100, 100),
size_hint=(0.3, 0.3),
auto_dismiss=False)
popup.open()
class MyApp(App):
def build(self):
return LoginScreen()
if __name__ == '__main__':
MyApp().run()
I report an example of buttons created dynamically within the main class, and then triggered into a single listener:
class allMyApp(TabbedPanel):
def __init__(self, **kwargs):
super(allMyApp, self).__init__(**kwargs)
#[...]
for y in sorted(set(emissionYears)):
btn = Button(text = y,
size_hint_y = None,
height = '48dp',
on_release = lambda btn: self.ids.choseEmissionDate.select(btn.text))
btn.bind(on_press = self.sortByYear)
self.ids.choseEmissionDate.add_widget(btn)
def sortByYear(self, instance):
year = instance.text
print(year)

Categories

Resources