I'm trying to call a custom widget method from its ids.
But I received an AttributeError: 'LabelBox' object has no attribute 'change_first_text'.
As a simple as possible working example can be found here with the PanelApp.py file:
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_file("panel.kv")
class LabelBox(BoxLayout):
def __init__(self, *args, **kwargs):
super(LabelBox, self).__init__(*args, **kwargs)
def change_first_text(self, text):
self.ids.first.text = text
class ButtonList(BoxLayout):
pass
class Test(TabbedPanel):
pass
class TabbedPanelApp(App):
def build(self):
self.test = Test()
self.btn_list = ButtonList()
self.vbox = BoxLayout(orientation="vertical")
self.vbox.add_widget(self.btn_list)
self.vbox.add_widget(self.test)
return self.vbox
def change_first(self, value):
print("Button clicked and new value is: '{}'".format(value))
self.test.ids.lbs.change_first_text(value)
if __name__ == '__main__':
TabbedPanelApp().run()
and the panel.kv file:
<ButtonList#ButtonList>:
orientation: "horizontal"
Button:
text: "change fisrt to me"
on_press: app.change_first(self.text)
Button:
text: "change two to me"
<LabelBox#BoxLayout>:
Label:
id: first
text: "first"
Label:
id: two
text: "two"
<Test>:
size_hint: .5, .5
pos_hint: {'center_x': .5, 'center_y': .5}
do_default_tab: False
TabbedPanelItem:
text: 'first tab'
LabelBox:
id: lbs
Calling the script cause a runtime error that I can't understand.
Have you any clue on how to manage such event callback through the application?
The problem in your case is that you are creating 2 classes called LabelBox:
1.
class LabelBox(BoxLayout):
def __init__(self, *args, **kwargs):
super(LabelBox, self).__init__(*args, **kwargs)
def change_first_text(self, text):
self.ids.first.text = text
2.
<LabelBox#BoxLayout>:
Label:
id: first
text: "first"
Label:
id: two
text: "two"
I understand that you only want to have a class so it is appropriate to do the creation with the inheritance in the .py and only the implementation of children in the .kv. The solution is to change delete #BoxLayout in the .kv
<LabelBox>:
Label:
id: first
text: "first"
Label:
id: two
text: "two"
Related
I have to write a program in python using kivy that takes text in a textbox and passing it to function that do web scraping and a lot of things then return array of strings and the last element in the array is array of pairs so I am really confused and sent a lot of time .so I must write two kv files or just one file ? here is my simple kivy code as a start .
I tried but it is not working
#textInput.py
from app import *
Builder.load_file('textInput.kv')
require('1.10.0')
class MainScreen(BoxLayout):
def __init__(self):
self.super(MainScreen, self).__init__()
def btn_click(self):
name =self.BoxLayout.BoxLayout.TextInput
#the function that takes the output of the text field
get_company_name(name)
#
#
#
# here I will call the function that returns the array so how to pass the answer
# and also pass to where ? shall I use the same kv file or create another one
class Test(App):
def build(self):
self.title = 'CompanyInfoApp'
return MainScreen()
if __name__ == '__main__':
Test().run()
lbl: My_label
orientation: 'vertical'
# Third section title
Label:
size_hint: (1, .1)
text: 'Welcome To compnay info App'
font_size: 25
# Third section Box
BoxLayout:
Button:
text:"let's start"
on_press:root.btn_click()
size_hint: (1, .2)
padding: [180, 180, 180, 180]
BoxLayout:
Label:
pos_hint:{'x': .3, 'y': .6}
text: 'Enter the Company Name:'
text_size: self.width-20, self.height-20
TextInput:
height: self.minimum_height
pos_hint:{'x': .3, 'y': .6}
multiline: False
text: ''
Try to give your TextInput widget an id. Then you can access the text data of TextInput widget with the use of its id.
Here is a basic example:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import *
main_kv = """
<Main>:
orientation: 'vertical'
TextInput:
# TextInput's id
id: txtinput
text: ''
Button:
text: "print text"
on_press: root.btn_click()
"""
class Main(BoxLayout):
def btn_click(self):
name = self.ids['txtinput'].text
print(name)
class Test(App):
def build(self):
Builder.load_string(main_kv)
return Main()
Test().run()
When I click on Account(root.display_account()) then call display_account().After that RVACCOUNT() function call .After that when i click on +Add Account then def add_account(self): call
I have a class AccountPopup which define a attribute state_text and assign value text:'Testing' in .kv file
How to get value of state_text 'Testing' and pass in on_text: root.filter(self.text,state_text) and print in def filter function.
test.py
class AccountPopup(Popup):
state_text = ObjectProperty(None)
popupAccountCity = ObjectProperty(None)
def display_cities_treeview_account(self, instance):
if len(instance.text) > 0:
#if self.popupAccountCity is None:
self.popupAccountCity = TreeviewCityAccount(self.state_text.text)
self.popupAccountCity.filter(instance.text,self.state_text.text)
self.popupAccountCity.open()
class TreeviewCityAccount(Popup):
state_text = ObjectProperty(None)
def __init__(self,state_text, **kwargs):
print(state_text)
def filter(self, f,state):
print(state)
class RVACCOUNT(BoxLayout):
def add_account(self):
self.mode = "Add"
popup = AccountPopup(self)
popup.open()
class MainMenu(BoxLayout):
def display_account(self):
self.dropdown.dismiss()
self.remove_widgets()
self.rvaccount = RVACCOUNT()
self.content_area.add_widget(self.rvaccount)
class FactApp(App):
title = "Test"
def build(self):
self.root = Builder.load_file('test.kv')
return MainMenu()
if __name__ == '__main__':
FactApp().run()
test.kv
<AccountPopup>:
state_text:state_text
TextInput:
id:state_text
text:'Testing'
<TreeviewCityAccount>:
BoxLayout
orientation: "vertical"
TextInput:
id: treeview
size_hint_y: .1
on_text: root.filter(self.text,state_text)
<RVACCOUNT>:
BoxLayout:
orientation: "vertical"
Button:
size_hint: .07, .03
text: "+Add Account"
on_press: root.add_account()
<MainMenu>:
content_area: content_area
dropdown: dropdown
BoxLayout:
orientation: 'vertical'
#spacing : 10
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
MenuButton:
id: btn
text: 'Master'
size : (60,30)
on_release: dropdown.open(self)
CustDrop:
DropdownButton:
text: 'Account'
size_hint_y: None
height: '32dp'
on_release: root.display_account()
Can someone help me?
You should reference it as self.state_text everywhere, also make it a StringProperty in the py file and can than access it as
on_text: root.filter(self.text,root.state_text)
root in kv refers to the most left widget aka <TreeviewCityAccount>: in your case.
See https://kivy.org/docs/api-kivy.lang.html
Alternatively you can work with ids in the kv file.
the value you are looking for is not in your immediate root which is why this is not working.The say thing to do is get the full path to that property like so:
Snippet:
<AccountPopup>:
id: ac_popup
#bunch of code
<TreeviewCityAccount>:
#chunk of code
TextInput:
id: tree view
on_text:root.filter(self.text,app.ac_popup.state_text
Also,generally,it's a good idea to id your classes mate
Disclaimer:code not tested
I want to call a method add_category_to_tree() from TreeCategory class, when pressing SAVE button in AddCategoryPopup class. However, I have problems how to reference to the TreeCategory instance, which is created in KV file.
I was trying to search solution, but nothing works. Currently i get AttributeError: 'super' object has no attribute '__getattr__' error.
How should I do this properly ? Thank you for help
class TreeCategory(TreeView):
def __init__(self, **kwargs):
super(TreeCategory, self).__init__(**kwargs)
def add_category_to_tree(self, name):
self.add_node(TreeViewLabel(text = name.upper()))
class AddCategoryPopup(Popup):
def save(self):
self.ids.tree.add_category_to_tree(self.ids.entry.text) # ????
db.adding_to_db('kategorie', 'nazwa', self.ids.entry.text)
self.dismiss()
def close(self):
self.dismiss()
class MainScreen(BoxLayout):
tree = ObjectProperty(None)
def add_category_button(self):
popup = AddCategoryPopup(title = 'Dodawanie nowej kategorii')
return popup.open()
class GuiCookBookApp(App):
def build(self):
self.title = "Książka kucharska"
return MainScreen()
if __name__ == "__main__":
db = DatabaseManager("cookbook.sqlite")
GuiCookBookApp().run()
KV file:
<AddCategoryPopup>:
BoxLayout:
orientation: 'vertical'
TextInput:
id: entry
multiline: False
hint_text: 'Podaj nazwę kategorii...'
BoxLayout:
orientation: 'horizontal'
Button:
text: 'SAVE'
on_press: root.save()
Button:
text: 'CANCEL'
on_press: root.close()
<MainScreen>:
orientation: "vertical"
display: entry
tree: tree
BoxLayout:
id: menu
size_hint_y: .1
Button:
text: 'Dodaj kategorię'
on_press: root.add_category_button()
BoxLayout:
id: recipe_view
orientation: "horizontal"
TreeCategory:
id: tree
hide_root: True
size_hint: .25, 1
With self.ids in Python you can only access the ids in KV from that particular class. So self.ids.tree is only possible inside the MainScreen class in Python not in the AddCategoryPopup class.
You could create an ObjectProperty 'topwidget' in your AddCategoryPopup rule and pass in the Main class when you instantiate the popup. Something like:
popup = AddCategoryPopup(topwidget=self)
Then in the 'save' method of your custom popup class you can do something like:
self.topwidget.tree...
You can do it quite a few ways. For instance you can put the .kv in your main .py file.
w = Builder.load_string('''
Widget:
height: self.width / 2. if self.disabled else self.width
x: self.y + 50
''')
https://kivy.org/docs/api-kivy.lang.builder.html
You can simply name the .kv file
guicookbookapp.kv
and leave it in the root directory of your project.
https://kivy.org/docs/examples/gen__application__app_with_kv__py.html
You can also add the following
from kivy.lang import Builder
Builder.load_file('guicookbookapp.kv')
Hopefully I understood your question correctly.
I'm trying to create a customer management software, so I need to create a GUI. I chose Kivy because it's Open Source and LGPL.
This software is meant to have multiple panels, so I need to have ID's to access widgets in each panel. I created Kivy rules in kv language, but when I nest a class is another one, I can't access the ID's. Below an example code:
LayoutTestApp.py :
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.boxlayout import BoxLayout
class SkipperList(GridLayout):
pass
class TestPanel(BoxLayout):
def __init__(self, **kwargs):
super(TestPanel, self).__init__(**kwargs)
print "TestPanel ids:", self.ids
class MasterPanel(TabbedPanel):
pass
class NjordApp(App):
def __init__(self, **kwargs):
super(NjordApp, self).__init__(**kwargs)
def build(self):
root = MasterPanel()
return root
if __name__ == '__main__':
application = NjordApp()
application.run()
njord.kv
#:kivy 1.9.0
<MasterPanel>
pos_hint: {'center_x': .5, 'center_y': .5}
do_default_tab: False
TabbedPanelItem:
text: 'Skippers'
BoxLayout:
padding: 10
spacing: 10
TestPanel:
<TestPanel>:
id: SkipperPanelId
BoxLayout:
padding: 10
spacing: 10
BoxLayout:
orientation: 'vertical'
Label:
text: 'List des mecs'
size_hint: 1, 0.09
Button:
id: button_up
size_hint: 1, 0.08
text:'/\\'
Button:
id: button_down
size_hint: 1, 0.08
text:'\/'
When I launch the software, the print only return {}.
Can someone tell me how to access button_up ID for example ?
Thanks advance.
The reason you don't see the ids is because you are printing in the constructor of TestPanel. It hasn't finished being created yet, let alone have anything added to it. If you print the ids after the GUI has been created (ie, from a button press) then you will see the ids:
class TestPanel(BoxLayout):
def __init__(self, **kwargs):
super(TestPanel, self).__init__(**kwargs)
print "TestPanel ids:", self.ids
def test(self, *x):
print self.ids
...
Button:
id: button_up
size_hint: 1, 0.08
text:'/\\'
on_press: root.test()
output:
{'button_down': <WeakProxy to <kivy.uix.button.Button object at 0x7f63159052c0>>, 'button_up': <WeakProxy to <kivy.uix.button.Button object at 0x7f63158f3738>>}
How can I access to the kivy data from MyScreenManager ? How can I access to Hellow or Timer data ? I cant use on_release: root.starttimer() in Hellow.
class Hellow(Screen):
pass
class Timer(Screen):
pass
class MyScreenManager(ScreenManager):
def starttimer(self):
#change text Hellow Button
root_widget = Builder.load_string('''
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
MyScreenManager:
transition: FadeTransition()
Hellow:
Timer:
<Hellow>:
AnchorLayout:
Button:
id: but
size_hint: None, None
size:300,100
text: 'make a foto'
font_size: 30
on_release: app.root.starttimer()
<Timer>:
name: 'Timer'
''')
class ScreenManagerApp(App):
def build(self):
print(self.ids)
return root_widget
if __name__ == '__main__':
ScreenManagerApp().run()
some text for stackoverflow (it says that I need to type more text),
Screen manager is only used to accept screen widgets if you try to add anything else like a button or label then it will throw an exception.
kivy.uix.screenmanager.ScreenManagerException: ScreenManager accepts only Screen widget.
Only one root object is allowed by .kv file In your case, you can access hello or Timer from each other.
<Hellow>:
name: 'hello'
...
Button:
id: but
...
on_release: root.parent.current = 'Timer'
<Timer>:
name: 'Timer'
Button:
text: "Take me back to hellow"
on_release: root.parent.current = 'hello'
but there could be another way too.
<Main>:
BoxLayout:
Button:
text: "Hello"
on_release: sm.current = 'Timer'
on_release: print(lbl.text)
Button:
text: "Timer"
on_release: sm.current = 'Hello'
ScreenManager:
id: sm
Screen:
name: hello
Label:
id: lbl
text: "I am hello"
Screen:
name: timer
Label:
text: "I am timer"
EDIT 1:
As you asked in your comment
class MyScreenManager(ScreenManager):
def __init__(self,**kwargs):
super(MyScreenManager,self).__init__(**kwargs)
def starttimer(self,event):
#print event.text
event.text = "changed text"
<Hellow>:
...
Button:
...
on_release: root.parent.starttimer(self)