I have a case where I need 2 Kivy windows each on a separate monitor to display keyboard press events simultaneously. I have a simple toy example of the code for 'app1.py' and 'app2.py' below.
I was able to successfully use 'subprocess' and kivy.config so that when you press the 'Press to open second window' button in my main application (i.e., app1) a second window (i.e., app2) opens on my second monitor. However, I am stuck on to how I can simultaneously display keyboard events to both application windows when the spacebar is pressed.
Any ideas on how this can be achieved, is it even possible in Kivy?
Best,
Tom
File name: app1.py
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.properties import StringProperty
from subprocess import Popen
Window.size = (1920, 1080)
Window.borderless = True
Builder.load_string('''
<FirstWindow>:
id: _FirstWindow
FloatLayout:
Label:
text: "First Window"
size_hint: None, None
font_size: 50
pos: (900,940)
Label:
text: _FirstWindow.key_down
size_hint: None, None
font_size: 30
pos: (900,800)
Button:
text: "Press to open second window"
size_hint: None, None
font_size: 30
size: (450, 60)
pos: (720, 600)
on_press: root.OpenSecondWindow()
''')
class FirstWindow(FloatLayout):
key_down = StringProperty() # perform button state
def __init__(self, **kwargs):
super(FirstWindow, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(None, self)
self._keyboard.bind(on_key_down=self.on_keyboard_down, on_key_up=self.on_keyboard_up)
def OpenSecondWindow(self):
p = Popen(['python ./app2.py'], shell=True)
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'spacebar':
self.key_down = 'spacebar pressed!'
def on_keyboard_up(self, keyboard, keycode):
if keycode[1] == 'spacebar':
self.key_down = ''
class App1(App):
def build(self):
return FirstWindow()
if __name__ == '__main__':
App1().run()
File name: app2.py
from kivy.config import Config
Config.set('graphics', 'resizable', '0')
Config.set('graphics', 'position', 'custom')
Config.set('graphics', 'top', '-900')
Config.set('graphics', 'left', '0')
Config.set('graphics', 'borderless', '1')
Config.set('graphics', 'fullscreen', 'auto')
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty
from kivy.lang import Builder
Window.size = (1920, 1080)
Window.borderless = True
Builder.load_string('''
<SecondWindow>:
id: _SecondWindow
FloatLayout:
Label:
text: "Second Window"
size_hint: None, None
font_size: 50
pos: (900,940)
Label:
text: _SecondWindow.key_down
size_hint: None, None
font_size: 30
pos: (900,800)
''')
class SecondWindow(FloatLayout):
key_down = StringProperty() # perform button state
def __init__(self, **kwargs):
super(SecondWindow, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(None, self)
self._keyboard.bind(on_key_down=self.on_keyboard_down, on_key_up=self.on_keyboard_up)
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'spacebar':
self.key_down = 'spacebar pressed!'
def on_keyboard_up(self, keyboard, keycode):
if keycode[1] == 'spacebar':
self.key_down = ''
class App2(App):
def build(self):
return SecondWindow()
if __name__ == '__main__':
App2().run()
From app1, you can write to the stdin of app2. Here are the changes to make that happen. In your OpenSecondWindow() method add stdin=PIPE and save a reference to the Popen object:
def OpenSecondWindow(self):
self.p = Popen(['python3 ./app2.py'], shell=True, stdin=PIPE, universal_newlines=True)
Then, in your on_keyboard_down() method, write to the stdin of app2:
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'spacebar':
self.key_down = 'spacebar pressed!'
self.p.stdin.write('spacebar pressed\n')
self.p.stdin.flush()
And in your app2, you must listen for input on stdin. Add this code to your SecondWindow:
def __init__(self, **kwargs):
super(SecondWindow, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(None, self)
self._keyboard.bind(on_key_down=self.on_keyboard_down, on_key_up=self.on_keyboard_up)
Thread(target=self.listen_for_input).start()
def listen_for_input(self):
while True:
data = input()
self.set_label(data)
#mainthread
def set_label(self, data):
self.ids.label.text = data
And the above requires an id in the kv for app2:
Label:
id: label
And you can add stdout=PIPE to the Popen, and use a similar construct to allow communication in the opposite direction.
Related
I am trying to add Numeric Keyboard in UI
Which will be display as clicked on the text on filed
But some how Numeric keyboard not show in the UI
I use numeric.json file to open the numeric keyboard as per the kivy Documentation json file
and link is [https://github.com/kivy/kivy/blob/master/examples/keyboard/numeric.json]
here is my code below
enter code here
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.screenmanager import ScreenManager
from kivy.core.window import Window
KV= '''
<REGITRATION_Window>:
name:'regitration_window1'
RelativeLayout:
MDToolbar:
title: 'Registraion'
elevation: 10
left_action_items: [['arrow-left']]
pos_hint: {"left":1, "top":1}
MDLabel:
text: 'Country Code '
font_size: 15
pos_hint : {'x':0.0322, 'y':0.272}
MDTextFieldRound:
int_text: 'For Eg:- +91'
pos_hint : {'x':0.0322, 'y':0.710}
size_hint : 0.08, .045
on_text: app.setup_key()
MDLabel:
text: 'Mobile Number'
font_size: 15
pos_hint : {'x':0.305, 'y':0.272}
MDTextFieldRound:
hint_text: 'For Eg:- 987654321'
pos_hint :{'x':0.305, 'y':0.710}
size_hint : 0.35, .045
MDFillRoundFlatButton:
text:'REGISTER'
pos_hint: {'x':.1, 'y':.1}
MDFillRoundFlatButton:
text:'Cancel'
pos_hint: {'x':.3, 'y':.1}
RelativeLayout:
id: data_layout
WindowManager:
REGITRATION_Window:
id: key_num
'''
class REGITRATION_Window(MDScreen):
pass
class WindowManager(ScreenManager):
pass
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
def close_key(self):
pass
def setup_key(self):
NumKB = Window.request_keyboard(self.close_key, self)
if NumKB.widget:
NumKB.widget.layout = 'numeric.json'
self.root.ids.key_num.ids.data_layout.add_widget(self.NumKB)
if __name__ == '__main__':
MainApp().run()
Well I took a look at your code and the Vkeyboard implementation on kivy github repo, it was a little complicated. but i came up with something. it was a little tricky but this should work.
I had to modify the numeric.json file a little so, here's my link to it and my github repo: numeric.json
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.screenmanager import ScreenManager
from kivy.uix.vkeyboard import VKeyboard
from kivy.core.window import Window
from kivy.properties import ObjectProperty
KV = '''
<NumericKeyboardScreen>:
name: 'numeric_screen'
RelativeLayout:
MDToolbar:
title: 'Numeric Keyboard Implementation'
elevation: 10
y: self.parent.height - self.height
MDLabel:
text: 'Number TextField'
font_size: 15
y: self.parent.height - self.height - dp(90)
pos_hint :{'center_x':0.5}
halign: 'center'
size_hint_y: None
height: dp(20)
MDTextField:
id: text_field
hint_text: 'For Eg:- 987654321'
y: self.parent.height - self.height - dp(135)
pos_hint :{'center_x':0.5}
size_hint_x : 0.35
mode: 'rectangle'
input_filter: 'int'
on_focus: root.set_layout(keyboard_anchor, self)
RelativeLayout:
id: keyboard_anchor
size_hint_y: 0.5
WindowManager:
NumericKeyboardScreen:
id: key_num
'''
class WindowManager(ScreenManager):
pass
class NumericKeyboardScreen(MDScreen):
focus_count = 0
def set_layout(self, keyboard_anchor, target_textfield):
self.focus_count += 1
v_keyboard = NumericKeyboard(
text_field = target_textfield
)
keyboard_anchor.clear_widgets()
keyboard_anchor.add_widget(v_keyboard)
if self.focus_count == 2:
keyboard_anchor.clear_widgets()
self.focus_count = 0
class NumericKeyboard(VKeyboard):
text_field = ObjectProperty()
custom_vk_layout = ObjectProperty('numeric.json')
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.available_layouts['numpad'] = self.custom_vk_layout
self.layout = self.custom_vk_layout
self.pos_hint = {'center_x': 0.5}
def on_key_down(self, keyboard, keycode, text, *args):
""" The callback function that catches keyboard events. """
if isinstance(keycode, tuple):
keycode = keycode[1]
if keycode == "bs":
if len(textfield_data) > 0:
self.text_field.text = textfield_data[:-1]
else:
self.text_field.text += u"{0}".format(keycode)
def on_key_up(self, keyboard, keycode, *args):
""" The callback function that catches keyboard events. """
textfield_data = self.text_field.text
if isinstance(keycode, tuple):
keycode = keycode[1]
if keycode == "bs":
if len(textfield_data) > 0:
self.text_field.text = textfield_data[:-1]
else:
self.text_field.text += u"{0}".format(keycode)
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
if __name__ == '__main__':
MainApp().run()
I'm still new to kivy and still relatively new to Python.
I have main. py. I plan to have WindowGUI take a number of inputs from main.py and display them in its textbox.
Here is what I have:
from os import curdir
from posixpath import dirname
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout # one of many layout structures
from kivy.uix.textinput import TextInput # allow for ...text input.
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.spinner import Spinner
from kivy.uix.dropdown import DropDown
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import ObjectProperty
kivy.require('2.0.0')
class MainGUI(Screen):
encoder = ObjectProperty()
mileage = ObjectProperty()
voltage = ObjectProperty()
power = ObjectProperty()
camera = ObjectProperty()
engaged = ObjectProperty()
idle = ObjectProperty()
log = ObjectProperty()
clear = ObjectProperty()
status = ObjectProperty(None)
def __init__(self, saySomething):
super().__init__() #Go up to __init__ of App
self.saySomething = saySomething
def power_option(self):
self.power.text = "Power"
def print_status(self):
self.ids.Status_screen.text = self.saySomething
return self.ids.Status_screen.text
class CameraGUI(Screen):
def __init__(self, **kwargs): #kwargs is just a variable. it could be "banana" or "apple"
super().__init__() #Go up to __init__ of App
class GUIManager(ScreenManager):
pass
kv = Builder.load_file('newWindow.kv')
class WindowGUI(App):
def __init__(self):
super().__init__()
# self.say = say
print("TRY ME!!!!")
def build(self):
self.title = 'IamHero'
return kv
if __name__ == '__main__':
myWindowGUI = WindowGUI()
myMainGUI = MainGUI('I am hero!')
myWindowGUI.run()
###########################_newWindow.kv_#############################
GUIManager:
MainGUI:
CameraGUI:
<MainGUI>:
name: "main"
status: Status_screen
GridLayout:
cols: 2
size: root.width, root.height #covers entire window
padding: 15
GridLayout:
cols:1
spacing: 10
GridLayout:
cols:2
size_hint: .25,.1
Label:
id: Engaged
text: "Engaged"
Label:
id: Idle
text: "Idle"
TextInput:
# size_hint: 1
id: Status_screen
multiline: True
readonly: True
background_color: .6, .7, 0, 1
text: root.print_status()
#on_text: root.print_status()
GridLayout:
cols:2
size_hint: .25,.1
Button:
id: Log
text: "Log status"
# size_hint_x: .5
Button:
id: Clear
text: "Clear"
However, I got the following error:
Traceback (most recent call last):
File "c:/Users/Jojo/#RaspberryPi4/Test/WindowGUI.py", line 57, in <module>
kv = Builder.load_file('newWindow.kv')
File "C:\Users\Jojo\Anaconda3\lib\site-packages\kivy\lang\builder.py", line 306, in load_file
return self.load_string(data, **kwargs)
File "C:\Users\Jojo\Anaconda3\lib\site-packages\kivy\lang\builder.py", line 408, in load_string
self._apply_rule(
File "C:\Users\Jojo\Anaconda3\lib\site-packages\kivy\lang\builder.py", line 659, in _apply_rule
child = cls(__no_builder=True)
TypeError: __init__() got an unexpected keyword argument '__no_builder'
Any ideas about what I did wrong?
You cannot require a positional argument in the __init__() of a class that you intend to use in kv. Your __init__() for MainGUI:
def __init__(self, saySomething):
requires an argument for saySomething. An easy way around that is to just make saySomething into a StringProperty of MainGUI. Then you don't even need to implement an __init__():
class MainGUI(Screen):
encoder = ObjectProperty()
mileage = ObjectProperty()
voltage = ObjectProperty()
power = ObjectProperty()
camera = ObjectProperty()
engaged = ObjectProperty()
idle = ObjectProperty()
log = ObjectProperty()
clear = ObjectProperty()
status = ObjectProperty(None)
saySomething = StringProperty('Hi')
# def __init__(self, saySomething):
# super().__init__() #Go up to __init__ of App
# self.saySomething = saySomething
Then you can set the value of saySomething just like any other property in the kv:
<MainGUI>:
name: "main"
status: Status_screen
saySomething: 'I am hero!'
or in python you can do:
MainGUI(saySomething='I am hero!')
Also, the line:
myMainGUI = MainGUI('I am hero!')
(which will no longer work) is creating an instance of MainGUI that is unused. So that line of code can be deleted.
Im am trying to display my arduino data into my kivy window under label but it says an error " ValueError: Label.text accept only str". Anyone able to help?
Below is my python kivy file
test.py file
import serial
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
class MainWindow(Screen):
def tm(self):
while (1):
with serial.Serial('COM4', 9600) as ser:
x = ser.readline()
return str(x)
ser.close()
class WindowManager(ScreenManager):
pass
hi = Builder.load_file("test.kv")
class MyMainApp(App):
def build(self):
return hi
if __name__ == "__main__":
MyMainApp().run()
test.kv file
WindowManager:
MainWindow:
<MainWindow>:
Label:
size: 75, 50
size_hint: None, None
pos_hint: {'right': 1, 'bottom': 1}
background_color: (1.0, 0.0, 0.0, 1.0)
text: root.x
I see a few problems in your example.
Be careful naming your variables. x for example, is almost always a NumericProperty of the widget already.
When you reference an attribute in python, use self, or the widgets x in your case wil not change.
When you have a loop in kivy, you will block the application. So you either use Clock or threading. I will suggest threading instead of Clock when using serial, because readline() will also block the app if waiting for an input.
You never actually call tm()
So, heres what I would change in MainWindow class, and a few more imports.
import serial
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
from kivy.properties import StringProperty
from kivy.clock import mainthread
import threading
KV = """
WindowManager:
MainWindow:
<MainWindow>:
Label:
size: 75, 50
size_hint: None, None
pos_hint: {'right': 1, 'bottom': 1}
background_color: (1.0, 0.0, 0.0, 1.0)
text: root.label_text
"""
class MainWindow(Screen):
label_text = StringProperty("")
def __init__(self, **kwargs):
super(MainWindow, self).__init__(**kwargs)
threading.Thread(target=self.tm).start()
def tm(self):
while (1):
with serial.Serial('COM4', 9600) as ser:
value = ser.readline()
ser.close()
self.update_label(value.decode("ascii").strip())
#mainthread
def update_label(self, value):
self.label_text = value
class WindowManager(ScreenManager):
pass
class MyMainApp(App):
def build(self):
return Builder.load_string(KV)
if __name__ == "__main__":
MyMainApp().run()
Be sure to use mainthread when changing any ui element in another thread. In this case when you change a stringproperty that will change the display.
I'm trying to create a button using touchripple.py found in kivy.uix.behaviors. However, I ended up unsuccessful. Can anyone show an easy example of touchripple with buttons using Kivy lang? Thanks in advance.
Now, only the ripple effect isn't showing still. Please advice. Thanks.
In 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)
def doit(self, *args):
print('in doit')
class Login(Screen):
pass
class MainScreen(Screen):
pass
class ScreenManager(ScreenManager):
pass
MainScreen = Builder.load_file("rippleexample2.kv")
class SimpleKivy4(App):
def build(self):
return MainScreen
if __name__ == "__main__":
SimpleKivy4().run()
In rippleexample2.kv:
ScreenManager:
Login:
MainScreen:
<Login>:
name:"login"
RippleButton:
text:'Login'
font_size: 24
on_release: app.root.current = "main"
<MainScreen>:
name: "main"
RippleButton:
text: 'back'
on_release: app.root.current = "login"
Below is a snippet for creating a Button which renders the touch ripple animation on interaction:
Snippet
class RippleButton(TouchRippleBehavior, Button):
def on_touch_down(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point:
touch.grab(self)
# The background_color (r, g, b, a) of button widget defaults to [1, 1, 1, 1]
# where 'a' (alpha compositing or transparency) is 1 i.e. not transparent
self.transparency = self.background_color[3] # backup original transparency / alpha compositing
self.background_color[3] = 0.5 # set transparency to half (0.5)
self.ripple_show(touch)
# dispatch on_press event because we have consumed on_touch_down
self.dispatch('on_press')
# consumed touch down and don’t want it to propagate any further.
return True
return False
def on_touch_up(self, touch):
if touch.grab_current is self:
touch.ungrab(self)
self.ripple_fade()
# defer on_release until ripple_fade has completed
def defer_release(dt):
self.background_color[3] = self.transparency # restore transparency / alpha compositing
self.dispatch('on_release')
Clock.schedule_once(defer_release, self.ripple_duration_out)
# consumed touch up and don’t want it to propagate any further.
return True
return False
Example
main.py
from kivy.app import App
from kivy.uix.behaviors.touchripple import TouchRippleBehavior
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
from kivy.clock import Clock
class RippleButton(TouchRippleBehavior, Button):
def on_touch_down(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point:
touch.grab(self)
# The background_color (r, g, b, a) of button widget defaults to [1, 1, 1, 1]
# where 'a' (alpha compositing or transparency) is 1 i.e. not transparent
self.transparency = self.background_color[3] # backup original transparency / alpha compositing
self.background_color[3] = 0.5 # set transparency to half (0.5)
self.ripple_show(touch)
# dispatch on_press event because we have consumed on_touch_down
self.dispatch('on_press')
# consumed touch down and don’t want it to propagate any further.
return True
return False
def on_touch_up(self, touch):
if touch.grab_current is self:
touch.ungrab(self)
self.ripple_fade()
# defer on_release until ripple_fade has completed
def defer_release(dt):
self.background_color[3] = self.transparency # restore transparency / alpha compositing
self.dispatch('on_release')
Clock.schedule_once(defer_release, self.ripple_duration_out)
# consumed touch up and don’t want it to propagate any further.
return True
return False
def doit(self, *args):
print('in doit')
class Login(Screen):
pass
class MainScreen(Screen):
pass
class SimpleKivy4(App):
def build(self):
return Builder.load_file("main.kv")
if __name__ == "__main__":
SimpleKivy4().run()
main.kv
#:kivy 1.11.0
ScreenManager:
Login:
MainScreen:
<Login>:
name:"login"
RippleButton:
text:'Login'
font_size: 24
on_release: root.manager.current = "main"
<MainScreen>:
name: "main"
RippleButton:
text: 'back'
on_release: root.manager.current = "login"
Output
After looking a bit closer at the example, I noticed that they are doing some odd things in the RippleButton. They were stopping the dispatching of the touch events for some unknown reason. I have modified the code to continue the dispatching (so now the on_release should work). And I have added a BooleanProperty to keep track of whether the TouchRipple Behavior is in effect.
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import BooleanProperty
from kivy.uix.behaviors import TouchRippleBehavior
from kivy.uix.button import Button
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)
def doit(self, *args):
print('in doit')
theRoot = Builder.load_string('''
RippleButton:
text: 'Click Here'
on_release: self.doit()
''')
class TouchRippleApp(App):
def build(self):
return theRoot
if __name__ == '__main__':
TouchRippleApp().run()
I wanted to make a dynamic class of a group of widget so whenever I add them to my main app, I had to only bring in the changes from somewhere(e.g. a python file, class etc, here I've done them in explicit lists), but binding the widget with properties like "on_text" which respond to events didn't really work, other properties like 'text', 'hint_text' worked perfectly but on_text doesn't really budge. I can't figure out the cause for this because I've checked the correct object being passed along the functions, below is my code:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
class Dynamic_TextInput(BoxLayout):
def __init__(self,changes=None, **kwargs):
super().__init__(**kwargs)
self.widgets = {'Mylabel':self.ids.mylabel,
'Myinput': self.ids.mytext}
self.Change(changes)
def Change(self, changes=None, **kwargs):
if changes:
for change in changes:
curwidget = self.widgets[change[0]]
cur_properties = change[1]
for attr in cur_properties.keys():
if attr=='bind':
print("The cur properties are: ")
print(cur_properties[attr])
curwidget.bind(**(cur_properties[attr]))
else:
setattr(curwidget, attr, cur_properties[attr])
class mainwidget(BoxLayout):
myobj1 = ObjectProperty()
myobj2 = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation='vertical'
change1=[('Mylabel', {'text':'firstchange',
'bind':{'on_text':lambda *_: print('something')}
}),
('Myinput', {'hint_text': 'Changed hint text'})
]
self.add_widget(Dynamic_TextInput(changes=change1))
self.add_widget(Dynamic_TextInput())
class MainApp(App):
def build(self):
return mainwidget()
if __name__ == '__main__':
MainApp().run()
And, in the kivy file:
#:kivy 1.10.0
<Dynamic_TextInput>:
myobj1: mylabel
myobj2: mytext
orientation: 'horizontal'
Label:
id: mylabel
text: 'testlable'
TextInput:
id: mytext
hint_text: 'some test'
What is the cause? And how can I fix it?
There are 2 ways to make a binding of a property:
*.py
object.bind(property=callback)
*.kv
object:
on_property: callback
So in the case of making the connection in python you should not use on_text, but text.
To verify the change, the Label can not be edited from the GUI, so I will use the TextInput to write on the Label:
*.py
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
class Dynamic_TextInput(BoxLayout):
def __init__(self,changes=None, **kwargs):
super().__init__(**kwargs)
self.widgets = {'Mylabel':self.ids.mylabel,
'Myinput': self.ids.mytext}
self.Change(changes)
def Change(self, changes=None, **kwargs):
if changes:
for change in changes:
curwidget = self.widgets[change[0]]
cur_properties = change[1]
for attr in cur_properties.keys():
if attr=='bind':
print("The cur properties are: ")
curwidget.bind(**(cur_properties[attr]))
print(cur_properties[attr])
else:
setattr(curwidget, attr, cur_properties[attr])
class mainwidget(BoxLayout):
myobj1 = ObjectProperty()
myobj2 = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation='vertical'
change1=[('Mylabel', {'text':'firstchange',
'bind':{'text':lambda *_: print('something')}
}),
('Myinput', {'hint_text': 'Changed hint text'})
]
self.add_widget(Dynamic_TextInput(changes=change1))
self.add_widget(Dynamic_TextInput())
class MainApp(App):
def build(self):
return mainwidget()
if __name__ == '__main__':
MainApp().run()
*.kv
#:kivy 1.10.0
<Dynamic_TextInput>:
myobj1: mylabel
myobj2: mytext
orientation: 'horizontal'
Label:
id: mylabel
text: mytext.text # <----
TextInput:
id: mytext
hint_text: 'some test'