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()
Related
I am creating myself a GPA calculator using python kivy and I am trying to switch the text of a label but it is not working. I could use some help. I am only going to show the code that I require to change the text, but if you need it all, I am happy to send it through.
.py file:
from kivy.core.window import Window
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.popup import Popup
class main(Screen):
def add_class(self):
sm.current = 'add'
sm.transition.direction = 'left'
def remove_class(self):
sm.current = 'remove'
sm.transition.direction = 'right'
def update(self):
#from the screen that adds the courses
courses = AddClass.courses
marks = []
for key in courses:
marks.append(courses[key])
total = 0.0
for i in marks:
total += i
total /= len(marks)
#print(total)
self.ids.gpa.text = f'{total}/4.0'
class MyApp(App):
def build(self):
#main screen
sm.add_widget(main(name='main'))
#screen that adds the marks
sm.add_widget(AddClass(name='add'))
#screen that removes a certain mark
sm.add_widget(RemoveClass(name='remove'))
sm.current = 'main'
return sm
if __name__ == '__main__':
MyApp().run()
.kv file:
<main>
FloatLayout:
Button:
text: 'Your GPA'
font_size: 50
size_hint: 0.4, 0.4
pos_hint: {'x': 0.3, 'y': 0.6}
id: gpa
background_color: 0, 0, 0
on_release: print(self.text)
Button:
text: 'Remove Class/Course'
font_size: 28
id: remove_class
size_hint: 0.4, 0.4
pos_hint: {'y': 0.1, 'x': 0.05}
on_release: root.remove_class()
Button:
text: 'Add Class/Course'
font_size: 30
id: add_class
size_hint: 0.4, 0.4
pos_hint: {'y': 0.1, 'x': 0.55}
on_release: root.add_class()
Help is much appreciated!
Text of a label can be a kivy property, which can be later changed and since it is a kivy property it will automatically update everywhere. Here is an example of .py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
import random
class YourWidget(Widget):
random_number = StringProperty()
def __init__(self, **kwargs):
super(YourWidget, self).__init__(**kwargs)
self.random_number = str(random.randint(1, 100))
def change_text(self):
self.random_number = str(random.randint(1, 100))
class YourApp(App):
def build(self):
return YourWidget()
if __name__ == '__main__':
YourApp().run()
and .kv example
<YourWidget>:
BoxLayout:
size: root.size
Button:
id: button1
text: "Change text"
on_release: root.change_text()
Label:
id: label1
text: root.random_number
When you click the button, it will call the change_text() function, which will randomly change the text of the label to a random integer between 1 and 100.
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.
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 trying to make a program that puts x amount of TextInputs based on the user's input. I can do that in the py file, but how do I do this in the kv file? I also need to be able to reference these TextInputs later on.
This is what I have so far:
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
class GetCount(Screen):
count_input = ObjectProperty(None)
def next(self):
# Setup next screen
text_inputs = [TextInput(multiline=False) 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
<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>:
FloatLayout:
GridLayout:
id: grid
cols: 1
""")
class MainApp(App):
def build(self):
return kv_file
if __name__ == "__main__":
app = MainApp()
app.run()
The problem is, the TextInputs are being added and created by the py code, how can I create the widget in kv and then add it in py?
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.