Why is this code is making two copies of the widgets? - python

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.

Related

Kivy showing a black screen when trying to use kv file?

So I want to do the styling of my Kivy app in a external .kv file, but when I run the main code, nothing appears to the black Kivy window.
Here's my main file called main.py
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
class MyGrid(Widget):
pass
class MyApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
MyApp().run()
And here's the .kv file located in the same directory and called my.kv
#:kivy 2.0.0
<MyGrid>:
Label:
text: "example example"
So I'm not getting any error, just nothing appearing in the Kivy GUI when I run the main code.
Why is this and how to fix it?
In order to load widgets from a separate kivy file, you need to import Builder:
from kivy.lang.builder import Builder
Builder.load_file('my.kv')
or in .py file
Builder.load_string("""
<MyGrid>:
Label:
text: "example example"
""")
The kivy file name should always be the App class name, in your case you should save file with MyApp.kv
else you need to use Builder to import

Kivy Spinner adds a widget that is not codded

Can someone explain why below code adds this Close Button on the bottom of the window you can see on the pic? How to get rid of this?
CODE
from kivy.app import App
from kivy.lang import Builder
kv = '''
<MenuSpinner#Spinner>
<Menu#BoxLayout>
orientation: 'vertical'
MenuSpinner:
text: 'SPINNER'
values: ['1', '2']
BoxLayout:
Menu:
'''
sm = Builder.load_string(kv)
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
The button is coming from the MenuSpinner, which is already present in Kivy (mostly internal, used by kivy.uix.settings). That class should probably be named differently on the Kivy side, but in any case renaming your version should fix the problem.

Referencing ids within screenmanager object

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)

Understanding Kivy properities and binding methods

I am having problems understanding the usage of custom Properities and ways of binding methods to events.
Here's my code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.properties import StringProperty
kivy_lang = '''
<MainWidget>:
on_my_property: my_label.text = 'from button bind method via StringProperty' + my_property
Label:
id: my_label
text: root.my_property
Button:
id: my_button
text: 'intro button'
'''
class MainWidget(BoxLayout):
# bind some properties
my_property = StringProperty('0')
def __init__(self, **kwargs):
super(MainWidget, self).__init__(**kwargs)
# if needed to do sth on widget construction
self.ids.my_button.bind(on_press=self.my_method)
def my_method(self,*args,**kwargs):
self.my_property = str(int(self.my_property)+1)
self.ids.my_button.text = 'new'
class MyApp(App):
def build(self):
Builder.load_string(kivy_lang)
return MainWidget()
if __name__ == '__main__':
MyApp().run()
When I run it it renders OK, but when I click a button, as a result I get
NameError: name 'my_property' is not defined
I tried binding method for Button in kv lang with (and removing whole 'init()' on python side):
on_press: root.my_method
and then when I press button the app doesn't crash but nothing happens
Can someone explain me how to adjust this code to work?
I understand the code is a little 'mixed techniques' but I did it that way to get to know different approaches, so I would appreciate if You don't turn it all around :)
1/ you are missing 'self' before 'my_property' in 'on_my_property' bindind, hence the crash
2/ in kv bindings. the python code is called as written, so you need '()' after 'root.my_method', or the statement has no effect.

Kivy rule inherence with add_widget()

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

Categories

Resources