Kivy Builder.load_file() not working as expected - python-3.x

I am trying to run a small kivy app. When I use Builder.load_string() my app runs okay, when I use Builder.load_file() all I get is a blank screen. Here is my code
main.py - Using Builder.load_string()
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.chip import MDChip
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MenuScreen>:
BoxLayout:
Button:
text: 'Goto settings'
on_press:
root.manager.transition.direction = 'left'
root.manager.current = 'settings'
Button:
text: 'Quit'
<SettingsScreen>:
BoxLayout:
Button:
text: 'My settings button'
Button:
text: 'Back to menu'
on_press:
root.manager.transition.direction = 'right'
root.manager.current = 'menu'
""")
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
pass
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class MyWidget(MDChip):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class MainApp(MDApp):
def __init__(self, **kwargs):
self.title = "My Material Application"
super().__init__(**kwargs)
def build(self):
return sm
if __name__ == "__main__":
MainApp().run()
Here is the same code with Builder.load_file()
main.py
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.chip import MDChip
from kivy.uix.screenmanager import ScreenManager, Screen
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
pass
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class MyWidget(MDChip):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class MainApp(MDApp):
def __init__(self, **kwargs):
self.title = "My Material Application"
super().__init__(**kwargs)
def build(self):
self.root = Builder.load_file('main.kv')
return sm
if __name__ == "__main__":
MainApp().run()
main.kv
<MenuScreen>:
BoxLayout:
Button:
text: 'Goto settings'
on_press:
root.manager.transition.direction = 'left'
root.manager.current = 'settings'
Button:
text: 'Quit'
<SettingsScreen>:
BoxLayout:
Button:
text: 'My settings button'
Button:
text: 'Back to menu'
on_press:
root.manager.transition.direction = 'right'
root.manager.current = 'menu'
All I get when I run the code is a blank screen.
What am I doing wrong?

Apparently you have to include the following code in def build(self)
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
So final main.py file will be
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.chip import MDChip
from kivy.uix.screenmanager import ScreenManager, Screen
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
pass
class MyWidget(MDChip):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class MainApp(MDApp):
def __init__(self, **kwargs):
self.title = "My Material Application"
super().__init__(**kwargs)
def build(self):
self.root = Builder.load_file('main.kv')
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
return sm
if __name__ == "__main__":
MainApp().run()

Related

How to simultaneously display keypress for 2 separate Kivy windows

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.

KIVY: Numeric Keyboard ON Text of the Text Field

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

Kivy: Error while trying to capture options from MDDropDownMenu

I am new to kivy and am trying to add a DropDown list and trying to capture the options being clicked in a variable, but I seem to run into many errors while doing so.
This is the error that I am getting:
This is my main.py file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty
from kivy.vector import Vector
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.uix.menu import MDDropdownMenu
from kivymd.uix.behaviors import RectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivy.config import Config
from kivymd.app import MDApp
from kivy.lang import Builder
Config.set('graphics', 'resizable', False)
class CustomToolbar(ThemableBehavior, RectangularElevationBehavior, MDBoxLayout,):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.md_bg_color = self.theme_cls.primary_color
class MainApp(MDApp):
def __init__(self, **kwargs):
self.VARIABLE = ""
super().__init__(**kwargs)
self.screen = Builder.load_file("main.kv")
menu_items = [{"viewclass": "MDMenuItem","text": "Label1"},{"viewclass":
"MDMenuItem","text": "Label2"}]
self.menu = MDDropdownMenu(
caller=self.screen.ids.drop_item,
items=self.menu_items,
position="center",
width_mult=3,
)
self.menu.bind(on_release=self.menu_callback)
def change_variable(self, value):
print("\nvalue=", value)
self.VARIABLE = value
print("\tself.VARIABLE=", self.VARIABLE)
def menu_callback(self, instance_menu, instance_menu_item):
instance_menu.dismiss()
def build(self):
return self.screen
MainApp().run()
This is my .kv file:
<ScreenManagement>:
id: scr_mngr
MainAppScreen:
name: 'main'
id: main
<CustomToolbar>:
#some widgets here
BoxLayout:
#some widgets here
<MDMenuItem>:
on_release: app.root.change_variable(self.text)
Screen:
MDRaisedButton:
id: drop_item
text: "W"
pos_hint: {"center_x": .9, "center_y": .68}
on_release: app.menu.open()
I am stuck at this for 2 days, please help. Thanks in advance :D
I think your code will work if you build the GUI in the build() method and use Clock.schedule_once() to setup the menu, like this:
class MainApp(MDApp):
def change_variable(self, value):
print("\nvalue=", value)
self.VARIABLE = value
print("\tself.VARIABLE=", self.VARIABLE)
def menu_callback(self, instance_menu, instance_menu_item):
instance_menu.dismiss()
def build(self):
self.VARIABLE = ""
self.screen = Builder.load_file("main.kv")
Clock.schedule_once(self.setup_menu)
return self.screen
def setup_menu(self, dt):
menu_items = [{"viewclass": "MDMenuItem","text": "Label1"},{"viewclass":
"MDMenuItem","text": "Label2"}]
self.menu = MDDropdownMenu(
caller=self.screen.ids.drop_item,
items=menu_items,
position="center",
width_mult=3
)
self.menu.bind(on_release=self.menu_callback)

Python - Kivy: Label does not update during a function excution

I saw that there are other topics related to this one but I could make this simple project to work.
I need a label to be updated with the function status while it is running just to let the user know that the program still working while the function is processing, but the label is only updated when the function is completed
Here are the files
my.kv
WindowManager:
ChangeLabel:
<ChangeLabel>:
BoxLayout:
orientation: 'vertical'
padding: 200, 20
Label:
id: labeltxt
text: 'Text to change'
font_size: 30
height: 50
Button:
text:'Run Function'
size_hint_y: None
height: 30
on_release:
root.changeLabel()
Label:
test.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.core.window import Window
import time
from kivy.clock import Clock
class ChangeLabel(Screen):
def changeLabel(self):
Clock.schedule_once(self.test)
def test(self, dt):
self.ids.labeltxt.text = 'Function is running...'#I wanna this in lablel while function is running
time.sleep(2)# function to run
self.ids.labeltxt.text = 'Completed'
class WindowManager (ScreenManager):
pass
kv = Builder.load_file('my.kv')
class MyApp(App):
def build(self):
return kv
MyApp().run()
I did this solution, now it is working the way I want, but not sure if this is the best way.
test.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.core.window import Window
import time
from kivy.clock import Clock
class ChangeLabel(Screen):
i = 0
def changeLabel(self):
Clock.schedule_interval(self.test, 0.05)
def test(self, dt):
if self.i == 0:
self.ids.labeltxt.text = 'Function is running...'
self.i = 1
else:
time.sleep(2)# function to run
self.ids.labeltxt.text = 'Completed'
self.i = 0
Clock.unschedule(self.test)
class WindowManager (ScreenManager):
pass
kv = Builder.load_file('my.kv')
class MyApp(App):
def build(self):
return kv
MyApp().run()

dynamic binding of functions not working in kivy

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'

Resources