Follow up question: Kivy outside rule inherence
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.stacklayout import StackLayout
from kivy.properties import ObjectProperty
from kivy.factory import Factory
class FancyButton(Button):
imp = ObjectProperty(None)
class Important(StackLayout):
def NoInspiration(self, smile):
print("Received: {}".format(smile))
def AddFancy(self):
temp = Factory.FancyButton(text='f', imp = self.ids.imp)
self.ids.boxy.add_widget(temp)
class TestApp(App):
def build(self):
pass
if __name__ == '__main__':
TestApp().run()
test.kv
#:kivy 1.9.0
#:import App kivy.app
<FancyButton>:
on_release: self.imp.NoInspiration(':)')
<Important>:
id: imp
BoxLayout:
id: boxy
orientation: 'vertical'
FancyButton:
text: "smiley"
imp: root
Button:
text: "add fancy"
on_release: imp.AddFancy()
BoxLayout:
Important
In test.kv the function call in FancyButton to the function NoInspiration() works, because the FancyButton defined in the .kv has imp: root, so it knows it should look for the function in < Important >:.
Question
However how does imp: root work when you create FancyButton through add_widget in Python?
Now when I press the button "add fancy" I get the error:
File "main.py", line 18, in AddFancy
temp = Factory.FancyButton(text='f', imp = self.ids.imp)
File "properties.pyx", line 756, in kivy.properties.ObservableDict.__getattr __ (kivy/properties.c:11093)
AttributeError: 'super' object has no attribute '__getattr __'
Follow up Question
Kivy outside rule inherence 2
Widget.ids only contain ids of its children (http://kivy.org/docs/api-kivy.uix.widget.html#kivy.uix.widget.Widget.ids. Id of the widget itself it's not needed because you can just pass it directly - in your case using self, since you're passing a reference to a widget from inside of a method:
class Important(StackLayout):
def NoInspiration(self, smile):
print("Received: {}".format(smile))
def AddFancy(self):
print(self.ids) # only returns {'boxy': <weakproxy at 0000000002D119A8 to BoxLayout at 0000000002D026A8>}
self.ids.boxy.add_widget(FancyButton(text='f', imp = self)) # no need to use a factory
Related
I am able to get the data in main app class and run it to get as Label but when I want to do the same by getting data in main app class where as defining Label in another class, it's giving name error.
import kivy
from kivy.app import App # import Kivy App module to create
from kivy.uix.label import Label # import Label Module
kivy.require('1.11.1')
class MyKivyApp(App):
def build(self):
f=open('tesit.txt','r')
t=f.read()
return Label(text =t)
rt=MyKivyApp()
rt.run()
Your lines:
rt=MyKivyApp()
rt.run()
are inside the MyKivyApp. Just unindent those two lines to get them outside of the App class.
This's how we can read a text file and display it as label in another screen in kivy.
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
from kivy.properties import StringProperty
class Screen1(Screen):
f=open('test.txt','r')
t=f.readlines()
b=t[0]
f.close()
class Screen2(Screen):
username = StringProperty("")
root = Builder.load_string('''
<Screen1>:
name:'xy'
id:loginscreen
BoxLayout:
orientation: "vertical"
Button:
text:'Execution button'
on_release: root.manager.current = "screen2"
<Screen2>:
name: "screen2"
FloatLayout:
Label:
text: root.username
Button:
text:'Back to menu'
size_hint:.3,.3
on_release: root.manager.current = "xy"
ScreenManager:
Screen1:
id:loginscreen
Screen2:
username: loginscreen.b
''')
class MyApp(App):
def build(self):
return root
r=MyApp()
r.run()
I'm new to Kivy and I would have to think this is possible, but I can't figure it out - How can I update a Kivy label when a button is pressed, but only by referencing that Kivy id within Python? (The reason I'm trying to do it this way is because in my actual application, I would like several labels to update at once, which I was hoping I could do all within the button_pressed equivalent button I have in my app).
In the simple example below, I'm just trying to have the button pressed and then have the label update to 'Updated!'
Thanks very much!
My Python code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
import random
class TestingWidget(BoxLayout):
# This is the kv id of the Label I would like to update
label_to_update = StringProperty('')
# This is the action I would like to happen when the button is pressed
def button_pressed(self):
label_to_update.text = 'Updated!'
class TestButtonApp(App):
def build(self):
return TestingWidget()
if __name__ == '__main__':
TestButtonApp().run()
My kv file:
<TestingWidget>:
BoxLayout:
orientation: 'horizontal'
Button:
text: 'test'
on_press: root.button_pressed()
Label:
id: label_to_update
text: 'Trying to get this to update'
You definitely update all label when you press the button. Just crate a StringProperty for each and do what you are doing now.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.properties import StringProperty
from kivy.lang import Builder #used because I didn't want to create two files
import random
Builder.load_string('''
<TestingWidget>:
BoxLayout:
orientation: 'horizontal'
Button:
text: 'test'
on_press: root.button_pressed()
Label:
id: label_to_update
text: root.label_to_update
''')
class TestingWidget(BoxLayout):
# This is the kv id of the Label I would like to update
label_to_update = StringProperty('Trying to get this to update')
#default text set
# This is the action I would like to happen when the button is pressed
def button_pressed(self):
self.label_to_update = 'Updated!'
class TestButtonApp(App):
def build(self):
return TestingWidget()
if __name__ == '__main__':
TestButtonApp().run()
I have my python code:
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
class Premade(BoxLayout):pass
class MyFirstApp(App):
def build(self):
return Premade()
if __name__ == '__main__':
MyFirstApp().run()
and my .kv file:
#:kivy 1.10.0
<Premade>:
orientation: 'Verticle'
TextInput:
id: my_textinput
font_size: 150
size_hint_y: None
height: 200
text: 'default'
FloatLayout:
Scatter:
Label:
text: my_textinput.text
font_size: 150
Supposedly, when I run the python code, the .kv file would be loaded, but all I get is a black screen. I named my .kv file according to the rule, in this case, is myfirst.kv and the .kv file is also in the same directory as the python module file. I also tried to use the build function but that didn't work either. Can anyone help?
Check your .kv filename, if you want it to be loaded automatically you should name it MyFirst.kv because your app is called MyFirstApp. An alternative solution would be to load the file manually as the following:
from kivy.lang import Builder
Builder.load_file('filename.kv')
Has to be called myfirstapp.kv or the same name as the app class
This makes two tabs for each TabbedPanelItem plus the default tab. Why does this happen and how do I prevent it?
Kivy:
:
TabbedPanelItem:
text: 'List'
TabbedPanelItem:
text: 'Add/Edit'
TabbedPanelItem:
text: 'Delete'
Python:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.tabbedpanel import TabbedPanel
presentation = Builder.load_file('my.kv')
class Panel(TabbedPanel):
pass
class MyApp(App):
def build(self):
return Panel()
if __name__ == '__main__':
MyApp().run()
Your kv file is being loaded twice, once by your explicit Builder.load_file and once implicitly because it has the same name as your App class (but lowercase and without the App, as expected for the default kv file to be loaded).
Remove the explicit Builder.load_file and it should work.
I'm trying to get my head around screenmanager, in particular referencing objects within.
I used to use this to set a value:
class Widgets(Widget)
pass
w = Widgets()
w.ids.MyTitle.text = 'something'
Now I have this:
class Widgets(Screen)
pass
class SettingsScreen(Screen)
pass
sm = ScreenManager()
sm.add_widget(Widgets(name='main'))
sm.add_widget(SettingsScreen(name='settings'))
How do I reference MyTitle now? I've tried all sorts of combos such as:
sm.ids.main.MyTitle.text =
sm.main.MyTitle.text =
sm.main.ids.MyTitle.text =
.... but not getting it! Can someone put me out of my misery? Is there an easy way of browsing through the sm object or iterating through it maybe?
EDIT: Adding minimal running version:
minimal.kv:
# File name: minimal.py
#:kivy 1.8.0
<Widgets>
Button:
id: MyTitle
text: 'hello'
<SettingsScreen>:
Button:
id: Other
text: 'settings'
minimal.py:
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.app import App
class Widgets(Screen):
pass
class SettingsScreen(Screen):
pass
class myApp(App):
def build(self):
return sm
def on_start(self):
global sm
sm.ids['main'].ids['MyTitle'].text = 'changed' # <-- this fails
Builder.load_file("minimal.kv")
sm = ScreenManager()
sm.add_widget(Widgets(name='main'))
sm.add_widget(SettingsScreen(name='settings'))
if __name__ == '__main__':
myApp().run()
To get a screen from ScreenManager, use get_screen:
sm.get_screen('main').ids.MyTitle.text = 'changed'
Also, you may construct your app so:
kv file:
# File name: minimal.py
#:kivy 1.8.0
ScreenManager:
Widgets:
name: 'main'
SettingsScreen:
name: 'settings'
<Widgets>:
Button:
id: MyTitle
text: 'hello'
<SettingsScreen>:
Button:
id: Other
text: 'settings'
and in the python file:
sm=Builder.load_file(..)
class my12App(App):
def build(self):
return sm
def on_start(self):
self.root.get_screen('main').ids.MyTitle.text = 'changed'
According to the documentation you access an id like you would any dictionary key:
widget.ids['MyTitle']
Because ScreenManager in itself derives from Widget, and a given widget maintains a list of widgets it is aware of, you probably need something like:
sm.ids[0].ids['MyTitle'].text
However this is hard to say without a Minimal, Complete, and Verifiable Example. One thing you could do is:
for id in sm.ids: # Iterate through all widgets in ids
print(id) # Get the string representation of that widget
As a side note, this:
class Widgets(Screen)
pass
... will probably cause confusion, because you're extending Widget with Widgets (via an intermediate class Screen). OOP suggests that a subclass of a class should be a more specific form of the class. So, a Screen is a type of Widget. But Widgets is really some number of Widgets.
Unfortunately neither the code in the question, nor the one in the accepted answer worked for me, and I'm unsure as to how exactly it is supposed to work. My interpretation of it is, that OP wanted to change a property of a widget (a Button) inside of one Screen based on a callback from another Screen. The below code is a complete MWE doing that:
# File name: minimal.py
#:kivy 2.0.0
ScreenManager:
FoobarScreen:
name: 'foobar'
Widgets:
name: 'widgets'
<FoobarScreen>:
Button:
id: lalala
text: 'lalala'
<Widgets>:
Button:
id: lololo
text: 'lololo'
(minimal.kv)
import kivy
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.app import App
class FoobarScreen(Screen):
pass
class Widgets(Screen):
pass
class myApp(App):
def build(self):
return sm
def on_start(self):
self.root.get_screen('widgets').ids.lololo.text = 'changed'
self.root.current = 'widgets'
if __name__ == '__main__':
sm=Builder.load_file('minimal.kv')
myApp().run()
(minimal.py)