I'am new in Kivy and have follow problem (Environment is Python 3.7 with Kivy-1.11.1.):
I need a navigation area and a view area (=ViewScreen). With the navigation area i change the view area (change of kv-files - look later at 'def next_screen'). My problem is, that i can't interact with widgets (e.g. 'lblTest') in the view area.
I use follow files:
testGUI.py (= GUI Application)
testGUIRoot.kv (= RootWidget as kv-file)
testGUIBtn1.kv (= view area 1 as kv-file)
testGUIBtn2.kv (= view area 2 as kv-file)
The GUI Application is simple and starts the GUI and change the view area.
testGUI.py:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
class RootWidget(BoxLayout):
# runs select application
def startApplication (self, instance):
print(self.lblTest)
class mainGUI(App):
def build(self):
# loading the content of root.kv
self.root = Builder.load_file('testGUIRoot.kv')
def next_screen(self, screen):
#Clear container and load the given screen object from file in kv folder.
filename = screen + '.kv'
# unload the content of the .kv file
# reason: it could have data from previous calls
Builder.unload_file(filename)
# clear the container
self.root.container.clear_widgets()
# load the content of the .kv file
screen = Builder.load_file(filename)
# add the content of the .kv file to the container
self.root.container.add_widget(screen)
if __name__ == '__main__':
'''Start the application'''
mainGUI().run()
I use follow kv-files:
testGUIRoot.kv:
#:kivy 1.11.1
RootWidget:
container: container
orientation: 'horizontal'
# Navigation
BoxLayout:
orientation: 'vertical'
size_hint: (0.35, 1)
Button:
text: 'testButton1'
on_release: root.startApplication(self,)
on_press: app.next_screen('testGUIBtn1')
Button:
text: 'testButton2'
on_release: root.startApplication(self,)
on_press: app.next_screen('testGUIBtn2')
# ViewScreen
BoxLayout:
size_hint: (0.65, 1)
id: container
orientation: 'vertical'
padding: 0
spacing: 3
Label:
text: 'no data'
color: (0.667,0.667,0.667,1)
font_size: 14
bold: True
testGUIBtn1.kv:
#:kivy 1.11.1
Label:
id: lblTest
text: 'Button 1'
color: (0.667,0.667,0.667,1)
font_size: 14
bold: True
testGUIBtn2.kv:
#:kivy 1.11.1
Label:
id: lblTest
text: 'Button 2'
color: (0.667,0.667,0.667,1)
font_size: 14
bold: True
Follow error appears:
AttributeError: 'RootWidget' object has no attribute 'lblTest'
Have you a solution to interact with the Object 'lblTest'? For example like self.lblTest.text = 'Test-Text'.
Thank you in advance
I have been working with Kivy just this week, also as a new user.
The thing I have learned is to define properties on your RootWidget, just as the labels you already defined in the .kv files. This 'links' the layout and the Python code to eachother.
First of all you need to import the Kivy ObjectProperty by adding:
from kivy.properties import ObjectProperty
Next up is to declare the property on your RootWidget class.
You can add lblTest = ObjectProperty(None) right after the class-declaration.
The top of your file testGUI.py should look like this then:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
class RootWidget(BoxLayout):
# Link the layout objects
lblTest = ObjectProperty(None)
# runs select application
def startApplication (self, instance):
print(self.lblTest)
The page that really helped me with this is https://techwithtim.net/tutorials/kivy-tutorial/object-properties/
A little sidenote is it would be best to keep your id attributes fully unique.
I found the solution
Content of 'def next_screen' of mainGUI(App) to 'def startApplication'. Now i can change widgets in object oScreen or using the object in other python libs.
def startApplication (self, instance, sScreen):
filename = sScreen + '.kv'
# unload the content of the .kv file
# reason: it could have data from previous calls
Builder.unload_file(filename)
# clear the container
self.container.clear_widgets()
# load the content of the .kv file
oScreen = Builder.load_file(filename)
# add the content of the .kv file to the container
self.container.add_widget(oScreen)
print(oScreen.ids.lblTest.text)
The follow should be added in kv-files testGUIBtn1.kv, testGUIBtn2.kv:
RootWidget:
In kv-file testGUIRoot.kv you have change on_release to
on_release: root.startApplication(self,'testGUIBtn1')
Related
I'm following a tutorial on youtube and when I run the code, the window shows the labels half off the screen on the left-bottom side. I uploaded a screenshot of the window.
The .kv file is:
<MyGrid>
GridLayout:
cols: 1
GridLayout:
cols: 2
Label:
text: "Name: "
TextInput:
multiline:False
Label:
text: "Email: "
TextInput:
multiline:False
Button:
text: "Submit"
And the py file is:
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()
You are trying to use the base Widget class as a container, but it was not designed for that use. Try just using some Layout class as the base for MyGrid, like this:
class MyGrid(FloatLayout):
pass
Note that this will result in a GUI that is a FloatLayout that contains a GridLayout which contains another GridLayout. If that is not your intention, consider changing the base class of MyGrid to GridLayout and eliminate one or two of the contained GridLayouts.
I am expecting the following kivy app to switch screens when I press and then release the button, but nothing happens and there's no error on the terminal. When I run the app, the GirisEkrani screen show up, and then when I press and release the button in GirisEkrani, the next screen (GirisEkrani2) should show up. Do you how to make that work?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<ekranApp>
GirisEkrani:
id: ge
Button:
text: "İleri"
on_release: ge.manager.current = ge.manager.next()
GirisEkrani2:
Button:
id: ge2
text: "Ileri 2"
on_release: ge2.manager.current = ge2.manager.next()
KontrolEkrani:
id: ke
Button:
text: "Geri"
on_release: ke.manager.current = ke.manager.previous()
""")
class GirisEkrani(Screen):
pass
class GirisEkrani2(Screen):
pass
class KontrolEkrani(Screen):
pass
class ekranApp(App, ScreenManager):
def build(self):
#root = ScreenManager()
#root.add_widget(GirisEkrani(name = "giris_ekrani"))
#root.add_widget(GirisEkrani2(name = "giris_ekrani2"))
#root.add_widget(KontrolEkrani(name = "kontrol_ekrani"))
return self
if __name__ == "__main__":
ekranApp().run()
While people seem to advocate the use of .kv files as opposed to using pure Python, I find it very frustrating to have no errors showing up when something doesn't work.
The build() method of an App is expected to return a Widget which will be the root of the Widget tree for your App, but yours returns the App itself. I suspect that will cause problems. However, your code is not working because you are not setting the names of the Screens. Here is a corrected version of your kv:
Builder.load_string("""
<ekranApp>:
GirisEkrani:
id: ge
name: "giris_ekrani"
Button:
text: "İleri"
on_release: ge.manager.current = ge.manager.next()
GirisEkrani2:
id: ge2
name: "giris_ekrani2"
Button:
text: "Ileri 2"
on_release: ge2.manager.current = ge2.manager.next()
KontrolEkrani:
id: ke
name: "kontrol_ekrani"
Button:
text: "Geri"
on_release: ke.manager.current = ke.manager.previous()
""")
Also, your id for GirisEkrani2 is actually set on the Button.
I am trying to use the popup widget in Kivy, but everytime I run the code, the popup just has 2 smaller versions of the widget on the main screen.
This is my Python code (the .py file):
import kivy
kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.widget import Widget
class Layout(Widget):
pass
class KivyTestApp(App):
def build(self):
return Layout()
app = KivyTestApp()
app.run()
and this is my Kivy code (the .kv file):
#: import Factory kivy.factory.Factory
<MyPopup#Popup>:
title: 'Test'
size_hint: None, None
size: 400, 400
<Layout>:
Button:
id: but
size: root.width, root.height
background_normal: ''
background_color: .5, .7, .9, 1
text: 'Press me to open the popup'
pos: 0, 0
on_press: Factory.MyPopup().open()
This creates a window that looks like this:
And the popup looks like this:
As you can see, I have added no content to the popup, yet Buttons still appear! If anyone could help me, that would be great, and thanks in advance!
Kivy already has an internal widget called Layout, which is subclassed all over the place. Try naming your own widget something else.
i have a simple test program:
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen
from kivy.clock import mainthread
class TestScreen(Screen):
#mainthread
def on_pre_enter(self): #Is loaded before kv buttons etc? how make it work
pass
#mainthread
def on_enter(self): #Load after kv buttons etc?
button = Button(text="Work?")
#how now add it to display?
#how control where display it? on end or begin is just about on_pre and on?
class TestApp(App):
pass
if __name__ == '__main__':
TestApp().run()
And test.kv file
#:import NoTransition kivy.uix.screenmanager.NoTransition
<TestScreen>:
name:'try'
GridLayout:
id:'test'
cols:2
Button:
text:'Test'
on_press:app.root.current='Main'
ScreenManager:
transition: NoTransition()
Screen:
name: 'Main'
GridLayout:
cols:1
Button:
text:'1'
Button:
text:'2'
Button:
text:'Test'
on_press:root.current='try'
TestScreen:
Is simple to control kv and python widgets(but i dont know how but is more easy to writes widgets etc in kv file, but still need create some in python for automatic content) or better just create all in python withou kv file? I wanna make somehting like this: App with left menu always displayed and on the right side another screen with dynamic content based on screen(clicked from menu) is maybe another simple solution for this. Anyone can explain me step by step? :)
AttributeError
The solution to AttributeError, please replace "id: 'test'" with "id: test" in test.kv file.
Dynamic Content
It is possible to display screen with dynamic content based on clicked from menu. But remember to remove the widgets that were added when exiting the screen (TestScreen/SettingsScreen). If you do not remove the widgets, you will get duplicates/multiples of each widget added each time you enter the screen (TestScreen/SettingsScreen). I recommend using on_pre_enter and on_leave methods. Please refer to the example below for details.
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
class MyScreenManager(ScreenManager):
pass
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
def on_pre_enter(self, *args):
self.ids.test.add_widget(Button(text="Work?"))
def on_leave(self, *args):
self.ids.test.remove_widget(self.ids.test.children[0])
class TestApp(App):
title = "Add & Remove Widgets Dynamically"
def build(self):
return MyScreenManager()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
#:import NoTransition kivy.uix.screenmanager.NoTransition
<MyScreenManager>:
transition: NoTransition()
MenuScreen:
SettingsScreen:
<MenuScreen>:
name: 'menu'
GridLayout:
cols: 1
Button:
text: '1'
Button:
text: '2'
Button:
text: 'Test'
on_press: root.manager.current = 'settings'
<SettingsScreen>:
name:'settings'
GridLayout:
id: test
cols: 2
Button:
text: 'Test'
on_press: root.manager.current = 'menu'
Output
I created a class for a Popup and set the title property in the kv file.
When the popup shows, it does not have the title as in the kv file, but instead shows No Title as if it was never set.
It is exactly the same problem as here, but I do not understand from this link what the problem is or how to make it work:
https://github.com/kivy/kivy/issues/751
I understand how to do this using IDs in kv lang, but that only works if the Popup is put as a child widget of the root widget (ex. MainUI). Then I can link an instance of a python class to a widget in the kv file.
But then the popup displays as part of the root widget.
What I want to do, is instantiate a new instance of the popNewDB class when the New button is clicked and have this instance use the values such as "title" in the KV file.
Can you please explain how to do this?
Here is my code:
py file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
class popNewDB(Popup):
pass
class MainUI(Widget):
pop = ObjectProperty(popNewDB())
def showpopup(self):
self.pop.open()
class VerseReminder(App):
def build(self):
return MainUI()
if __name__ == '__main__':
VerseReminder().run()
kv file:
#:kivy 1.9.1
<popNewDB>
title: 'Hallo'
<MainUI>
Label:
pos: root.center_x - self.width/2,root.center_y + 200
text: "Quote Reminder"
BoxLayout:
size_hint: None,None
width: 400
height: 200
pos: root.center_x-200,root.center_y-50
orientation: 'vertical'
spacing: 20
Button:
size_hint: 1,1
text: "New..."
on_press: root.showpopup()
Button:
size_hint: 1,1
text: "Open..."
Button:
size_hint: 1,1
text: "Quit"
At the time pop = ObjectProperty(popNewDB()) is evaluated, the rules haven't been loaded, so only a barebones Popup will be created. Instead, you could do this:
class MainUI(Widget):
pop = ObjectProperty()
def showpopup(self):
if self.pop is None:
self.pop = PopNewDB()
self.pop.open()
Here, the first time the button is pressed, a new instance of PopNewDB will be created and stored in self.pop.
(NB: I renamed the Popup subclass to start with a Capital Letter, to be consistent with language standards and kivy expectations)