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]}")
Related
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.checkbox import CheckBox
class Ckbox(BoxLayout):
orientation = "vertical"
cc =[]
def __init__(self):
super().__init__()
for i in range(5):
self.bb = Button()
self.cq = CheckBox()
self.cc.append(self.cq)
self.add_widget(self.bb)
self.add_widget(self.cq)
class Ckboxapp(App):
def build(self):
return Ckbox()
Ckboxapp().run()
from the above code, i get this
but i want the checkbox and button to be together so i add the button variable to the add widget as self.bb.add_widget(self.cq) and got this
the second image show the checkbox attached to the button but only showing on the last button only. i want itlike in the second iamge but i want it to be on all the buttons. what can i do?
If you put each CheckBox/Button pair in another BoxLayout, I think you will get what you want. Try this:
class Ckbox(BoxLayout):
orientation = "vertical"
def __init__(self):
super().__init__()
for i in range(5):
bl = BoxLayout()
bb = Button(text='Button ' + str(i), size_hint_x=0.85)
cq = CheckBox(size_hint_x=0.15)
bl.add_widget(cq)
bl.add_widget(bb)
self.add_widget(bl)
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
In my Kivy-App, i generate Buttons via a python-class based on a dictionary (in the following example i use a list but that's just an example for the underlying problem).
Within the App, the dictionary gets changed and i want to display that change (obviously) in my App (by adding/ removing/ rearranging the Buttons).
To achieve this, my approach is to either restart the entire App or only reload that particular BoxLayout. Unfortunately, non of my attempts worked out so far and i could not find any (working) solution on the internet.
This is my code example:
Python Code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
buttonlist = []
counter = 0
class MainWindow(BoxLayout):
def addbutton(self):
global buttonlist
global counter
buttonlist.append(counter)
counter += 1
class ButtonBox(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
global buttonlist
for button in buttonlist:
b = Button(text=str(button))
self.add_widget(b)
class KivyApp(App):
def build(self):
return MainWindow()
KivyApp().run()
KV Code:
<MainWindow>:
BoxLayout:
ButtonBox:
Button:
text: "add Button"
on_press: root.addbutton()
My closest attempt was something containing a restart-Method like:
def restart(self):
self.stop()
return KivyApp().run()
and calling:
App.get_running_app().restart()
But for some reason, this does not stop the App but opens a second instance of the App within the first one (resulting in App in App in App in App if pressed often)
You can rebuild the ButtonBox by first calling clear_widgets() on the ButtonBox instance. Here is a modified version of your code that does that:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
kv = '''
<MainWindow>:
BoxLayout:
ButtonBox:
id: box
Button:
text: "add Button"
on_press: root.addbutton()
'''
buttonlist = ['Abba', 'Dabba', 'Doo']
counter = 3
class MainWindow(BoxLayout):
def addbutton(self):
global buttonlist
global counter
buttonlist.append(str(counter))
counter += 1
self.ids.box.reload()
class ButtonBox(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
self.reload()
def reload(self):
# method to rebuild the ButtonBox contents
global buttonlist
self.clear_widgets()
for button in buttonlist:
b = Button(text=str(button))
self.add_widget(b)
class KivyApp(App):
def build(self):
Builder.load_string(kv)
return MainWindow()
KivyApp().run()
I used your kv as a string, just for my own convenience.
I'm making an MP3 Player for a project using Kivy. I am having issues on updating the text of a Button.
The only method that I've used and successfully worked was to update the button text directly, but I want to update a variable that is what the button's text is.
Here's the minimum reproducible example:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class FirstKivy(App):
def __init__(self, **kwargs):
super(FirstKivy, self).__init__(**kwargs)
self.pausePlay = "Play"
def build(self):
layout = BoxLayout(orientation = "vertical")
btn = Button(text = self.pausePlay)
btn.bind(on_press = self.changePausePlay)
layout.add_widget(btn)
return layout
def changePausePlay(self, button):
if self.pausePlay == "Play":
self.pausePlay = "Pause"
elif self.pausePlay == "Pause":
self.pausePlay = "Play"
FirstKivy().run()
I expect the button's text to change from "Play" to "Pause" on click and then from "Pause" to "Play on click again. No error messages are sent.
Any help is appreciated, I'm new to Kivy as well as OOP in Python.
The easiest way to do it is to use kv to build the gui with a StringProperty to hold the Button text:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty
kv = '''
BoxLayout:
orientation: 'vertical'
Button:
text: app.pausePlay
on_press: app.changePausePlay(self)
'''
class FirstKivy(App):
pausePlay = StringProperty('Play')
def __init__(self, **kwargs):
super(FirstKivy, self).__init__(**kwargs)
def build(self):
layout = Builder.load_string(kv)
return layout
def changePausePlay(self, button):
if self.pausePlay == "Play":
self.pausePlay = "Pause"
elif self.pausePlay == "Pause":
self.pausePlay = "Play"
FirstKivy().run()
Some key points. The kv language automatically sets up bindings when it can (creating the same gui in Python does not). The StringProperty allows kv to set up the binding so that any change in pausePlay will be reflected in the Button text.
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]