How to get value outside function using python and kivy - python

this is a simple python kivy app, only text label and slider. This code works fine.
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.floatlayout import FloatLayout
from kivymd.app import MDApp
from kivymd.theming import ThemeManager
from kivymd.uix.slider import Slider
from kivy.properties import NumericProperty
from kivymd.uix.label import MDLabel
class Screen1(Screen):
def __init__(self, **kwargs):
super(Screen1, self).__init__(**kwargs)
layout = FloatLayout()
self.hours_value = MDLabel(text = "0", halign = "center", pos_hint = {'center_x':.5, 'center_y':.80}, theme_text_color = 'Error')
self.hours_slider = Slider(min = 0, max = 24, pos_hint = {'center_x':.5}, size_hint_x = .7)
self.hours_slider.bind(value = self.on_value_hours)
layout.add_widget(self.hours_value)
layout.add_widget(self.hours_slider)
self.add_widget(layout)
def on_value_hours(self, instance, slider):
self.hours_value.text = "%d"%slider
print(instance)
print("%d"%slider)
return slider
class myapp(MDApp):
def build(self):
self.theme_cls = ThemeManager()
self.theme_cls.primary_palette = 'Orange'
self.theme_cls.accent_pallete = 'Blue'
self.theme_cls.theme_style = 'Dark'
sm = ScreenManager()
sm.add_widget(Screen1(name = 's1'))
return sm
if __name__ == "__main__":
myapp().run()
But now I want to get value on slider outside my function def on_value_hours(self, instance, slider):
so I add this code my_value_slider = on_value_hours()
but I get a error on_value_hours() missing 3 required positional arguments: 'self', 'instance', and 'slider'
I tried to add my_value_slider = on_value_hours(1,1,1)
but this is also not working for me :(
give me a error AttributeError: 'int' object has no attribute 'hours_value'
this is a full code but still give me a error. I want to get value outside on my function. Please help me.
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.floatlayout import FloatLayout
from kivymd.app import MDApp
from kivymd.theming import ThemeManager
from kivymd.uix.slider import Slider
from kivy.properties import NumericProperty
from kivymd.uix.label import MDLabel
class Screen1(Screen):
def __init__(self, **kwargs):
super(Screen1, self).__init__(**kwargs)
layout = FloatLayout()
self.hours_value = MDLabel(text = "0", halign = "center", pos_hint = {'center_x':.5, 'center_y':.80}, theme_text_color = 'Error')
self.hours_slider = Slider(min = 0, max = 24, pos_hint = {'center_x':.5}, size_hint_x = .7)
self.hours_slider.bind(value = self.on_value_hours)
layout.add_widget(self.hours_value)
layout.add_widget(self.hours_slider)
self.add_widget(layout)
def on_value_hours(self, instance, slider):
self.hours_value.text = "%d"%slider
print(instance)
print("%d"%slider)
return slider
my_value_slider = on_value_hours(1,1,1)
class myapp(MDApp):
def build(self):
self.theme_cls = ThemeManager()
self.theme_cls.primary_palette = 'Orange'
self.theme_cls.accent_pallete = 'Blue'
self.theme_cls.theme_style = 'Dark'
sm = ScreenManager()
sm.add_widget(Screen1(name = 's1'))
return sm
if __name__ == "__main__":
myapp().run()

You can do something like this:
class Screen1(Screen):
# slider value
my_value_slider = NumericProperty(0)
def __init__(self, **kwargs):
super(Screen1, self).__init__(**kwargs)
layout = FloatLayout()
self.hours_value = MDLabel(text = "0", halign = "center", pos_hint = {'center_x':.5, 'center_y':.80}, theme_text_color = 'Error')
self.hours_slider = Slider(min = 0, max = 24, pos_hint = {'center_x':.5}, size_hint_x = .7)
self.hours_slider.bind(value = self.on_value_hours)
layout.add_widget(self.hours_value)
layout.add_widget(self.hours_slider)
self.add_widget(layout)
def on_value_hours(self, instance, slider):
self.hours_value.text = "%d"%slider
print(instance)
print("%d"%slider)
# save value
self.my_value_slider = slider
return slider

Another approach is more like your initial code, where my_slider_value would be a class variable (as opposed to an instance variable):
class Screen1(Screen):
# class variable
my_value_slider = 0
def __init__(self, **kwargs):
super(Screen1, self).__init__(**kwargs)
layout = FloatLayout()
self.hours_value = MDLabel(text = "0", halign = "center", pos_hint = {'center_x':.5, 'center_y':.80}, theme_text_color = 'Error')
self.hours_slider = Slider(min = 0, max = 24, pos_hint = {'center_x':.5}, size_hint_x = .7)
self.hours_slider.bind(value = self.on_value_hours)
layout.add_widget(self.hours_value)
layout.add_widget(self.hours_slider)
self.add_widget(layout)
def on_value_hours(self, instance, slider):
self.hours_value.text = "%d"%slider
print(instance)
print("%d"%slider)
# update value of class variable
Screen1.my_value_slider = slider
return slider
You can then access my_value_slider as Screen1.my_value_slider. Note that no parens are used in that access. The disadvantage of this approach is that my_value_slider is not a Property, so you can't bind to it.

Related

Kivy: Create a list of coloured labels using build() method

I want to create a list of coloured labels. The thing is that I could do it with the kv file, but I need to do it through the build() method. So I tried replicate what I have done, but it does not work. And I can't understand why.
This is what I've coded
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import *
class RL(RelativeLayout): # Creates the background colour for each label
def __init__(self, **kwargs):
super().__init__(**kwargs)
with self.canvas:
Color(.7, 0, .5, 1)
Rectangle(size_hint=self.size)
class MainMenu(BoxLayout):
N_LBLS = 8
labels_text = []
RL_list = []
def __init__(self, **kwargs):
super().__init__(**kwargs)
button = Button(text='do something')
button.bind(on_release=self.change_text)
box = BoxLayout(orientation='vertical', padding= 10, spacing = 15)
for i in range(0, self.N_LBLS):
self.RL_list.append(RL())
self.labels_text.append(Label(text=f'{i}º label', size_hint=self.size))
self.RL_list[i].add_widget(self.labels_text[i])
box.add_widget(self.RL_list[i])
self.add_widget(button)
self.add_widget(box)
def change_text(self, instance):
for lbl in self.labels_text:
if lbl.text[0] == '5':
lbl.text = 'Text changed'
class MainApp(App):
def build(self):
return MainMenu()
if __name__ == '__main__':
MainApp().run()
It's supposed to make a button to the left, and a list of 8 coloured labels to the right.
The problem is that you are setting size_hint=self.size in each Label. The self.size is the size of the MainMenu, which is [100,100] when that code is executed. Note that size_hint is a multiplier that is applied to the parents size to calculate the widgets size. So a size_hint of [100,100] makes each Label 100 times bigger than the MainMenu. So your code is working, but the Labels are so large that the text is off the screen. Start by just removing size_hint=self.size.
And, to set a background color on a Label, you can just use the canvas of that Label, rather than some container. Here is a version of your code that does that:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
class ColorLabel(Label):
pass
Builder.load_string('''
<ColorLabel>:
bg_color: [.7, 0, .5, 1]
canvas.before:
Color:
rgba: self.bg_color
Rectangle:
pos: self.pos
size: self.size
''')
class MainMenu(BoxLayout):
N_LBLS = 8
labels_text = []
def __init__(self, **kwargs):
super().__init__(**kwargs)
button = Button(text='do something')
button.bind(on_release=self.change_text)
box = BoxLayout(orientation='vertical', padding=10, spacing=15)
for i in range(0, self.N_LBLS):
self.labels_text.append(ColorLabel(text=f'{i}º label'))
box.add_widget(self.labels_text[i])
self.add_widget(button)
self.add_widget(box)
def change_text(self, instance):
for lbl in self.labels_text:
if lbl.text[0] == '5':
lbl.text = 'Text changed'
lbl.bg_color = [0, 1, 0, 1]
class MainApp(App):
def build(self):
return MainMenu()
if __name__ == '__main__':
MainApp().run()

Why is my navigation code not working? kivy python

I have a screen manager ThirdWindow which is supposed to change the screen when B1 is clicked basically a navigation thing. Please note ThirdWindow is a a different class. callback is called when B1 is clicked. it changes the screen with the help of ThirdWindow I think. I used self.manager.current it didn't worked, I also tried using it by making ThirdWindow a def in class Dre but the results were same. The print statement is not getting printed that is inside def callback(self, instance) so I think the def is not getting called on_click at all. How can I fix that? Thanks
import kivy
from kivy.app import App
from kivy.core.audio import SoundLoader
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.stacklayout import StackLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.graphics import Rectangle
from kivy import platform
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MainWindow(Screen):
pass
class ThirdWindow(ScreenManager):
def load(self):
sm = ScreenManager
sm.add_widget(Dre(name='Dre'))
sm.add_widget(SecondWindow(name='SecondWindow'))
self.sm.current = 'Dre'
class Dre(RelativeLayout):
def __init__(self, **kwargs):
super(Dre, self).__init__(**kwargs)
self.color = [254/255, 102/255, 37/255, 1]
self.H_color = [254/255, 102/255, 37/255]
self.sound_theme = None
self.init_audio()
self.kv = Builder.load_file('Levels.py')
if platform in ('linux', 'win', 'macosx'):
with self.canvas.before:
self.bg = Rectangle(size=self.size, source='Neo.png')
self.bind(pos=self.update_bg)
self.bind(size=self.update_bg)
else:
with self.canvas.before:
self.bg = Rectangle(size=self.size, source='Neon.png')
self.bind(pos=self.update_bg)
self.bind(size=self.update_bg)
def update_bg(self, *args):
if platform in ('linux', 'win', 'macosx'):
self.bg.pos = self.pos
self.bg.size = self.size
self.add_widget(Label(text='D R E A M S',
pos_hint={'center_x': .5, 'center_y': .8},
font_size='60dp', font_name='Roboto-Bold.ttf', color=self.H_color))
B1 = Button(text='P L A Y', font_name='Roboto-Bold.ttf', size_hint=(.2, .15),
pos_hint={'center_x': .5, "center_y": .3}, background_color = self.color, background_normal='')
# B1.bind(on_press=return self.kv)
self.add_widget(B1)
else:
self.bg.pos = self.pos
self.bg.size = self.size
self.add_widget(Label(text='D R E A M S',
pos_hint={'center_x': .5, 'center_y': .8},
font_size='30dp', font_name='Roboto-Bold.ttf', color=self.H_color))
B1 = Button(text='P L A Y', font_name='Roboto-Bold.ttf', size_hint=(.2, .15),
pos_hint={'center_x': .5, "center_y": .3}, background_color=self.color, background_normal='',
on_press=self.callback)
self.add_widget(B1)
def callback(self, instance):
print('working')
self.manager.current = 'SecondWindow'
def init_audio(self):
self.sound_theme = SoundLoader.load('Bg_theme.mp3')
self.sound_theme.volume = 1
self.sound_theme.loop = True
if self.sound_theme:
self.sound_theme.play()
print('okay')
else:
print('not okay')
class SecondWindow(Screen):
def __init__(self, **kwargs):
super(SecondWindow, self).__init__(**kwargs)
self.color = [254 / 255, 102 / 255, 37 / 255, 1]
if platform in ('linux', 'win', 'macosx'):
with self.canvas.before:
self.bg = Rectangle(size=self.size, color=self.color)
class LabApp(App):
def build(self):
return Dre()
if __name__ == '__main__':
LabApp().run()```
if you provide any solution please provide it in .py file.
Many issues with your code.
First, you have defined a ScreenManager class (ThirdWindow), but you have not created an instance of it or included it in your GUI. In order to use a ScreenManager to manage Screens, you must add it to your GUI. I would suggest modifying your LabApp class to use ThirdWindow:
class LabApp(App):
def build(self):
return ThirdWindow()
and modify ThirdWindow to replace the load() method with an __init__() method:
class ThirdWindow(ScreenManager):
def __init__(self, **kwargs):
super(ThirdWindow, self).__init__(**kwargs)
self.add_widget(Dre(name='Dre'))
self.add_widget(SecondWindow(name='SecondWindow'))
Since the Dre class is being used as a Screen, it must be redefined as a Screen:
class Dre(Screen):
and you can add the callback to the creation of the B1 Button:
B1 = Button(text='P L A Y', font_name='Roboto-Bold.ttf', size_hint=(.2, .15), on_release=self.callback,
pos_hint={'center_x': .5, "center_y": .3}, background_color = self.color, background_normal='')

How to store to list in kivy?

I am making kivy app, something like project manager. I've got problem, i'll try to describe it.
In my app you can create new "project" by pressing button, then open the project and create some task. I need to store "project" into list or dictioniares. I've been thinking about this problem whole week and I can't solve. Do you have any ideas? I include my py. file and my kv. file here.
If you have some improvments to my codes, come here with them.
My python file:
from kivy.app import App
import sys
from kivy.storage.jsonstore import JsonStore
from kivy.uix.screenmanager import ScreenManager,Screen, SlideTransition, RiseInTransition
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.checkbox import CheckBox
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.behaviors import ButtonBehavior
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.image import Image
from kivy.uix.checkbox import CheckBox
from kivy.core.window import Window
from kivy.graphics import Color, Ellipse, Rectangle, Line
from kivy.uix.spinner import Spinner
from kivy.uix.stencilview import StencilView
Window.size = (400, 650)
Window.clearcolor = (0,0,0,0)
kv = Builder.load_file('main.kv')
c = 0
class MS(Screen, Widget):
def on_exit(self):
sys.exit()
class SES(Screen):
pass
class OPS(Screen, BoxLayout):
themes = ["DARK","LIGHT"]
def __init__(self, **kwargs):
super(OPS, self).__init__(**kwargs)
label = Label(text="COLOR THEME:", size_hint=(.2,.1), pos_hint={"x":.25,"top":.9})
self.add_widget(label)
tgbt = ToggleButton(text="DARK", size_hint=(.2, .1), pos_hint={"x":.5,"top":.9},background_normal="",background_color =(0,0,0,0))
self.add_widget(tgbt)
tgbt2 = ToggleButton(text="LIGHT", size_hint=(.2, .1), pos_hint={"x": .7, "top": .9},background_normal="", background_color= (1,1,1,1))
self.add_widget(tgbt2)
class PRS(Screen, BoxLayout):
widgets1 = []
l1x = .1
l1top = .87
txt1x = .4
txt1top = .88
ck1x = .64
ck1top = .88
chtop = 0.88
deltop = 0.88
projects = []
def changer(self,*args):
self.manager.current = 'task'
def changer2(self, *args):
self.manager.current = 'main'
def new_pr(self, *args):
txt1 = TextInput(multiline=False,size_hint=(0.3,0.05), pos_hint={'x':self.txt1x,"top":self.txt1top})
self.add_widget(txt1)
label1 = Label(text="YOUR PROJECT NAME:",size_hint=(0.2,0.04),pos_hint={"x":self.l1x,"top":self.l1top})
self.add_widget(label1)
check1 = CheckBox(active=False, size_hint=(0.2,0.05), pos_hint={"x":self.ck1x,"top":self.ck1top})
self.add_widget(check1)
print(self.projects)
self.widgets1.append(check1)
self.widgets1.append(label1)
self.widgets1.append(txt1)
self.l1top = self.l1top - .07
self.txt1top = self.txt1top - .07
self.ck1top = self.ck1top - .07
spinner = Spinner(text="OPTIONS", values="DELETE",background_normal="",background_color =(200/255.0, 194/255.0, 136/255.0,1))
button = Button(text="OPEN",color=(200 / 255.0, 194 / 255.0, 136 / 255.0, 1), size_hint=(0.1,0.05), pos_hint={"x":0.77, "top":self.chtop})
self.add_widget(button)
self.widgets1.append(button)
button2 = Button(text = "DELETE",color=(200 / 255.0, 194 / 255.0, 136 / 255.0, 1), size_hint=(0.13,0.05), pos_hint={"x":0.87,"top":self.deltop})
self.add_widget(button2)
self.widgets1.append(button2)
self.chtop = self.chtop - 0.07
self.deltop = self.deltop - 0.07
button.bind(on_press= self.changer)
button2.bind(on_release= self.remover)
def remover(self, *args):
for i in self.widgets1:
self.remove_widget(i)
self.l1top = .88
self.deltop = .88
self.chtop = .88
self.ck1top = .88
self.txt1top = .88
def __init__(self, **kwargs):
super(PRS, self).__init__(**kwargs)
bt1 = (Button(text="NEW PROJECT",pos_hint={"x":.0,"top":1},size_hint=(.5,.1)))
self.add_widget(bt1)
bt2 = (Button(text="BACK",pos_hint={"x":.5,"top":1},size_hint=(.5,.1)))
self.add_widget(bt2)
bt1.bind(on_release=self.new_pr)
bt2.bind(on_release=self.changer2)
def clear_screen(self):
self.canvas.clear()
class TASKS(Screen, BoxLayout):
task1 = []
tinpx = 0.25
tinptop = .83
chkx = 0.76
chktop = .84
showed = False
def note(self, *args):
self.showed = True
if self.showed == True:
textinput = TextInput(multiline=True,size_hint=(.4,.5), pos_hint={"x":0.56,"top":self.chktop-0.1})
self.add_widget(textinput)
while self.showed == False:
self.remove_widget(textinput)
break
def change(self, *args):
self.showed = False
def changer(self,*args):
self.manager.current = 'project'
def new_task(self, *args):
txt1 = TextInput(multiline=False, size_hint=(.2,.04), pos_hint={"x":self.tinpx, "top":self.tinptop})
self.add_widget(txt1)
chck1 = CheckBox(active=False, size_hint = (.2, .05), pos_hint = {"x":self.chkx,"top":self.chktop})
self.add_widget(chck1)
notes = Button(text="NOTES", color=(200 / 255.0, 194 / 255.0, 136 / 255.0, 1), size_hint=(.2,.05), pos_hint={"x":0.56,"top":self.chktop})
notes.bind(on_press=self.note)
notes.bind(on_release=self.change)
self.add_widget(notes)
self.tinptop = self.tinptop - .07
self.chktop = self.chktop - .07
def __init__(self, **kwargs):
super(TASKS, self).__init__(**kwargs)
self.add_widget(Label(text="TASK NAME", size_hint=(.2,.04), pos_hint={"x":0.25,"top":.88}))
self.add_widget(Label(text="COMPLETE", size_hint = (.2,.04), pos_hint={"x":0.75,"top":.88}))
bt1 = Button(text="NEW TASK",color=(0,0,0),size_hint=(.5,.1), pos_hint = {"x":0, "top":1},background_normal="",background_color =(200/255.0, 194/255.0, 136/255.0,1))
self.add_widget(bt1)
homebt = Button(text="BACK",color=(0,0,0),size_hint=(.5, .1), pos_hint={"x":0.5,"top":1},background_normal="",background_color =(200/255.0, 194/255.0, 136/255.0,1))
self.add_widget(homebt)
homebt.bind(on_release=self.changer)
bt1.bind(on_release=self.new_task)
class Help(Screen, Widget):
pass
class MApp(App):
title = "PROJECT MANAGER"
def build(self):
wid = PRS(size_hint=(None, None), size=Window.size)
sm = ScreenManager()
sm.add_widget(MS(name = "main"))
sm.add_widget(SES(name = "second"))
sm.add_widget(OPS(name = "options"))
sm.add_widget(PRS(name="project"))
sm.add_widget(TASKS(name="task"))
sm.add_widget(Help(name="help"))
return sm
if __name__ == "__main__":
MApp().run()
My kv. file:
<MS>:
GridLayout:
cols:7
GridLayout:
cols:2
Button:
text : "NEW"
color:0,0,0
on_release:app.root.current="project"
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
Button:
text : "OPTIONS"
color:0,0,0
on_release:app.root.current="options"
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
Button:
text : "LOAD"
color:0,0,0
on_release:app.root.current="second"
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
Button:
text : "HELP"
color:0,0,0
on_release:app.root.current="help"
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
Button:
text : "Next"
color:0,0,0
on_release:app.root.current="second"
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
Button:
id:bt1
text : "Exit"
color:0,0,0
background_normal:""
background_color:(200/255.0, 194/255.0, 136/255.0,1)
on_release:app.stop()
<SES>:
name:"second"
Button:
text:"back"
color:0,0,0
on_release:
app.root.current="main"
<OPS>:
name:"options"
BoxLayout:
Button:
size_hint:.5,.2
pos_hint : {"x":.5,"top":.15}
text: "HOME"
color:0,0,0
on_release:app.root.current="main"
<PRS>:
name:"project"
<TASKS>:
name:"task"
<Help>:
name:"help"
GridLayout:
Label:
text:"COMING SOON!!!"
color:0,0,0
Thaks for help, time and effort.
Micheno
Here is simple example of my problem.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class Test(BoxLayout):
x1 = 0.2
top1 = 0.9
widgets = []
def adder(self, *args):
lb1 = Label(text="HELLO WORLD", pos_hint={"x":self.x1,"top":self.top1})
self.add_widget(lb1)
self.x1 = self.x1 - 0.05
self.top1 = self.top1 - 0.05
self.widgets.append(lb1)
def remover(self, *args):
for i in self.widgets:
self.remove_widget(i)
self.x1=0.2
self.top1 = 0.9
def __init__(self, **kwargs):
super(Test, self).__init__(**kwargs)
bt1 = Button(text="ADD", pos_hint={"x":.1,"top":1}, size_hint=(0.2,0.4))
self.add_widget(bt1)
removebt = Button(text = "REMOVE",pos_hint={"x":.4,"top":1}, size_hint=(0.2,0.4))
self.add_widget(removebt)
removebt.bind(on_release=self.remover)
bt1.bind(on_release=self.adder)
class APP(App):
def build(self):
return Test()
if __name__ == "__main__":
APP().run()
If i want to delete only one label it will delete all. Just try to copy and run it
Just remove the last Label in the list:
def remover(self, *args):
if len(self.widgets) > 0:
self.remove_widget(self.widgets.pop(-1))
OK i could delete last that's improvement, but my problem is like this. What if I with button "ADD" I add text and button to remove the text and the button itself. Here is my new example with yours improvment.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
Window.size = (400, 650)
class Test(BoxLayout):
x1 = 0.1
top1 = 0.9
widgets = []
def adder(self, *args):
lb1 = Label(text="HELLO WORLD", pos_hint={"x":self.x1,"top":self.top1})
self.add_widget(lb1)
removebt = Button(text = "REMOVE",pos_hint={"x":self.x1-0.1,"top":self.top1}, size_hint=(0.5,0.1))
self.add_widget(removebt)
self.widgets.append(removebt)
removebt.bind(on_release=self.remover)
self.widgets.append(lb1)
self.top1 = self.top1 - 0.05
def remover(self, *args):
if len(self.widgets) > 0:
self.remove_widget(self.widgets.pop(-1))
self.x1=0.2
self.top1 = 0.9
def __init__(self, **kwargs):
super(Test, self).__init__(**kwargs)
bt1 = Button(text="ADD", pos_hint={"x":.1,"top":1}, size_hint=(1,0.4))
self.add_widget(bt1)
bt1.bind(on_release=self.adder)
class APP(App):
def build(self):
return Test()
if __name__ == "__main__":
APP().run()

Passing "on_start data" to MyScreen and MyImage requires Screen name in ScreenManager

I am using Kivy library to create an app using mainly Python code. I am newbie in Python and Kivy. Here some snippets of my code.
I was trying to pass an image defined "on_start" to my NavButton. #ikolim helped me out to identify the issue. You can see my comments below.
Widget
class NavButton(GridLayout):
def __init__(self, **kwargs):
super(NavButton, self).__init__(**kwargs)
self.rows = 1
frame = FloatLayout(id = "frame")
grid1 = MyGrid(
rows = 1,
cols = 5,
pos_hint = {"top":1, "left": 1},
size_hint = [1, .2] )
grid1.set_background(0.95,0.95,0.95,1)
grid1.add_widget(MyButton(
source = "img/settings.png",
size_hint = [0.2,0.2] ))
grid1.add_widget(MyButton(
source = "img/arrow-left.png" ))
city_icon = Image(
source="img/image1.png",
id="city_icon",
size_hint=[0.8, 0.8])
self.ids.cityicon = city_icon
grid1.add_widget(city_icon)
grid1.add_widget(MyButton(
source = "img/arrow-right.png" ))
grid1.add_widget(MyButton(
source = "img/globe.png",
size_hint = [0.2,0.2] ))
frame.add_widget(grid1)
self.add_widget(frame)
Screens
class Homescreen(Screen):
def __init__(self, **kwargs):
super(Homescreen, self).__init__(**kwargs)
frame = FloatLayout(id = "frame")
grid1 = NavButton()
frame.add_widget(grid1)
...
Screen Manager
Here is where I was messing up! Apparently you have to define the name of your homescreen (i.e.homescreen = Homescreen(name="home_screen")) otherwise it does not update the image of the NavButton when you start the application. I am not sure why but I just what to highlight this for future coders.
class MyScreenManager(ScreenManager):
def __init__(self, **kwargs):
super(MyScreenManager, self).__init__(**kwargs)
homescreen = Homescreen()
self.add_widget(homescreen)
self.ids.screenmanager = self
def change_screen(self, name):
self.current = name
Builder
GUI = Builder.load_file("main.kv")
class Main(App):
def build(self):
return GUI
def on_start(self):
self.mynewimage = "image2"
homescreen = self.root.get_screen("home_screen")
homescreen.ids.navbutton.ids.cityicon.source = f"img/{self.mynewimage}.png"
if __name__ == "__main__":
Main().run()
Again, #ikolim thanks for your support.
Solution 2
The following enhancements are required to solve the problem.
class NavButton()
Replace self.ids.cityicon = "cityicon"
with self.ids.cityicon = city_icon
Delete self.bind(on_start=self.update_image) and method update_image()
Method on_start()
Replace homescreen =
Main.get_running_app().root.get_screen("home_screen") with
homescreen = self.root.get_screen("home_screen")
Replace
frame.children[3].children[0].children[0].children[2].source with
homescreen.ids.navbutton.ids.cityicon.source
Remove lines (frame = homescreen.children[0], navbutton =
frame.children[3], and print(...))
Snippets - py file
class NavButton(GridLayout):
def __init__(self, **kwargs):
super(NavButton, self).__init__(**kwargs)
...
city_icon = Image(
source="img/settings.png",
id="city_icon",
size_hint=[0.8, 0.8])
self.ids.cityicon = city_icon
...
class Main(App):
...
def on_start(self):
self.my_city = "it-Rome"
homescreen = self.root.get_screen("home_screen")
homescreen.ids.navbutton.ids.cityicon.source = f"img/{self.my_city}.png"
...
Example - main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import ButtonBehavior
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.graphics import Color, Rectangle
class MyButton(ButtonBehavior, Image):
pass
class ImageButton(ButtonBehavior, Image):
pass
### Components
class NavButton(GridLayout):
def __init__(self, **kwargs):
super(NavButton, self).__init__(**kwargs)
self.rows = 1
frame = FloatLayout(id="frame")
grid1 = MyGrid(
rows=1,
cols=5,
pos_hint={"top": 1, "left": 1},
size_hint=[1, .2])
grid1.set_background(0.95, 0.95, 0.95, 1)
grid1.add_widget(MyButton(
source="img/settings.png",
size_hint=[0.2, 0.2]))
grid1.add_widget(MyButton(
source="img/arrow-left.png"))
city_icon = Image(
source="img/settings.png",
id="city_icon",
size_hint=[0.8, 0.8])
self.ids.cityicon = city_icon
grid1.add_widget(city_icon)
grid1.add_widget(MyButton(
source="img/arrow-right.png"))
grid1.add_widget(MyButton(
source="img/globe.png",
size_hint=[0.2, 0.2]))
frame.add_widget(grid1)
self.add_widget(frame)
### Grids
class MyGrid(GridLayout):
def set_background(self, r, b, g, o):
self.canvas.before.clear()
with self.canvas.before:
Color(r, g, b, o)
self.rect = Rectangle(pos=self.pos, size=self.size)
self.bind(pos=self.update_rect,
size=self.update_rect)
def update_rect(self, *args):
self.rect.pos = self.pos
self.rect.size = self.size
### Screens
class Homescreen(Screen):
def __init__(self, **kwargs):
super(Homescreen, self).__init__(**kwargs)
frame = FloatLayout(id="frame")
grid1 = NavButton()
frame.add_widget(grid1)
# This grid contains the number of zpots collected
grid2 = MyGrid(
cols=1,
pos_hint={"top": 0.8, "left": 1},
size_hint=[1, .2], )
grid2.add_widget(Image(
source="img/spot.png"))
grid2.add_widget(Label(
text="[color=3333ff]20/30[/color]",
markup=True))
grid2.set_background(0.95, 0.95, 0.95, 1)
frame.add_widget(grid2)
# This grid contains a scrollable list of nearby zpots
grid3 = MyGrid(
cols=1,
pos_hint={"top": 0.6, "left": 1},
size_hint=[1, .5])
grid3.set_background(0.95, 0.95, 0.95, 1)
frame.add_widget(grid3)
# This grid contains a the map of nearby zpots
grid4 = MyGrid(
cols=1,
pos_hint={"top": 0.1, "left": 1},
size_hint=[1, .1])
grid4.set_background(0.95, 0.95, 0.95, 1)
frame.add_widget(grid4)
self.ids.navbutton = grid1
self.add_widget(frame)
class Newscreen(Screen):
pass
class Settingscreen(Screen):
pass
### ScreenManager
class MyScreenManager(ScreenManager):
def __init__(self, **kwargs):
super(MyScreenManager, self).__init__(**kwargs)
homescreen = Homescreen(name='home_screen')
self.add_widget(homescreen)
self.ids.screenmanager = self
def change_screen(self, name):
self.current = name
class TestApp(App):
def build(self):
return MyScreenManager()
def on_start(self):
self.my_city = "it-Rome"
homescreen = self.root.get_screen("home_screen")
print(f"\non_start-Before change: img={homescreen.ids.navbutton.ids.cityicon.source}")
homescreen.ids.navbutton.ids.cityicon.source = f"img/{self.my_city}.png"
print(f"\non_start-After change: img={homescreen.ids.navbutton.ids.cityicon.source}")
if __name__ == "__main__":
TestApp().run()
Output
Solution 1
Use App.get_running_app() function to get an instance of your application
Add self in-front of my_city
Snippets - py file
class NavButton(GridLayout):
def get(self):
...
city_icon = Image(
source = "img/" + App.get_running_app().my_city,
size_hint = [0.8,0.8] )
...
class Main(App):
def build(self):
return GUI
def on_start(self):
# get data from DB
self.my_city = "it-Rome"

Animation subclass

in Animation class, how to set d = 0.4, t = 'in_out_quad' by default? Can I use subclass for that?
from kivy.app import App
from kivy.lang import Builder
from kivy.animation import Animation
KV = """
Label
text: '123'
on_touch_down: app.test()
"""
class MyApp(App):
def build(self):
self.root = Builder.load_string(KV)
def test(self):
a = Animation(x = 500, d = .2, t = 'in_out_quad')
a.start(self.root)
MyApp().run()
Tried something like this (with no success):
from kivy.app import App
from kivy.lang import Builder
from kivy.animation import Animation
KV = """
Label
text: '123'
on_touch_down: app.test()
<MyAnim>:
d: .2
t: 'in_out_quad'
"""
class MyAnim(Animation):
pass
class MyApp(App):
def build(self):
self.root = Builder.load_string(KV)
def test(self):
a = MyAnim(x = 500)
a.start(self.root)
MyApp().run()
I just want not to write the values of the arguments d and t many times if they are the same in my project
I haven't tested this, but I think you could just put those defaults in your class definition:
class MyAnim(Animation):
def __init__(self, **kw):
super(MyAnim, self).__init__(d = .2, t = 'in_out_quad', **kw)
Of course, if you use MyAnim and specify another t= or d=, you will get an error.
That error can be avoided by only adding the default values if they are not already specified:
class MyAnim(Animation):
def __init__(self, **kw):
if 'd' not in kw:
kw['d'] = 0.2
if 't' not in kw:
kw['t'] = 'in_out_quad'
super(MyAnim, self).__init__(**kw)

Categories