How do I set Id of widget inside python file? - python

I am trying to change the text of an MDLabel inside one screen from another screen. I can reference the screen itself but since I'm not using kv language I can't set Id of the MDLabel I'm trying to reference.
Is there a way to set Id of an MDLabel from within Python and add it to self.ids of the screen it's part of?
-Or. Is there another way to reference widgets of another screen?
My code:
def main():
class HomeScreen(Screen, GridLayout, MDApp):
def __init__(self, **kwargs):
super(HomeScreen, self).__init__(**kwargs)
self.add_widget(MDRaisedButton(text='Read', size_hint=(.3, .2), font_size='30sp', on_press=lambda x:self.changerReadMail()))
def changerReadMail(self, *args):
self.manager.transition.direction = 'right'
# It's here I want to change the text of label inside the ReadMail class.
self.manager.current = 'read'
class ReadMail(Screen, FloatLayout, MDApp):
def __init__(self, **kwargs):
super(ReadMail, self).__init__(**kwargs)
label = (MDLabel(text='hej'))
self.add_widget(label)
self.add_widget(MDFillRoundFlatButton(text='Back', font_size='20sp', size_hint=(.1,.1), pos_hint={'x':.01, 'y':.02}, on_press=lambda x:self.changerInbox()))
def changerInbox(self, *args):
self.manager.transition.direction = 'left'
self.manager.current = 'home'
class KivyApp(MDApp):
def build(self):
Window.size = (1000, 600)
self.sm = ScreenManager()
self.sm.add_widget(HomeScreen(name='home'))
self.sm.add_widget(ReadMail(name='read'))
self.sm.current = 'home'
return self.sm
KivyApp().run()
if __name__ == '__main__':
main()

I solved this one!
Here's my solution (See the line commented with "This is a new line"):
import weakref #This is a new line
def main():
class HomeScreen(Screen, GridLayout, MDApp):
def __init__(self, **kwargs):
super(HomeScreen, self).__init__(**kwargs)
self.add_widget(MDRaisedButton(text='Read', size_hint=(.3, .2), font_size='30sp', on_press=lambda x:self.changerReadMail()))
def changerReadMail(self, *args):
self.manager.transition.direction = 'right'
self.manager.get_screen('read').ids.test.text = 'test' #This is a new line
self.manager.current = 'read'
class ReadMail(Screen, FloatLayout, MDApp):
def __init__(self, **kwargs):
super(ReadMail, self).__init__(**kwargs)
label = (MDLabel())
self.ids['test'] = weakref.ref(label) #This is a new line
self.add_widget(label)
self.add_widget(MDFillRoundFlatButton(text='Back', font_size='20sp', size_hint=(.1,.1), pos_hint={'x':.01, 'y':.02}, on_press=lambda x:self.changerInbox()))
def changerInbox(self, *args):
self.manager.transition.direction = 'left'
self.manager.current = 'home'
class KivyApp(MDApp):
def build(self):
Window.size = (1000, 600)
self.sm = ScreenManager()
self.sm.add_widget(HomeScreen(name='home'))
self.sm.add_widget(ReadMail(name='read'))
self.sm.current = 'home'
return self.sm
KivyApp().run()
if __name__ == '__main__':
main()

Related

It is necessary to transfer to the SecondScreen class, the text that the button_press function returns from the ScreenMain class

Please help, I'm trying to make an application for a child to learn the alphabet, I'm just learning programming and working with classes.
I need to pass to the SecondScreen class the text that the button_press function returns from the ScreenMain class.
`
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from itertools import chain
from kivy.uix.screenmanager import ScreenManager, Screen
class ScreenMain(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.alphabet = [chr(i) for i in chain(range(1040, 1046), range(1025, 1026), range(1046, 1069), range(32, 33),
range(1069, 1072))]
gr = GridLayout(cols=5, padding=[35], spacing=3)
for i in self.alphabet:
gr.add_widget(Button(text=i, on_press=self.button_press))
self.add_widget(gr)
def button_press(self, instance):
self.manager.transition.direction = 'left'
self.manager.current = 'second_screen'
print(instance.text) # I output to the console, everything is ok
return instance.text
class SecondScreen(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
get = ScreenMain.button_press # I'm trying to pass a letter from a function to a class button_press
layout = GridLayout(cols=5, rows=5, padding=[35], spacing=10)
layout.add_widget(Button(
text=str(get))) # Trying to create a button on the second page with the text of the letter pressed on the first screen
self.add_widget(layout)
def _on_press_button_new_layout(self, *args):
self.manager.transition.direction = 'right'
self.manager.current = 'main_screen'
class MyApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(ScreenMain(name='main_screen'))
sm.add_widget(SecondScreen(name='second_screen'))
return sm
if __name__ == '__main__':
MyApp().run()
`

Use button in screen 1 to change label text in screen 2 in Kivy

I've been trying to change the label2 text in layout 2, with button1 in layout1, but it doesn't seem to work, when I press the button nothing happens
Here's the code:
class layout1(GridLayout):
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.cols = 2
self.button1 = Button(text = "Button 1 changes screen 2", on_press = self.change_label)
self.add_widget(self.button1)
self.change_button = Button(text = "move to screen 2", on_press = self.change_screen)
self.add_widget(self.change_button)
def change_screen(self, instance):
practice_app.sm.current = "screen2"
def change_label(self,instance):
func_layout = layout2()
func_layout.label2.text = "changed"
class layout2(GridLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2
self.label2 = Label(text = "this should change")
self.add_widget(self.label2)
class TestApp(App):
def build(self):
self.sm = ScreenManager()
screen1 = Screen(name = "screen1")
screen1.add_widget(layout1())
self.sm.add_widget(screen1)
screen2 = Screen(name = "screen2")
screen2.add_widget(layout2())
self.sm.add_widget(screen2)
return self.sm
if __name__ == "__main__":
practice_app = TestApp()
practice_app.run()
There are many ways to do what you want. Since you are not using kv, perhaps the easiest way is to save a reference to layout2. Here is a modified version of your build() method that does that:
class TestApp(App):
def build(self):
self.sm = ScreenManager()
screen1 = Screen(name = "screen1")
screen1.add_widget(layout1())
self.sm.add_widget(screen1)
screen2 = Screen(name = "screen2")
self.layout2 = layout2() # save reference to layout2
screen2.add_widget(self.layout2)
self.sm.add_widget(screen2)
return self.sm
And then, use that reference in the change_label() method:
def change_label(self,instance):
# func_layout = layout2() # creates a new instance of layout2 (not the one in the GUI)
func_layout = App.get_running_app().layout2
func_layout.label2.text = "changed"

Problems with ScreenManager and Screen in Python (kivy). I want to call on def in Screen from the Screenmanager class

I am trying to call on def in class Screen from Screenmanager from the class Screenmanager but I can't.
I want to make a box layout and put a button in that boxlayout when the app is started as soon as ScreenManager is called so it is the default screen.
I want to do this so I can switch between multiple screens using the self.parent.current = 'Screen Name' on the press of a button
Here is the code
class Manager(ScreenManager):
def __init__(self):
self.add_widget(Scr1().make_button())
class Scr1(Screen):
def __init__(self):
self.layout = BoxLayout(orientation = 'vertical')
self.button = Button(text='hello')
self.layout.add_widget(self.button)
def make_button(self, layout):
return self.layout
class MainApp(App):
def build(self):
sm =Manager()
s1 = Scr1()
sm.add_widget=s1
return sm
if __name__ == "__main__":
MainApp().run()
Here is the error I get
Traceback (most recent call last):
File "C:/Users/user/Desktop/CarsonMusic/Learn.py", line 43, in <module>
MainApp().run()
File "C:\Users\user\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\app.py", line 829, in run
root = self.build()
File "C:/Users/user/Desktop/CarsonMusic/Learn.py", line 37, in build
sm =Manager()
File "C:/Users/user/Desktop/CarsonMusic/Learn.py", line 24, in __init__
self.add_widget(Scr1().make_button())
File "C:\Users\user\AppData\Local\Programs\Python\Python37-32\lib\site-packages\kivy\uix\screenmanager.py", line 979, in add_widget
'ScreenManager accepts only Screen widget.')
kivy.uix.screenmanager.ScreenManagerException: ScreenManager accepts only Screen widget.
I know I am probably making a dumb mistake but can someone help me?
I solved it! I had to delete the Manager(ScreenManager) class, added the 'super', and then I added the layout widget to self
Here is my new code:
class Scr1(Screen):
def __init__(self, **kwargs):
super (Scr1, self).__init__(**kwargs)
self.layout = BoxLayout(orientation='vertical')
self.button = Button(text='hello')
self.button.bind(on_press=self.screen2)
self.layout.add_widget(self.button)
self.add_widget(self.layout)
def screen2(self, *args):
self.manager.current = 'screen2'
class Scr2(Screen):
def __init__(self, **kwargs):
super (Scr2,self).__init__(**kwargs)
self.layout2 = BoxLayout(orientation='vertical')
self.add_widget(self.layout2)
self.button2 = Button(text='hello2')
self.button2.bind(on_press=self.screen1)
self.layout2.add_widget(self.button2)
def screen1(self, *args):
self.manager.current = 'screen1'
class MainApp(App):
def build(self):
sm= ScreenManager()
screen1 = Scr1(name='screen1')
screen2= Scr2(name='screen2')
sm.add_widget(screen1)
sm.add_widget(screen2)
return sm
if __name__ == "__main__":
MainApp().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"

Can't split my kivy programm into different files

I wanted to make an kivy game with an enemy class.
In order to make it more easy to understand, I want to make an own file for my Enemy class, called enemy.py.
But I get the error: kivy.factory.FactoryException: Unknown class <Level>
Here is the code of my main file:
from enemy import *
class Level(Widget):
def __init__(self, **kwargs):
super(Level, self).__init__(**kwargs)
self.l_Clock = Clock
self.l_Clock.schedule_interval(self.Update, 2/1.)
def Update(self, *args):
self.add_widget(Enemy(pos=(800, 300)))
root = Builder.load_file('main.kv')
class app(App):
def build(self):
Window.clearcolor = (1, 1, 1, 1)
return root
if __name__ == "__main__":
app().run()
Here is my main.kv file:
FloatLayout:
canvas.before:
Color:
rgba: 1, 1, 1, 1
Level:
id: level
And here is my enemy.py file:
root = Builder.load_file('main.kv')
class Sprite(Image):
def __init__(self, **kwargs):
super(Sprite, self).__init__(**kwargs)
self.size = self.texture_size
class Enemy(Widget):
droga = StringProperty('feind.png')
velocity = ListProperty([1, 0])
def __init__(self, **kwargs):
super(Enemy, self).__init__(**kwargs)
Clock.schedule_interval(self.Update, 1/60.)
self.skin = Sprite(source=self.droga)
self.add_widget(self.skin)
def Update(self, *args):
self.x -= self.velocity[0]
if self.x < 1:
self.velocity[0] = 0
def on_touch_down(self, touch):
obj = root.ids.level
obj.remove_widget(self)
Why can't my program find the Enemy Widget?

Categories