I need to know how to reference dynamically created objects. Like in the example, how can I change text of the buttons after creation? Normaly I would do it using their ids, but as far as I know, you can't give ids to objects created in python.
.py code
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.button import Button
from kivy.clock import mainthread
NUMBER_OF_BUTTONS = 5
class MapScreen(Screen):
#mainthread
def on_enter(self):
for i in range(NUMBER_OF_BUTTONS):
button = Button(text="B_" + str(i))
self.ids.grid.add_widget(button)
class Test(App):
pass
Test().run()
.kv code
ScreenManager:
MapScreen:
<MapScreen>:
name: 'map'
GridLayout:
id: grid
cols: 1
You could just keep a list of the Buttons:
def on_enter(self):
self.buttons = []
for i in range(NUMBER_OF_BUTTONS):
button = Button(text="B_" + str(i))
self.buttons.append(button)
self.ids.grid.add_widget(button)
or you could create your own dictionary:
def on_enter(self):
self.buttons = {}
for i in range(NUMBER_OF_BUTTONS):
text = "B_" + str(i)
button = Button(text=text)
self.buttons[text] = button
self.ids.grid.add_widget(button)
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)
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.
What I want to do is take input from kivy.uix.textinput.TextInput() and show it on the screen.
I am new to gui Programming and I think it is an easy task.
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
class MyWindowApp(App):
def __init__(self):
super(MyWindowApp, self).__init__()
self.lbl = Label(text='Read Me!')
self.inp = TextInput(multiline=False,
size_hint =(1, 0.05),
pos_hint = {"x":0, "y":0.05})
def build(self):
self.inp.bind(on_text_validate=self.on_enter)
#self.bt1.bind(on_press=self.clk)
layout = FloatLayout()
layout.orientation = 'vertical'
layout.add_widget(self.lbl)
layout.add_widget(self.inp)
return layout
def on_enter(self,value):
print(value)
def clk(self, obj):
print ('input')
x = input()
self.lbl.text = x
window = MyWindowApp()
window.run()
when i run the code, I get the regular output output.
when I type say "hello world" in the textbox, this is the output:
<kivy.uix.textinput.TextInput object at 0x03F5AE30>
I do not get what I typed.
please suggest what should I do
Modify the following ...
def on_enter(self, value):
print(value.text)
I am working on a random dice roller project, Im new to python (I started learning it on my own about 2 months ago) and thought that it would be a good beginner project to work on. But I keep having trouble binding my functions to my buttons. I keep getting an AttributeError: 'Dice' object has no attribute 'displaybox'. Which I dont really understand because my Dice class clearly has a displaybox object inside of it. Thanks for any help!
Here is my Python code:
from random import randint
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.label import Label
class Dice(GridLayout):
def __init__(self, **kwargs):
super(Dice, self).__init__(**kwargs)
# Define Buttons and labels
d4btn = Button(text='D4',
bold = True,
on_press = self.rolld4)
d6btn = Button(text='D6',
bold = True)
d8btn = Button(text='D8',
bold = True)
d10btn = Button(text='D10',
bold = True)
d12btn = Button(text='D12',
bold = True)
d20btn = Button(text='D20',
bold = True)
displaybox = (Label(text=('You have not rolled, \n please select a number to roll.')))
infobox = (Label(text='Random Dice Project \nJoshua \n10/17/2017'))
# Create Buttons and Labels
self.add_widget(d4btn)
self.add_widget(d6btn)
self.add_widget(d8btn)
self.add_widget(d10btn)
self.add_widget(d12btn)
self.add_widget(d20btn)
self.add_widget(displaybox)
self.add_widget(infobox)
# Button Behavior
def rolld4(self, obj):
self.displaybox.text = print(str(randint(1,4)))
class DiceRollerApp(App):
def build(self):
return Dice()
if __name__ == '__main__':
DiceRollerApp().run()
And here is my Kivy code:
#:kivy 1.10.0
<Dice>:
cols: 2
rows: 4
Edit: I got a working version completed. Thanks for your help!
My recommendation is to define all your widgets in your kv file, and use ObjectProperty to hook up to your display box. Please refer to the example below for details.
Example
main.py
from random import randint
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
class Dice(GridLayout):
displaybox = ObjectProperty(None)
# Button Behavior
def rolld4(self):
self.displaybox.text = str(randint(1, 4))
class DiceRollerApp(App):
def build(self):
return Dice()
if __name__ == '__main__':
DiceRollerApp().run()
diceroller.kv
#:kivy 1.10.0
<MyButton#Button>:
bold: True
<Dice>:
displaybox: displaybox
cols: 2
rows: 4
MyButton:
text: "D4"
on_press: root.rolld4()
MyButton:
text: "D6"
MyButton:
text: "D8"
MyButton:
text: "D10"
MyButton:
text: "D12"
MyButton:
text: "D20"
Label:
id: displaybox
text: 'You have not rolled, \nplease select a number to roll.'
Label:
id: infobox
text: 'Random Dice Project \nJoshua \n10/17/2017'
Output
in the init() of your dice you need to instanciate the displaybox as an attribute of the dice then you can manipulate it in the button behaviour, so your code is correct just add the self parameter and everything would work fine
here is the code:
-kv:
<Dice>:
cols: 2
rows: 4
-py:
from kivy.app import App
from random import randint
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
class Dice(GridLayout):
def __init__(self, **kwargs):
super(Dice, self).__init__(**kwargs)
# Define Buttons and labels
d4btn = Button(text='D4',
bold = True,
on_press = self.rolld4)
d6btn = Button(text='D6',
bold = True)
d8btn = Button(text='D8',
bold = True)
d10btn = Button(text='D10',
bold = True)
d12btn = Button(text='D12',
bold = True)
d20btn = Button(text='D20',
bold = True)
#Here you instantiate it ass an attribute of the dice
self.displaybox = (Label(text=('You have not rolled, \n please select a number to roll.')))
infobox = (Label(text='Random Dice Project \nJoshua \n10/17/2017'))
# Create Buttons and Labels
self.add_widget(d4btn)
self.add_widget(d6btn)
self.add_widget(d8btn)
self.add_widget(d10btn)
self.add_widget(d12btn)
self.add_widget(d20btn)
#Then you add it to the layout
self.add_widget(self.displaybox)
self.add_widget(infobox)
# Button Behavior
def rolld4(self, obj):
self.displaybox.text = str(randint(1,4))
print(str(randint(1, 4)))
class DiceRollerApp(App):
def build(self):
return Dice()
if __name__ == '__main__':
DiceRollerApp().run()
proof:
I hope I helped !
I can't find the syntax for setting on_press in python code to change the screen anywhere. I keep getting errors for anything like Button(text = 'hi', on_press = self.current = 'start_menu. Here's the code and it works as is.
class LoadMenu(Screen):
def __init__(self, **kwargs):
super(LoadMenu, self).__init__(**kwargs)
Clock.schedule_once(self.update)
def update(self, dt):
L = [x for x in range(len(os.listdir('saves')))]
for x in L:
x = self.add_widget(Button(text = os.listdir('saves')[x]))
I haven't positioned the buttons so they just are on top of each other but I can fix that later. What I need to happen is for each button to change to the play screen on press so that will be the same for each button but I also need each one to load the Shelve file they a referencing.(I know I'll need another function for that) Can I have an on_press trigger two events at once, and again how do I set it in the python code?
Consider the following programme:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.properties import StringProperty
dirlist = ['aaa', 'bbb', 'ccc', 'ddd']
class MyButton(Button):
prop = StringProperty('')
def on_press(self):
print "Class-defined on_press handler (I'm {})".format(self.text)
def other_on_press_handler(sender):
print "other_on_press_handler, from {}".format(sender.text)
def some_func(text):
print "yeah: " + text
class LoadMenu(Screen):
def __init__(self, **kwargs):
super(LoadMenu, self).__init__(**kwargs)
Clock.schedule_once(self.update)
def on_press_handler(self, sender):
print "on_press_handler, from {}".format(sender.text)
self.parent.current = 'sc2'
def yet_another_on_press_handler(self, sender):
print "yet_another_on_press_handler, from {}".format(sender.text)
self.parent.current = 'sc2'
def update(self, dt):
for x in range(len(dirlist)):
my_b = Button(text = dirlist[x], on_press=self.on_press_handler)
self.parent.ids.button_container.add_widget(my_b)
if x > 1:
my_b.bind(on_press=other_on_press_handler)
if x == 3:
my_b.bind(on_press=lambda sender: some_func("Whoa, lambda was here ({})".format(sender.text)))
for x in range(len(dirlist)):
my_b = MyButton(text = 'my '+ dirlist[x], prop="{} {}".format(dirlist[x], x))
self.parent.ids.button_container.add_widget(my_b)
my_b.bind(on_press=self.yet_another_on_press_handler)
root = Builder.load_string("""
ScreenManager:
LoadMenu:
name: 'sc1'
GridLayout:
cols: 4
id: button_container
Screen:
name: 'sc2'
BoxLayout:
Button:
text: "Go back"
on_press: root.current = 'sc1'
""")
class MyApp(App):
def build(self):
return root
if __name__ == '__main__':
a = MyApp()
a.run()
Let's start by looking at the update method in LoadMenu: In the first loop, a bunch of buttons is generated, and each receives an on_press callback at creation. The last two buttons in the loop get bound to another callback, and the last example shows how to use a lambda expression to generate a callback.
In the second for loop, we instantiate object of class MyButton, a child of Button. Note that we also define an on_press handler in the class definition; this gets called in addition to other functions we may have bound.
But really, this is actually all pretty nicely explained in the kivy Events and Properties docs.