I am trying to change the text of a label using an id, I tried it with stringProperty, with objectProperty without any properties. There has to be something I am missing in my code because it simply does not work whatever I try and any help would be greatly appreciated.
This bit of code is a simple screen with 2 buttons, one for going to the other screen and one for changing the label
from kivy.app import *
from kivy.uix.button import *
from kivy.graphics import *
from kivy.uix.widget import *
from kivy.uix.label import *
from kivy.uix.floatlayout import *
from kivy.uix.boxlayout import *
from kivy.uix.relativelayout import *
from kivy.uix.scrollview import ScrollView
from kivy.properties import ListProperty, StringProperty,ObjectProperty
from kivy.uix.screenmanager import ScreenManager,Screen
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
class WindowManager(ScreenManager):
pass
class Name(FloatLayout):
def __init__(self, **kwargs):
super(Name, self).__init__(**kwargs)
def changeName(self):
print(self.ids)
self.name = self.ids.nameOfSong.text
print(self.name)
self.ids.nameOfSong.text = 'name'
self.name = self.ids.nameOfSong.text
print(self.name)
class MainWindow(Screen):
def __init__(self, **kwargs):
super(MainWindow, self).__init__(**kwargs)
self.pos = (0, 0)
self.size = (1,1)
self.z = Name()
self.add_widget(self.z)
def swap(self):
Name().changeName()
class SecondWindow(Screen,BoxLayout):
def __init__(self, **kwargs):
super(SecondWindow, self).__init__(**kwargs)
class langApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MainWindow(name='main'))
sm.add_widget(SecondWindow(name='second'))
return sm
Builder.load_file("kiv.kv")
if __name__ == '__main__':
langApp().run()
My kiv.kv file, most of it is not connected to the problem (I think)
#:kivy 1.11.1
WindowManager:
MainWindow:
SecondWindow:
<MainWindow>:
name: "main"
FloatLayout:
pos: 0,0
size: root.width,root.height
Button:
on_release:
root.manager.transition.direction = 'left'
app.root.current = "second"
text: 'Stop'
pos_hint: {'x':.45,'y':.1}
size_hint: .1,.1
Button:
on_press: root.swap()
text: 'Next'
pos_hint: {'x':.65,'y':.1}
size_hint: .1,.1
<SecondWindow>:
name: "second"
FloatLayout:
pos: 0,0
size: root.width,root.height
Button:
on_release:
root.manager.transition.direction = 'right'
app.root.current = "main"
text: 'Stop'
pos_hint: {'x':.45,'y':.1}
size_hint: .1,.1
<Name>:
Label:
text: nameOfSong
font_size: 20
size_hint: None, None
pos_hint: {'x': 0.435, 'y': 0.25}
A few problems with your code:
First, your code as posted dos not run. The line in your kv:
text: nameOfSong
is illegal.
Second, the code:
def swap(self):
Name().changeName()
is creating a new instance of Name and calling changeName() on that new instance. However, that new instance is not the one that is displayed in your GUI.
To fix that, you just need to call changeName() on the instance of Name that is in your GUI. Conveniently, you have saved a reference to the correct instance with the line:
self.z = Name()
So, you can change the swap() method to use that instance of Name:
def swap(self):
self.z.changeName()
The other problem is that the changeName() method tries to use a non-existent id nameOfSong. To fix that (and to make your posted code runnable), just change the <Name> rule in your kv to define that id:
<Name>:
Label:
id: nameOfSong
text: 'Some Name'
font_size: 20
size_hint: None, None
pos_hint: {'x': 0.435, 'y': 0.25}
On an unrelated note, your code is building the App GUI twice. The line:
Builder.load_file("kiv.kv")
is building the GUI from the lines:
WindowManager:
MainWindow:
SecondWindow:
and your python code is building it again here:
def build(self):
sm = ScreenManager()
sm.add_widget(MainWindow(name='main'))
sm.add_widget(SecondWindow(name='second'))
return sm
You can delete those three lines from your kv file.
Related
I'm having this small issue with Kivy currently. I'm working on a POS system and I am keeping track of the total with a variable named total that is a NumericProperty in the MyApp class. When I add a function that resets it to 0, the total variable stays 0 and will not add again. After a bit of troubleshooting, I've figured out that if I exclude the line self.clear_widgets(), it works as intended however I do need this line. Is there any reason why this happens?
I have provided a simple example code below to demonstrate the issue. Any help would be appreciated!
.py file
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import NumericProperty
class MainScreen(Screen):
def add(self, *args):
App.get_running_app().total += 1
def set_0(self, *args):
App.get_running_app().total = 0
self.clear_widgets()
self.add_widget(Label(text = str(App.get_running_app().total), font_size = 50))
self.add_widget(Button(text = 'Add 1', size_hint = (0.2,0.1), pos_hint = {'x': 0.3, 'y': 0.2}, on_release = self.add))
class WindowManager(ScreenManager):
pass
class MyApp(App):
total = NumericProperty(0)
def build(self):
return WindowManager()
if __name__ == '__main__':
MyApp().run()
.kv
<WindowManager>:
MainScreen
<MainScreen>:
FloatLayout:
Label:
text: str(app.total)
font_size: 50
Button:
text: 'Add 1'
size_hint: 0.2,0.1
pos_hint: {'x': 0.3, 'y': 0.2}
on_release:
root.add()
Button:
text: 'Set to 0'
size_hint: 0.2,0.1
pos_hint: {'x': 0.5, 'y': 0.2}
on_release:
root.set_0()
I figured out a way around this issue. I created another FloatLayout with an id of list inside my MainScreen and placed only the widgets I needed to remove inside this FloatLayout. So when I add the line self.ids.list.clear_widgets(), it doesn't affect the total variable.
.py
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import NumericProperty
class MainScreen(Screen):
def add(self, *args):
App.get_running_app().total += 1
def set_0(self, *args):
App.get_running_app().total = 0
self.ids.list.clear_widgets()
class WindowManager(ScreenManager):
pass
class MyApp(App):
total = NumericProperty(0)
def build(self):
return WindowManager()
if __name__ == '__main__':
MyApp().run()
.kv
<WindowManager>:
MainScreen
<MainScreen>:
FloatLayout:
id: list
size_hint: 0.16,0.81
FloatLayout:
Label:
text: str(app.total)
font_size: 50
Button:
text: 'Add 1'
size_hint: 0.2,0.1
pos_hint: {'x': 0.3, 'y': 0.2}
on_release:
root.add()
Button:
text: 'Set to 0'
size_hint: 0.2,0.1
pos_hint: {'x': 0.5, 'y': 0.2}
on_release:
root.set_0()
Is there a way to reference a custom widget from the py file?
I made a widget in kv, but I want to reference it from py, then add it again to another widget in kv.
I tried doing this using id but I got an error (KeyError: 'words_entry').
This is what I tried:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.textinput import TextInput
import os
class GetCount(Screen):
count_input = ObjectProperty(None)
def next(self):
# Setup next screen
text_inputs = [self.ids.words_entry for i in range(int(self.count_input.text))]
for text_input in text_inputs:
self.manager.ids.get_input.ids.grid.add_widget(text_input)
# Switch to next screen
self.manager.current = "get_input"
class GetInput(Screen):
pass
kv_file = Builder.load_string("""
ScreenManager:
GetCount:
name: "get_count"
id: get_count
GetInput:
name: "get_input"
id: get_input
<WordEntry#TextInput>:
id: words_entry
multiline: False
size_hint: (self.width, None)
<GetCount>:
count_input: count_input
FloatLayout:
Label:
text: "count"
size_hint: 1, 0.05
pos_hint: {"top":0.9}
TextInput:
id: count_input
size_hint: 0.8, 0.05
pos_hint: {"top":0.7, "x":0.1}
multiline: False
Button:
text: "Next"
on_release: root.next()
size_hint: 0.8, 0.05
pos_hint: {"top":0.5, "x":0.1}
<GetInput>:
ScrollView:
GridLayout:
size_hint_y: None
height: self.minimum_height
id: grid
cols: 1
""")
class MainApp(App):
def build(self):
return kv_file
if __name__ == "__main__":
app = MainApp()
app.run()
In this code, I want to add WordEntry to the GridLayout in GetInput from py (the reason is that I need to add multiple depending on the user's input).
You can use Factory to create an instance of a class that has been defined in kv. So your GetCount class can be:
from kivy.factory import Factory
class GetCount(Screen):
count_input = ObjectProperty(None)
def next(self):
# Setup next screen
for _ in range(int(self.count_input.text)):
new_word_entry = Factory.WordEntry()
self.manager.ids.get_input.ids.grid.add_widget(new_word_entry)
# Switch to next screen
self.manager.current = "get_input"
I'm new to kivy and python so my code isn't perfect.
I'm trying to make a program with 2 screens, a first screen where there is a label with a text that is not defined and that can change and a second screen that keeps the same text as the first screen.
I've been searching for a week and I tried to make a global variable that I edit and that becomes the text of the second label but it doesn't work.
I also tried with String. property () or object. property () but I didn't get any more results and I didn't really understand how to use it.
Any help would be welcome <3
(sorry for my english)
Here is a part of my code that I have simplified as much as possible:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MenuScreen>:
label_wid : ratio
FloatLayout:
Button:
text: "options"
pos: 270, 240
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press: root.manager.current = 'settings'
Label:
id:ratio
text: ""
pos: 0,90
font_size:30
color:1,0,0,1
<SettingsScreen>:
label_wid2 : ratio
FloatLayout:
Label:
id:ratio
text: str(root.texte2())
pos: 0,90
font_size:30
color:1,0,0,1
""")
u=""
class MenuScreen(Screen):
def texte(self):
global u
u= self.label_wid.text = 'exemple'
pass
class SettingsScreen(Screen):
def texte2(self):
self.label_wid2.text=u
pass
class Quizz(App):
def build(self):
self.title = 'Quizz'
Window.clearcolor = (0, 1, 1, 0.25)
return sm
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
if __name__ == '__main__':
Quizz().run()
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MenuScreen>:
label_wid : ratio
FloatLayout:
Button:
text: "options"
pos: 270, 240
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press: root.manager.current = 'settings'
Label:
id:ratio
text: ""
pos: 0,90
font_size:30
color:1,0,0,1
<SettingsScreen>:
label_wid : ratio
FloatLayout:
Label:
id:ratio
text: root.texte2()
pos: 0,90
font_size:30
color:1,0,0,1
""")
u=""
class MenuScreen(Screen):
def texte(self):
global u
u= self.label_wid.text = 'exemple'
pass
class SettingsScreen(Screen):
def texte2(self, text):
u
pass
class Quizz(App):
def build(self):
self.title = 'Quizz'
Window.clearcolor = (0, 1, 1, 0.25)
return sm
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
if __name__ == '__main__':
Quizz().run() ```
Using a StringProperty in the ScreenManager and another in SettingsScreen provide a source of text for both Labels in the different Screens. And changing the StringProperty can change both Labels:
from kivy.app import App
from kivy.clock import Clock
from kivy.properties import StringProperty
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
# this is the text to be used when the first Label text is not '1'
theOtherText = StringProperty('Not One')
class MyScreenManager(ScreenManager):
# This is the text that both Labels may display
theText = StringProperty('0')
kv = """
MyScreenManager:
MenuScreen:
name: 'menu'
SettingsScreen:
name: 'settings'
<MenuScreen>:
label_wid : ratio
FloatLayout:
Button:
text: "options"
pos: 270, 240
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press: root.manager.current = 'settings'
Label:
id:ratio
# Use theText Property from MyScreenManager
text: root.manager.theText
pos: 0,90
font_size:30
color:1,0,0,1
<SettingsScreen>:
label_wid : ratio
FloatLayout:
Label:
id:ratio
# Use theText Property from MyScreenManager
text: '1' if root.manager.theText == '1' else root.theOtherText
pos: 0,90
font_size:30
color:1,0,0,1
"""
class Quizz(App):
def build(self):
self.title = 'Quizz'
Window.clearcolor = (0, 1, 1, 0.25)
Clock.schedule_once(self.change_text, 4)
return Builder.load_string(kv)
def change_text(self, dt):
print('changing text')
self.root.theText = '1'
if __name__ == '__main__':
Quizz().run()
Building the MyScreenManager in the kv eliminates the need to check if the manager attribute of the Screens is set. That simplifies the logic in the kv. Then the line in kv:
text: '1' if root.manager.theText == '1' else root.theOtherText
sets the text of the second Label to 1 if the first Label text is 1, otherwise it is set to the value of theOtherText in the SettingsScreen.
I have added a Clock.schedule_once() just for testing.
I have a list to display to the user but I want to break each section to a separate textinput as it will make more sense that way. I then need to be able to capture those changes and update my variable.
I need to get the variables to display correctly tlmanualreg being backgroundManualReg[0] and on closeit() store the changes back to a list.
EDIT: Updated with bare bones popup project to test variable exchanging.
from kivy.properties import StringProperty
from os.path import join, dirname
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.app import App
import json
Builder.load_string("""
<-MyPopup#Popup>:
tlpos:tlpos
tlmanualreg:tlmanualreg
cols: 1
GridLayout:
cols:3
Label:
text: "Location"
Label:
text: "Dist from Center (mm)"
Label:
text: "Corners of sheet (for manual-align)"
Label:
text: "Top-Left:"
TextInput:
id: tlpos
text: root.backgroundTLPOS
TextInput:
id: tlmanualreg
text: root.backgroundTLManualReg
Button:
size_hint_y:.1
text: 'Update Variables'
on_press: root.closeit()
""")
class ScreensApp(App):
def build(self):
content = MyPopup()
_popup = Popup(content=content, auto_dismiss=False)
_popup.open()
class MyPopup(Popup):
backgroundManualReg = [[551, 218], [3168, 319], [519, 1617], [3190, 1589]]
backgroundTLPOS = StringProperty("[0, 0]")
backgroundTLManualReg = StringProperty("[1,1],[2,2]")
def __init__(self, **kwargs):
super(MyPopup, self).__init__(**kwargs)
self.ids.tlmanualreg.text = str([1, 1])
self.backgroundTLManualReg = str(self.backgroundManualReg[0])
def closeit(self):
self.backgroundTLPOS = self.tlpos.text
print("backgroundTLPOS: ", self.backgroundTLPOS)
print("backgroundTLManualReg: ", self.ids.tlmanualreg.text)
print("backgroundManualReg: ", self.backgroundManualReg)
if __name__ == '__main__':
ScreensApp().run()
In the following example, we are:
Using dictionary. The key of the dictionary is use as Kivy widget's id and also as a reference to update the values.
Dynamically adding widgets as per item (key, value) in the dictionary
Retrieving data using for child in reversed(self.container.children) and isinstance(child, TextInput)
Example
main.py
from kivy.properties import StringProperty, DictProperty
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.app import App
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
import ast
Builder.load_string("""
<HeaderCell#Label>
text_size: self.size
halign: "left"
valign: "middle"
canvas.before:
Color:
rgba: 1, 0.502, 0, 1
Rectangle:
pos: self.pos
size: self.size
<MyPopup>:
title: 'My Popup'
container: container
GridLayout:
cols: 1
GridLayout:
cols: 3
size_hint_y: 0.1
HeaderCell:
text: "Location"
HeaderCell:
text: "Dist from Center (mm)"
HeaderCell:
text: "Corners of sheet (for manual-align)"
GridLayout:
id: container
cols:3
Button:
size_hint_y:.1
text: 'Update Variables'
on_press: root.closeit()
""")
class ScreensApp(App):
def build(self):
_popup = MyPopup(auto_dismiss=False)
_popup.open()
class MyPopup(Popup):
backgroundTLPOS = StringProperty("[0, 0]")
bgManualRegDict = DictProperty({'Top-Left': [551, 218], 'Top-Right': [3168, 319],
'Bottom-Left': [519, 1617], 'Bottom-Right': [3190, 1589]})
def __init__(self, **kwargs):
super(MyPopup, self).__init__(**kwargs)
self.populate_rows()
def populate_rows(self):
for key, value in self.bgManualRegDict.items():
location = Label(text="{}:".format(key), valign="middle")
location.bind(size=location.setter('text_size'))
bgTLpos = TextInput(text="{}".format(self.backgroundTLPOS))
bgTLmanualReg = TextInput(id="{}".format(key), text="{}".format(value))
self.container.add_widget(location)
self.container.add_widget(bgTLpos)
self.container.add_widget(bgTLmanualReg)
def closeit(self):
print("\ncloseit:")
for child in reversed(self.container.children):
if isinstance(child, TextInput):
if child.id in self.bgManualRegDict.keys():
print("\tid={0}, text={1}".format(child.id, child.text))
self.bgManualRegDict[child.id] = ast.literal_eval(child.text)
print("\n\tself.bgManualRegDict=", self.bgManualRegDict)
if __name__ == '__main__':
ScreensApp().run()
Output
Question: How do I update Label text in this scenario?
Python code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.textinput import TextInput
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.scrollview import ScrollView
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
class SearchForPart(Widget):
def searchforpart(self):
Barty = 'text update';
demogar = DemoGarage()
demogar.ids.scrollref.UpdateParam(Barty)
class ScrollableLabel1(ScrollView):
text_variable_1 = StringProperty()
def __init__(self, **kwargs):
super(ScrollableLabel1, self).__init__(**kwargs)
self.text_variable_1 = 'initial'
def UpdateParam(self, param):
self.param = param
self.text_variable_1 = param
print(param)
class AnotherScreen(Screen):
pass
class MainScreen(Screen):
pass
class DemoGarage(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("garagemainexample.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
Kv code:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
DemoGarage:
<BigButton#Button>:
font_size: 40
size_hint: 0.5, 0.15
color: 0,1,0,1
<MainScreen>:
name: "main"
FloatLayout:
BigButton:
on_release: app.root.current = "demogarage"
text: "Demo Garage"
pos_hint: {"x":0.25, "top": 0.4}
<DemoGarage>:
name: "demogarage"
ScrollableLabel1:
id:scrollref
SearchForPart:
id:searchpart
<ScrollableLabel1>:
Label:
id: mylabel
text: root.text_variable_1
font_size: 20
text_size: self.width, None
size_hint_y: None
height: self.texture_size[1]
padding_y: 10
padding_x: 140
<SearchForPart>:
size_hint: 1,1
FloatLayout:
Label:
text: "Search for Part:"
font_size: 30
pos_hint: {"x": 0.95, "y": 0.85}
color: 0,1,0,1
size_hint: 0.3, 0.2
TextInput:
id: partname
multiline: False
font_size: 30
pos_hint: {"x": 0.85, "y": 0.15}
color: 0,1,0,1
on_text_validate: root.searchforpart()
size_hint: 1, 0.5
After initializing program, the Label text is successfully initialized and outputs 'initial text'.
Once the 'searchforpart' method runs (as a response to hitting enter in search box), even though 'print(param)' prints 'text update', the label text isn't updated.
Question: How do I update Label text in this scenario?
Am I misunderstanding a core concept of how changes to the Kivy event loop are triggered?
Use a Kivy property:
from kivy.properties import StringProperty
class (YourClass):
text_variable_1 = StringProperty()
When you reference this property in kv, and its value changes, the gui will automatically update. If you don't use a property this doesn't happen because the kv code has no way to know the value changed.
Also, you don't have to follow pep8, but the property name should start with a lower case letter - it probably doesn't matter for your example, but can matter in kv if you try to set the property there.