I'm having a hard time with Kivy's sytem of half pure-python and half kv language setup. All I'm trying to do is for now is a 'hello world' type on_press event, and I can't get it to work.
from kivy.uix.modalview import ModalView
from kivy.uix.listview import ListView
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
from kivy.app import App
import citylists
import cat_dict
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.storage.jsonstore import JsonStore
store = JsonStore('data.json')
Builder.load_string("""
#:import ListItemButton kivy.uix.listview
#:import sla kivy.adapters.listadapter
<ListViewModal>:
ListView:
size_hint: .8, .8
adapter:
sla.ListAdapter(
data=["{0}".format(i) for i in root.categories],
on_press=root.callback(self),
cls=ListItemButton.ListItemButton)
""")
class ListViewModal(ModalView):
categories = sorted(cat_dict.SECTION_DICT)
def __init__(self, **kwargs):
super(ListViewModal, self).__init__(**kwargs)
def callback(self, instance):
print "HI" + str(instance)
class MainView(GridLayout):
def __init__(self, **kwargs):
kwargs['cols'] = 1
super(MainView, self).__init__(**kwargs)
listview_modal = ListViewModal()
self.add_widget(listview_modal)
class MainScreen(Screen):
pass
mainscreen=MainScreen()
mainlayout = MainView()
mainscreen.add_widget(mainlayout)
sm = ScreenManager()
sm.add_widget(mainscreen)
class CARApp(App):
def build(self):
return sm
if __name__ == '__main__':
CARApp().run()
cat_dict.py
SECTION_DICT = {
"accounting+finance": "acc",
"admin / office": "ofc",
"arch / engineering": "egr",
'art / media / design': 'med',
'biotech / science': 'sci',
'business / mgmt': 'bus',
'customer management': 'csr',
'education': 'edu',....
Ultimately, I want to bind the on_press event for each of the dynamically created buttons titled with each key in SECTION_DICT, then save the value in JsonStore.
In simple terms all I need to happen is for a user to press a button to choose a craigslist category, which will return the 3 letter abbreviation to be used later in the program.
A ListAdapter does not have an on_press event. You need to bind to the on_press event of each button, which can be done using an args converter:
#:import ListItemButton kivy.uix.listview.ListItemButton
#:import ListAdapter kivy.adapters.listadapter.ListAdapter
<ListViewModal>:
ListView:
size_hint: .8, .8
adapter:
ListAdapter(
data=["{0}".format(i) for i in root.categories],
args_converter=lambda row_index, rec: \
{'text': rec, 'on_press': root.callback, 'size_hint_y': None, 'height': 25},
cls=ListItemButton)
Also, take care to pass functions themselves as callbacks rather than the return value of functions. In other words, use root.callback instead of root.callback(self).
Related
How is it possible that the Kivy GUI app is not load the whole code at the time of initialization?
Is it possible that when we called some function so the execution of the code which is inside that function starts at runtime.
The code is:
from kivymd.app import MDApp
from kivymd.uix.card import MDCard
from kivymd.uix.screen import MDScreen
from kivymd.uix.selectioncontrol import MDCheckbox
from kivymd.uix.textfield import MDTextField
from kivymd.uix.label import MDLabel
from kivymd.uix.boxlayout import MDBoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.metrics import dp
from kivymd.uix.fitimage import FitImage
from kivy.uix.boxlayout import BoxLayout
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
from kivy.uix.screenmanager import Screen,ScreenManager
class my_mdcard(MDCard,RoundedRectangularElevationBehavior):
pass
class Item_Menu(Screen,MDBoxLayout):
list_of_information={"checkbox":[],"labels":[],"quantity":[]}
selected_items= {"checkbox": [], "labels": [], "quantity": []}
def __init__(self,**kwargs):
super(Item_Menu,self).__init__(**kwargs)
self.size_hint=(1,0.9)
self.pos_hint={"top":1}
self.orientation="vertical"
scrollbarwin=ScrollView()
content_box=BoxLayout(orientation='vertical',padding=dp(8),spacing=dp(8),size_hint=(1,None))
content_box.bind(minimum_height=content_box.setter('height'))
for i in range(0,50):
Template_card= my_mdcard(
size_hint_y=None,
size_hint_x=.960,
height = dp(100),
padding = dp(4),
pos_hint={'center_y': .5, 'center_x': .490},
radius = [20,],
elevation = 4,
)
checkbox=MDCheckbox(
size_hint=(None, None),
size= (dp(48),dp(48)),
pos_hint={'center_y': .5}
)
checkbox.bind(active=lambda instance,value:self.Selected_checkbox(instance,value))
self.list_of_information["checkbox"].append(checkbox)
image_box=MDBoxLayout(adaptive_size=True)
image=FitImage(
source="D:/Study/Python/Kivy/images/2.jpg",
size_hint= (None, None),
height=dp(80),
width=dp(130),
radius=[12,],
pos_hint={'center_y':0.5}
)
image_box.add_widget(image)
text_box=MDBoxLayout(orientation="vertical",adaptive_height=True,pos_hint={'center_y':0.5},padding=[12,0,0,0])
item_name=MDLabel(text=f"item{i+1}",font_style="H5",size_hint=(1,None),bold=True,theme_text_color="Primary")
item_name.bind(texture_size=item_name.setter('size'))
self.list_of_information["labels"].append(item_name)
price=MDLabel(text=u"Price: \u20B910/per",font_style="Subtitle1",size_hint=(1,None),bold=True,theme_text_color="Hint")
price.bind(texture_size=price.setter('size'))
quantitybox=MDBoxLayout(orientation='vertical',adaptive_height=True,size_hint_x=0.2,pos_hint = {'center_y': .5,'center_x':0.5})
quantityfield=MDTextField(
hint_text= "Quantity",
mode= "rectangle",
size_hint=(None,None),
width=dp(80),
height= dp(40),
padding=[0,0,15,0]
)
self.list_of_information["quantity"].append(quantityfield)
quantitybox.add_widget(quantityfield)
Template_card.add_widget(checkbox)
Template_card.add_widget(image_box)
Template_card.add_widget(text_box)
text_box.add_widget(item_name)
text_box.add_widget(price)
Template_card.add_widget(quantitybox)
content_box.add_widget(Template_card)
scrollbarwin.add_widget(content_box)
buttonbox=MDBoxLayout(orientation="vertical",pos_hint={"top":0.1},adaptive_height=True)
button=MDRaisedButton(text="Selected!!!",size_hint=(1,0.2))
button.bind(on_release=lambda x:self.Change_window(x))
buttonbox.add_widget(button)
self.add_widget(scrollbarwin)
self.add_widget(buttonbox)
def Selected_checkbox(self,instance,value):
for i in range(0,len(self.list_of_information["checkbox"])):
if instance==self.list_of_information["checkbox"][i] and value== True:
self.selected_items["labels"].append(self.list_of_information["labels"][i])
self.selected_items["quantity"].append(self.list_of_information["quantity"][i])
elif instance==self.list_of_information["checkbox"][i] and value== False :
self.selected_items["labels"].remove(self.list_of_information["labels"][i])
self.selected_items["quantity"].remove(self.list_of_information["quantity"][i])
def Change_window(self,instance):
MyApp.sm.current="Item Description"
class Description_item(Screen):
def __init__(self,**kwargs):
super(Description_item,self).__init__(**kwargs)
print(Item_Menu.selected_items)
wholeContentBoxContainer=MDBoxLayout(orientation="vertical")
for item in range(0,len(Item_Menu.selected_items["labels"])):
label=MDLabel(text=Item_Menu.selected_items["labels"][item].text)
print(Item_Menu.selected_items["labels"][item].text)
quantity=MDLabel(text=Item_Menu.selected_items["quantity"][item].text)
wholeContentBoxContainer.add_widget(label)
wholeContentBoxContainer.add_widget(quantity)
self.add_widget(wholeContentBoxContainer)
class MyApp(MDApp):
sm=ScreenManager()
def build(self):
self.sm.add_widget(Item_Menu(name="Item Menu"))
self.sm.add_widget(Description_item(name="Item Description"))
self.theme_cls.theme_style="Dark"
return self.sm
MyApp().run()
When you run this code so you will see that at the time of initialization of the app the print function is getting print the the dictionary "selected_items" which I never want to be print it out at the time of initialization of the code.
Here is a picture will help you to better understand:
Due to this reason I am unable to access the data of the dictionary "selected_items" in the class "Description_item" because it points to empty list which is the value of the dictionary keys.
So how I can access the data of the dictionary "selected_items"?
Struggling to pass a variable to kivy window. I have read similar threads all over the place but none of the fixes seem to work for me. Im sure this is simple to someone who knows their way around tiny, unfortunately I don't.
main.py
import kivy
from kivy.uix.togglebutton import ToggleButton
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.app import App
kivy.require('1.10.0')
from phue import Bridge
import nest
b = Bridge('xxx.xxx.x.xxx')
b.connect()
b.get_api()
lights = b.lights
class Controller(GridLayout):
print("launching")
def __init__(self):
super(Controller, self).__init__()
def KitchenSpot1(self,state):
lights[0].name
lights[0].on = state
def update(dt):
if b.get_light(1, 'on')== True:
#print("down") # When this line is commented out I get an continuous accurate update on the status of the light, showing that its working.
return 'down' # This is the part I want passed to the state criteria in the ivy window
else:
#print("up")# When this line is commented out I get an continuous accurate update on the status of the light, showing that its working.
return 'down' # This is the part I want passed to the state criteria in the ivy window
class ActionApp(App):
def build(self):
Clock.schedule_interval(Controller.update, 1.0 / 60.0)
return Controller()
myApp = ActionApp()
myApp.run()
action.kv
<Controller>:
cols: 4
rows: 3
spacing: 10
ToggleButton:
id: KitchenSpot1Toggle
text: "Kitchen Spot 1"
on_press: root.KitchenSpot1(True)
#on_release: root.KitchenSpot1(False)
#state1 = app.update.h
state: Controller.update # This is the part that is throwing up the error.
The error:
11: #on_release: root.KitchenSpot1(False)
12: #state1 = app.update.h
>> 13: state: Controller.update
14:
15:
...
NameError: name 'Controller' is not defined
Thanks in advance to anyone that can help me.
Make update an instance method and use a StringProperty to update state property in your kv:
main.py:
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivy.uix.gridlayout import GridLayout
from kivy.uix.togglebutton import ToggleButton
from phue import Bridge
import nest
b = Bridge('xxx.xxx.x.xxx')
b.connect()
b.get_api()
lights = b.lights
class Controller(GridLayout):
state = StringProperty('normal') # <<<<<<<<<<<<
def __init__(self, **kwargs):
super(Controller, self).__init__(**kwargs)
Clock.schedule_interval(self.update, 1.0 / 60.0)
def KitchenSpot1(self,state):
lights[0].name
lights[0].on = state
def update(self, dt):
if b.get_light(1, 'on'):
self.state = 'down' # <<<<<<<<<<<<
else:
self.state = 'normal' # <<<<<<<<<<<<
class ActionApp(App):
def build(self):
return Controller()
if __name__ == "__main__":
myApp = ActionApp()
myApp.run()
action.kv:
<Controller>:
cols: 4
rows: 3
spacing: 10
state: "normal" # <<<<<<<<<<<<
ToggleButton:
id: KitchenSpot1Toggle
text: "Kitchen Spot 1"
on_press: root.KitchenSpot1(True)
#on_release: root.KitchenSpot1(False)
#state1 = app.update.h
state: root.state # <<<<<<<<<<<<
Here is a more generic simplified answer from the kivy documentation, look for the section called "Keyword arguments and init()" because there are some other ways to do it as well.
The following code passes myvar to the build() method of MyApp. It does this by over-riding the init() of the Kivy App class by a new init() that calls App.init() and then continues with whatever extra initialisation you want. You can then store variables in the MyApp class instances and use them in build().
from kivy.app import App
from kivy.uix.label import Label
myvar = 'Hello Kivy'
class MyApp(App):
def __init__(self, myvar, **kwargs):
super(MyApp, self).__init__(**kwargs)
self.myvar = myvar
def build(self):
widget = Label(text=self.myvar)
return widget
if __name__ == '__main__':
MyApp(myvar).run()
New to kivy, and OOP.
I'm trying to update a label in kivy with data I pull from a temp sensor. The code that pulls in the sensor data is in labeltempmod. I created a function getTheTemp() that is called every second. In the function I try to assign the text of the label via Label(text=(format(thetemp)), font_size=80). The program ignores this. What am I doing wrong here?
#This is a test to see if I can write the temp to label
import labeltempmod
import kivy
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
def getTheTemp(dt):
thetemp = labeltempmod.readtemp()
Label(text=(format(thetemp)), font_size=80)
print thetemp
class LabelWidget(BoxLayout):
pass
class labeltestApp(App):
def build(self):
# call get_temp 0.5 seconds
Clock.schedule_interval(getTheTemp, 1)
return LabelWidget()
if __name__ == "__main__":
labeltestApp().run()
Here is the kivy language file:
<LabelWidget>:
orientation: 'vertical'
TextInput:
id: my_textinput
font_size: 80
size_hint_y: None
height: 100
text: 'default'
FloatLayout:
Label:
id: TempLabel
font_size: 150
text: 'Temp Test'
Thanks.
Sorry but you never update something You are just creating another label
Try this:
class LabelWidget(BoxLayout):
def __init__(self, **kwargs):
super(LabelWidget, self).__init__(**kwargs)
Clock.schedule_interval(self.getTheTemp, 1)
def getTheTemp(self, dt):
thetemp = labeltempmod.readtemp()
self.ids.TempLabel.text = thetemp
print thetemp
class labeltestApp(App):
def build(self):
return LabelWidget()
if __name__ == "__main__":
labeltestApp().run()
Update : for your last request, I think the best way to do that is:
...
class LabelWidget(BoxLayout):
def __init__(self, **kwargs):
super(LabelWidget, self).__init__(**kwargs)
self.Thetemp = None
Clock.schedule_interval(self.getTheTemp, 1)
def getTheTemp(self, dt):
if self.Thetemp is None:
self.thetemp = labeltempmod.readtemp()
else:
self.thetemp = labeltempmod.readtemp(self.theTemp)
self.ids.TempLabel.text = str(self.thetemp)
I'm trying to add the MDFloatingActionButton widget after clicking on button but I'm not getting it.
Someone could help me to solve this problem.
The goal is to create a list of buttons after clicking the FAB with the icon plus.
I tried adding code to add_widget() in a number of ways but none worked.
fab.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty
from kivymd.theming import ThemeManager
from kivymd.time_picker import MDTimePicker
from kivymd.button import MDFloatingActionButton
from kivy.animation import Animation
from kivy.core.window import Window
Window.clearcolor = (1, 1, 1, 1)
class MDFloatingActionButtonList(MDFloatingActionButton):
angle = NumericProperty(0)
def on_touch_up(self, touch):
if self.collide_point(*touch.pos):
if self.angle == 0:
self.angle += 45
#MDFloatingActionButton.add_widget()
else:
self.angle -= 45
class Fab(App):
theme_cls = ThemeManager()
def build(self):
return MDFloatingActionButtonList()
Fab().run()
fab.kv
<MDFloatingActionButtonList>:
canvas.before:
PushMatrix
Rotate:
angle: self.angle
axis: (0, 0, 1)
origin: self.center
canvas.after:
PopMatrix
MDFloatingActionButton:
id: float_act_btn
icon: 'plus'
opposite_colors: True
elevation_normal: 8
pos_hint: {'center_x': 0.5, 'center_y': 0.2}
Result:
Goal for example:
Oh boy, this is a tough one. The KivyMD project is poorly documented, even though the design is so pretty.
Ok, here is one example of how it might look:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivymd.button import MDFloatingActionButton
from kivymd.menu import MDDropdownMenu
from kivymd.theming import ThemeManager
Window.clearcolor = (1, 1, 1, 1)
menu_items = [
{'viewclass': 'MDFloatingActionButton',
'text': 'Example',
'on_press': lambda: print("Hello")},
{'viewclass': 'MDFloatingActionButton',
'text': 'Example'},
{'viewclass': 'MDFloatingActionButton',
'text': 'Example'},
{'viewclass': 'MDFloatingActionButton',
'text': 'Example item'},
{'viewclass': 'MDFloatingActionButton',
'text': 'Example'},
{'viewclass': 'MDFloatingActionButton',
'text': 'Example'},
{'viewclass': 'MDFloatingActionButton',
'text': 'Example'},
]
class Fab(App):
theme_cls = ThemeManager()
layout = BoxLayout()
md = MDDropdownMenu(items=menu_items)
def build(self):
button = MDFloatingActionButton()
self.layout.add_widget(button)
button.bind(on_press=lambda x: self.md.open(button))
return self.layout
Fab().run()
Another way is to manually add the buttons to the window. But then you will have to handle the dissmiss (I did not implement it):
from kivy.app import App
from kivy.core.window import Window
from kivymd.button import MDFloatingActionButton
from kivymd.theming import ThemeManager
Window.clearcolor = (1, 1, 1, 1)
class Fab(App):
theme_cls = ThemeManager()
button = None
def build(self):
self.button = MDFloatingActionButton()
self.button.bind(on_press=lambda x: self.open_menu(self.button))
return self.button
def open_menu(self, instance):
x, y = instance.to_window(instance.x, instance.center_y)
for i in range(1, 5):
Window.add_widget(MDFloatingActionButton(center_y=y+100*i, x=x))
Fab().run()
Why don't you try this?
MDFloatingActionButtonSpeedDial
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
Screen:
MDFloatingActionButtonSpeedDial:
data: app.data
root_button_anim: True
'''
class Example(MDApp):
data = {
'language-python': 'Python',
'language-php': 'PHP',
'language-cpp': 'C++',
}
def build(self):
return Builder.load_string(KV)
Example().run()
Here is the output:
I'm trying to implement a code I found for a list that returns the item selected in kivy.
The code works as an indepedent app. But I can't figure out how it is working step by step to implement it within my own application (using mainly the kv lang).
from kivy.uix.modalview import ModalView
from kivy.uix.listview import ListView
from kivy.uix.gridlayout import GridLayout
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.factory import Factory
# Note the special nature of indentation in the adapter declaration, where
# the adapter: is on one line, then the value side must be given at one level
# of indentation.
Builder.load_string("""
#:import lv kivy.uix.listview
#:import la kivy.adapters.listadapter
<MainView>:
ListViewModal
<ListViewModal>:
list_view: list_view_id
size_hint: None,None
size: 400,400
ListView:
id: list_view_id
size_hint: .8,.8
adapter:
la.ListAdapter(
data=["Item #{0}".format(i) for i in xrange(10)],
selection_mode='single',
allow_empty_selection=False,
cls=lv.ListItemButton)
""")
class ListViewModal(ModalView):
selected_item = StringProperty('no selection')
list_view = ObjectProperty(None)
def __init__(self, **kwargs):
super(ListViewModal, self).__init__(**kwargs)
self.list_view.adapter.bind(on_selection_change=self.selection_changed)
# This is for the binding set up at instantiation, to the list adapter's
# special on_selection_change (bind to it, not to adapter.selection).
def selection_changed(self, *args):
print ' args when selection changes gets you the adapter', args
self.selected_item = args[0].selection[0].text
# This is to illustrate another type of binding. This time it is to this
# class's selected_item StringProperty (where the selected item text is set).
# See other examples of how bindings are set up between things. This one
# works because if you put on_ in front of a Kivy property name, a binding
# is set up for you automatically.
def on_selected_item(self, *args):
print ' args when a list property changes gets you the list property, and the changed item', args
print 'selected item text', args[1]
class MainView(GridLayout):
"""
Implementation of a ListView using the kv language.
"""
# def __init__(self, **kwargs):
# kwargs['cols'] = 1
# kwargs['size_hint'] = (1.0, 1.0)
# super(MainView, self).__init__(**kwargs)
# listview_modal = ListViewModal()
# self.add_widget(listview_modal)
if __name__ == '__main__':
from kivy.base import runTouchApp
runTouchApp(MainView(width=800))from kivy.uix.modalview import ModalView
from kivy.uix.listview import ListView
from kivy.uix.gridlayout import GridLayout
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.factory import Factory
# Note the special nature of indentation in the adapter declaration, where
# the adapter: is on one line, then the value side must be given at one level
# of indentation.
Builder.load_string("""
#:import lv kivy.uix.listview
#:import la kivy.adapters.listadapter
<MainView>:
ListViewModal
<ListViewModal>:
list_view: list_view_id
size_hint: None,None
size: 400,400
ListView:
id: list_view_id
size_hint: .8,.8
adapter:
la.ListAdapter(
data=["Item #{0}".format(i) for i in xrange(10)],
selection_mode='single',
allow_empty_selection=False,
cls=lv.ListItemButton)
""")
class ListViewModal(ModalView):
selected_item = StringProperty('no selection')
list_view = ObjectProperty()
def __init__(self, **kwargs):
super(ListViewModal, self).__init__(**kwargs)
self.list_view.adapter.bind(on_selection_change=self.selection_changed)
# This is for the binding set up at instantiation, to the list adapter's
# special on_selection_change (bind to it, not to adapter.selection).
def selection_changed(self, *args):
print ' args when selection changes gets you the adapter', args
self.selected_item = args[0].selection[0].text
# This is to illustrate another type of binding. This time it is to this
# class's selected_item StringProperty (where the selected item text is set).
# See other examples of how bindings are set up between things. This one
# works because if you put on_ in front of a Kivy property name, a binding
# is set up for you automatically.
def on_selected_item(self, *args):
print ' args when a list property changes gets you the list property, and the changed item', args
print 'selected item text', args[1]
class MainView(GridLayout):
"""
Implementation of a ListView using the kv language.
"""
pass
# def __init__(self, **kwargs):
# kwargs['cols'] = 1
# kwargs['size_hint'] = (1.0, 1.0)
# super(MainView, self).__init__(**kwargs)
# listview_modal = ListViewModal()
# self.add_widget(listview_modal)
if __name__ == '__main__':
from kivy.base import runTouchApp
runTouchApp(MainView(width=800))
It raises me this error:
"AttributeError: 'NoneType' object has no attribute 'adapter'"
I have read the documentation about 'adapter' but it's pretty new to me and I can't figure out why do I need an adapter for the list to return a result?