How to get ID of the button pressed in kivy? - python

Here is the example code that I am working with.
I am creating a pre-set number of buttons, each with their own ID numbers, based on the for loop
All the buttons have the same function attached to them when they are pressed
My goal is to get the ID name of the button that was pressed
Currently, my code is printing out the specific object address(?) like 0xAABBCCEE
I want to know how to print out the code in ID format, like "Button 3"
class MainScreen(GridLayout):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.cols = 1
MainScreenLayout = GridLayout()
MainScreenLayout.cols = 3
#for loop creating buttons with varying IDs
NumberToCreate = 4
for x in range(int(NumberToCreate)):
aButton = Button(text='button text ' + str(x), on_press=self.press_auth)
MainScreenLayout.add_widget(aButton)
self.ids['button' + str(x)] = aButton
self.add_widget(MainScreenLayout)
#function for when button is pressed
def press_auth(self, instance):
print(str(instance)) #ulimate goal is get ID name and x number, to use later in code
class MyApp(App):
def build(self):
return MainScreen()
if __name__== '__main__':
MyApp().run()

IDs in kivy are usually used in conjunction with kv files to track objectes through inheritance. You probably shouldn't use the instance variable ids as a setter like you do as it is normally set internally to kivy and used as a getter by the developer.
An easier way to do what you want is to just set an arbitrary variable on each Button instance and track the differences there. If you plan on using deep inheritance and that's why you want to use ids then I would set the id on the instance before adding it to the MainScreen.
The first way could be done simply like this:
class MainScreen(GridLayout):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.cols = 1
MainScreenLayout = GridLayout()
MainScreenLayout.cols = 3
#for loop creating buttons with varying IDs
NumberToCreate = 4
for x in range(int(NumberToCreate)):
aButton = Button(text='button text ' + str(x), on_press=self.press_auth)
aButton.my_id = x # or 'button' + str(x) or whatever you want to use to track buttons
MainScreenLayout.add_widget(aButton)
self.add_widget(MainScreenLayout)
#function for when button is pressed
def press_auth(self, instance):
print(str(instance.my_id)) #ulimate goal is get ID name and x number, to use later in code
class MyApp(App):
def build(self):
return MainScreen()
if __name__== '__main__':
MyApp().run()

this is should work for you
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
class MainScreen(GridLayout):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.cols = 1
MainScreenLayout = GridLayout()
MainScreenLayout.cols = 3
# for loop creating buttons with varying IDs
NumberToCreate = 4
for x in range(int(NumberToCreate)):
aButton = Button(text='button text ' + str(x), on_press=self.press_auth)
MainScreenLayout.add_widget(aButton)
# let us make the id similar to the text to simplify the searching
self.ids['button text ' + str(x)] = aButton
self.add_widget(MainScreenLayout)
# function for when button is pressed
def press_auth(self, instance):
# here we can accuses the button id using button text
print(self.ids[instance.text])
class MyApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
MyApp().run()
Update
you can not get the ID by the button instance because its stored as MainScreen ids I my example is to show you how you can get an instance using using one of the instance property like text

Related

How to grab the text from a kivy button?

I'm trying to build this simple GUI for a "voting app" where, by clicking the button with the candidate's ID, +1 will be added in the value of the ID key inside a dictionary. (counting votes on click basically)
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
CANDIDATES = {"T1031" : 0,"T2112" : 0, "T4561" : 0}
class MyLayout(BoxLayout):
orientation = "Vertical"
def __init__(self, **kwargs):
super(MyLayout, self).__init__(**kwargs)
for i in CANDIDATES:
canditate = Button(text=i, on_press=self.button_clicked)
self.add_widget(canditate)
def button_clicked(self, obj):
print("button pressed", obj)
class MyApp(App):
def build(self):
return MyLayout()
if __name__ == "__main__":
MyApp().run()
So how can I grab the text displayed on the button ?
(Also, if any of you guys know...how do I put an ID on the buttons? I tried with writing "id = i" but the GUI doesn't even start when I do that)
Many thanks in advance!
You can access the text value from a button using the Button.text property:
def button_clicked(self, obj):
# Note that obj here is a button object
# You can access it's text value, for example, using obj.text
CANDIDATES[obj.text] += 1
print(f"{obj.text}'s current vote count is now {CANDIDATES[obj.text]}")

Kivy: how to retrieve ids or active states of checkbox (or other widgets) created within python

My app follows 3 steps:
In step 1, the users enter a number (all widgets are in a .kv file -cf the code below).
In step 2, as many labels and checkboxes as the number entered in step 1 are generated. Then the user select some checkboxes and click on the button "OK 2".(Because the number of widgets of the second step can vary,they are created in the .py ―it may not be the best way to do it but I haven't found a better idea).
In step 3, I get the active state of the checkboxes generated in step 2 and according to which one is active or not, I do some more steps.
My question is how can I get the state of the checkboxes? When they are "created", each has an id but these ids don't appear when I print self.ids. Also I get an error if I pass any argument to the getcheckboxes_active def. (None is not callable).
The .py:
import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.checkbox import CheckBox
from kivy.uix.button import Button
from kivy.properties import StringProperty
class MyWidget(BoxLayout):
input_text = StringProperty("10")
def show(self, number):
layout = BoxLayout(padding=10, orientation="vertical")
for each in range(int(number)):
layout2 = BoxLayout(padding=10, orientation="horizontal")
l=Label(bold= True,font_size=20, text='Hello', markup = True)
c= CheckBox(id = "CheckBox"+str(each))
layout2.add_widget(l)
layout2.add_widget(c)
layout.add_widget(layout2)
button = Button(text="OK 2")
button.bind(on_press=self.getcheckboxes_active) # self.getcheckboxes_active(self, "test") give an error None is not callable
layout.add_widget(button)
self.add_widget(layout)
self.input_text = "Done"
def getcheckboxes_active(self, *arg):
'''how to get the active state of all checkboxed created in def show'''
print(self.ids) # CheckBoxes id aren't displayed
print(*arg)
print("State of all checkboxes")
class MyApp_auto(App):
def build(self):
return MyWidget()
MyApp_auto().run()
The .kv: I need to have a .kv because the "step 1 real app" is way more complex than an TextInput and a Button.
<MyWidget>
orientation: "horizontal"
TextInput:
text: root.input_text
id:input
Button:
text:'OK 1'
on_press: root.show(input.text)
The issue here is that the ids dictionary is only populated with id values that were defined in the .kv file, not in python.
However, you can create you own dictionary that contains the references to the CheckBox widgets. Instead of providing the id property upon the creation of the widget, you could populate a dictionary attribute of MyWidget (let's call it check_ref) that links your id with each CheckBox instance:
class MyWidget(BoxLayout):
input_text = StringProperty("10")
check_ref = {}
def show(self, number):
layout = BoxLayout(padding=10, orientation="vertical")
for each in range(int(number)):
layout2 = BoxLayout(padding=10, orientation="horizontal")
l=Label(bold= True,font_size=20, text='Hello', markup = True)
c = CheckBox()
# Stores a reference to the CheckBox instance
self.check_ref["CheckBox"+str(each)] = c
layout2.add_widget(l)
layout2.add_widget(c)
layout.add_widget(layout2)
button = Button(text="OK 2")
button.bind(on_press=self.getcheckboxes_active) # self.getcheckboxes_active(self, "test") give an error None is not callable
layout.add_widget(button)
self.add_widget(layout)
self.input_text = "Done"
def getcheckboxes_active(self, *arg):
'''how to get the active state of all checkboxed created in def show'''
# Iterate over the dictionary storing the CheckBox widgets
for idx, wgt in self.check_ref.items():
print(wgt.active)
# You can also get a specific CheckBox
# print(self.check_ref[--my id--].active)
Possibly a common scenario: From a list of strings, make labels and their corresponding checkbox, using the previously mentioned idea of a dictionary, then show the selected checkbox label as the text of another label.
class BuildRequester(BoxLayout):
chkref = {}
def on_checkbox_active(self,chkbox,value):
self.ids.label2.text = 'Selected ' + self.chkref[chkbox]
def __init__(self, **kwargs):
super(BuildRequester,self).__init__(**kwargs)
prods = [' B_0003',' B_0007',' B_0008', ' B_0200']
for i in range(4):
self.add_widget(Label(text=prods[i],italic=True,bold=True))
chkbox = CheckBox(group='1',color=[0.1,1,0,4])
chkbox.bind(active=self.on_checkbox_active)
self.add_widget( chkbox)
self.chkref[chkbox]= prods[i]

Kivy - Why Label does not update?

I want to change a text of a label but I can't do it, I can see it changing on the shell but not on the UI. I even directly change the text of the label by referencing its id but still its not updating. Anyone knows how to do this?
class MainApp(Screen, EventDispatcher):
title = "Top 10 Plays of 2015"
def __init__(self,*args,**kwargs):
super(MainApp, self).__init__(*args, **kwargs)
def change_curr_title(self, title, *args):
self.title = title
self.ids.lblTitle.text = self.title
print(self.ids.lblTitle.text)
pass
class OtherVideos(BoxLayout, EventDispatcher):
def __init__(self, *args, **kwargs):
super(OtherVideos,self).__init__(*args, **kwargs)
self.loadVideos()
def loadVideos(self):
self.clear_widgets()
con = MongoClient()
db = con.nba
vids = db.videos.find()
vidnum = 1
for filename in vids:
myid = "vid" + str(vidnum)
getfilename = filename['filename']
button = Button(id=myid,
text=getfilename,
color=[0,0.7,1,1],
bold=1)
button.bind(on_release=partial(self.change_Title, getfilename))
self.add_widget(button)
vidnum += 1
def change_Title(self, title, *args):
main = MainApp()
main.change_curr_title(title)
This is the construction of my kivy:
<MainApp>:
....
BoxLayout:
....
BoxLayout:
....some widgets
BoxLayout:
OtherVideos:
...this is where the buttons are generated...
BoxLayout:
Label:
id: lblTitle
text: root.title
Is there anyway to upload my whole code on this? like the file itself, so you guys can look at it.
EDIT: I can easily update the label when I'm making a new method like this without a parameter and binding it to a button through kivy
def update_label(self):
self.ids.lblTitle.text = "New Title"
I don't know why buttons with events created dynamically doesn't work.
Here:
def change_Title(self, title, *args):
main = MainApp() # !
main.change_curr_title(title)
you are creating a new object of screen (MainApp), which isn't connected to anything. To make it work, main should link to the existing instance of MainApp screen.
The OtherVideos box layout needs to have a reference to it, preferably in kv file.
Edit
In order to create a link from MainApp to OtherVideos, create an ObjectProperty:
class OtherVideos(BoxLayout):
main = ObjectProperty()
def __init__(self, *args, **kwargs):
super(OtherVideos,self).__init__(*args, **kwargs)
self.loadVideos()
...
which will be populated in kv file:
OtherVideos:
main: root
...this is where the buttons are generated...
Then, in the change_Title function, use this reference:
def change_Title(self, title, *args):
self.main.change_curr_title(title)

Kivy binding to on_property doesn't seem to work

Essentially, I have a grid with squares, and I keep track of which squares are occupied with a BooleanProperty on each square. Here's a simplified version of all the places in my code I declare the "occupied" property:
class Board(GridLayout):
def __init__(self):
super().__init__()
self.cols = 4
self.grid = []
self.create_slots()
def create_slots(self):
for i in range(10):
self.grid.append([])
for j in range(4):
temp = Square(i,j, "sideboard")
self.grid[i].append(temp)
self.add_widget(temp)
temp.bind(on_occupied = self.do_a_thing)
def do_a_thing(self):
for square in self.children:
#do a thing
class Square(Button):
def __init__(self, row, col, type):
self.row = row
self.col = col
self.id = str(self.row) + "," + str(self.col)
self.type = type
self.occupied = BooleanProperty(False)
super().__init__()
My goal is to bind the "do_a_thing" method to be called each time the value of the square's "occupied" property changes. Because the Square class is used elsewhere in my app, I don't want to set the callback for on_occupied in the kivy language, and I was hoping to avoid creating a Square sub-class just to change the one binding.
When I run my code, it throws no errors, and I've verified that the "occupied" property does actually change. But the "do_a_thing" method never gets fired. Can anyone tell me what I'm doing wrong?
Note that for a property my_property, the change event is called my_property as well. The callback receives two arguments: instance that fired the event, and new value of the property, as shown in the docs. Also, if the class has a method called on_propertyname, this will be called as well.
Here is a self-contained example that works for me:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.properties import BooleanProperty
class Board(GridLayout):
def __init__(self, **kwargs):
super(Board, self).__init__(**kwargs)
for i in range(10):
self.add_widget(Square())
for square in self.children:
print square
square.bind(occupied=self.do_a_thing)
def do_a_thing(self, *args):
print "hello from {}, new state: {}".format(*args)
for square in self.children:
pass
#do a thing
class Square(Button):
occupied = BooleanProperty(False)
def on_occupied(self, *args):
print "Callback defined in class: from {} state {}".format(*args)
class mApp(App):
def build(self):
return Builder.load_string("""
Board:
cols: 4
rows: 3
<Square>:
on_press: self.occupied = ~self.occupied
""")
mApp().run()

Print and clear output on screen with Kivy

I'm trying to print some words on screen with kivy.
What I'm looking for is print first word then sleep 2 seconds then clear first word and print second word
What I'm currently doing:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from random import choice
from time import sleep
xWords = ["hello1", "hello2", "hello3", "hello4", "hello5"]
class Test(GridLayout):
def __init__(self, **kwargs):
super(Test, self).__init__(**kwargs)
self.cols = 1
for x in xrange(2):
# I want it to show frist word then sleep 2 sec then clear first word from screen then print second word
self.add_widget(Label(text = "[b]"+choice(xWords)+"[/b]", markup = True, font_size = "40sp"))
sleep(2)
# then clear all words in screen
for x in xrange(5):
# then show the new 4 words
self.add_widget(Label(text = "[b]"+choice(xWords)+"[/b]", markup = True, font_size = "40sp"))
class TestApp(App):
def build(self):
return Test()
if __name__ == "__main__":
TestApp().run()
How can I do this?
Don't use time.sleep, this blocks the gui because the whole function doesn't return until time.sleep does.
Use Clock.schedule_once instead. Below is a simple example to call a function called update in 2 seconds, you can do whatever you want within that function including scheduling another one.
from kivy.clock import Clock
class Test(GridLayout):
def __init__(self, **kwargs):
super(Test, self).__init__(**kwargs)
Clock.schedule_once(self.update, 2) # 2 is for 2 seconds
def update(self, *args):
self.clear_widgets()
# then add some more widgets here

Categories