my application is built from a .kv file.
I want to add buttons to a Screen from my python file.
class Profiles(Screen):
def suh(self):
for i in range(5):
self.add_widget(Button(text=i))
The suh function seem to have no effect. Is this because the function is called after the app is built? Is there a better way to do this?
Solution 1
Since Screen is a RelativeLayout, use on_pre_enter or on_enter to inovke method, suh(), plus add size and pos to Button widget.
Solution 2
Use a container e.g. BoxLayout or Gridayout on top of Screen.
Note
Whenever widget is added to a screen via on_pre_enter or on_enter, use on_pre_leave or on_leave to remove the widget. This is to prevent doubling your widget each time the screen is entered.
If the widgets are cleared too fast, use Clock.schedule_once with a time interval.
Note 1: Screen Events
Kivy Screen has the following events.
Kivy Screen » Events
on_pre_enter: ()
Event fired when the screen is about to be used: the entering
animation is started.
on_enter: ()
Event fired when the screen is displayed: the entering animation is
complete.
on_pre_leave: ()
Event fired when the screen is about to be removed: the leaving
animation is started.
on_leave: ()
Event fired when the screen is removed: the leaving animation is
finished.
Note 2: Screen is a RelativeLayout
Kivy Screen » RelativeLayout
Please note that by default, a Screen displays nothing: it’s just
a RelativeLayout. You need to use that class as a root widget for
your own screen, the best way being to subclass.
Warning
As Screen is a RelativeLayout, it is important to understand
the Common Pitfalls.
Example - Solution 1
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.lang import Builder
Builder.load_string("""
<Profiles>:
# on_pre_enter: self.suh()
on_enter: self.suh()
""")
class Profiles(Screen):
def suh(self):
for i in range(5):
self.add_widget(Button(text=str(i), size_hint=(0.1, 0.1), pos=(i*100, i*100)))
sm = ScreenManager()
sm.add_widget(Profiles(name='profiles'))
class TestApp(App):
def build(self):
return sm
if __name__ == "__main__":
TestApp().run()
Output - Solution 1
Example - Solution 2
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.lang import Builder
Builder.load_string("""
<Profiles>:
# on_pre_enter: self.suh()
on_enter: self.suh()
BoxLayout:
id: container
""")
class Profiles(Screen):
def suh(self):
for i in range(5):
self.ids.container.add_widget(Button(text=str(i), size_hint=(0.1, 0.1)))
sm = ScreenManager()
sm.add_widget(Profiles(name='profiles'))
class TestApp(App):
def build(self):
return sm
if __name__ == "__main__":
TestApp().run()
Output - Solution 2
Add the widgets through the App class in stead of the Screen. So you would have to create a function that creates the widgets, and in your build function, you would have to use Clock.schedule_once from the Clock module to run the other function. Example:
class TestApp(App):
def build(self):
Clock.schedule_once(self.yoyo, 0)
return MainWin()
def yoyo(self, *args):
for i in memu_acc:
self.root.ids["prof"].ids["pro"].add_widget(Button(text=i))
Related
I have dynamically created textinput widgets in a .kv file using kv language. However i would like to set the text in main python file using a for loop by referencing their ids.
I have written the following code:
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
class setText(Widget):
def __init__(self,**kwargs):
super().__init__(**kwargs)
texts=['txtip1','txtip2']
IDS=self.ids.keys()
for i in range(IDS):
self.ids[IDS[i]].text=texts[i]
class DocApp(App):
def build(self):
return Builder.load_file("docapp\Doc.kv")
return setText()
if __name__=="__main__":
DocApp().run()
Doc.kv
# File: docapp.py
#: import TextInput kivy.uix.textinput.TextInput
GridLayout:
cols:1
on_kv_post:
[self.add_widget(TextInput(id=str(i))) for i in range(2)]
I have searched through other questions but couldn't find suitable solution. Please help
The problem in your code, is that id only works in the kv syntax.
So you need to reference your widgets some other way.
In case you need to do it that way, dynamically adding in kv and accessing in a loop in python, you could try something like the example below.
The text will change after 5 seconds in this case. If you know that the only children of this gridlayout will be the widgets you want to access, you might as well just iterate the children.
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
KV = """
#: import TextInput kivy.uix.textinput.TextInput
GridLayout:
cols:1
on_kv_post:
for i in range(2): app.dynamic_widgets.append(TextInput())
for wid in app.dynamic_widgets: self.add_widget(wid)
"""
class DocApp(App):
dynamic_widgets = []
def build(self):
Clock.schedule_once(self.set_text, 5)
return Builder.load_string(KV)
def set_text(self, dt):
texts=['txtip1','txtip2']
for text, wid in zip(texts, self.dynamic_widgets):
wid.text = text
if __name__=="__main__":
DocApp().run()
Now if you remove one of the widgets, you probably want to remove it from the list also.
You can create the TextInput widgets dynamically in kv by creating the kv dynamically. Here is a modification of your code that does that:
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
class DocApp(App):
def build(self):
# schedule the call to add the texts
Clock.schedule_once(self.setText)
# return the root widget from the kvString
return layoutRoot
def setText(self, dt):
texts=['txtip1','txtip2']
IDS=self.root.ids.keys()
i = 0
for id in IDS:
self.root.ids[id].text=texts[i]
i += 1
if __name__=="__main__":
# Create the kv string here
kvString = '''
GridLayout:
cols:1
'''
for i in range(2):
kvString += ' TextInput:\n' + ' id: ' + str(i) + '\n'
# Load the kv string
layoutRoot = Builder.load_string(kvString)
# and finally, run the App
DocApp().run()
I have eliminated the setText class (assuming it was only a holder for the setText method), and placed that method in the App. The Clock.schedule_once() schedules the call to setText to happen after the App has been created.
I have tried to create the multiple screen(i.e tried to use Screen manager) without the use of .kv file i.e all in .py file.But i am unable to create it . please help me out . Basically I want to know how to use screen manager without. Kv file
I had tried to make it but i am unable to do that
# kivy code
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
class SwitchApp(App):
# define build function
def build(self):
# Create the manager
sm = ScreenManager()
# Add few screens
for i in range(4):
screen = Screen(name='Title %d' % i)
sm.add_widget(screen)
# By default, the first screen added into the ScreenManager will be
# displayed. You can then change to another screen.
# Let's display the screen named 'Title 2'
# A transition will automatically be used.
sm.current = 'Title 2'
return sm
# Run the kivy app
if __name__ == '__main__':
SwitchApp().run()
i added the widget for each screen
(screen.add_widget(Label(text="%d" % i)))
and i selected the screen 1 to be presented, you can switch between them
sm.current = 'Title 1'
kivy code
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
class SwitchApp(App):
# define build function
def build(self):
# Create the manager
sm = ScreenManager()
# Add few screens
for i in range(4):
screen = Screen(name='Title %d' % i)
screen.add_widget(Label(text="%d" % i))
sm.add_widget(screen)
sm.current = 'Title 1'
return sm
Run the kivy app
if name == 'main':
SwitchApp().run()
I'm starting to learn Kivy.
The code below generates a 10x10 button grid:
from kivy.uix.gridlayout import GridLayout
from kivy.app import App
from kivy.uix.button import Button
class MyApp(App):
def build(self):
layout = GridLayout(cols=10)
for i in range (1, 101):
layout.add_widget(Button(text=str(i)))
return layout
MyApp().run()
Now, I'd like to add a png image to an independent layer, which randomly "wander" over these buttons, independently.
Then the user should click on the button on which the image is going, as in a game.
That is, the image should not be clickable and will be shown only visually over the buttons, meanwhile, the buttons should respond perfectly as if there was no image over them.
How to do this?
You can draw the image in the Canvas of the GridLayout using a Rectangle. And the position can be updated using Clock_schedule_interval(). Like this:
from kivy.clock import Clock
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.uix.gridlayout import GridLayout
from kivy.app import App
from kivy.uix.button import Button
class MyApp(App):
def build(self):
layout = GridLayout(cols=10)
with layout.canvas.after:
Color(1,1,1,0.5) # if you want to see through the image
self.bg = Rectangle(source='KQxab.png') # source is the image
for i in range (1, 101):
layout.add_widget(Button(text=str(i)))
Clock.schedule_interval(self.update_bg, 1.0/24.0) # schedule the image movement
return layout
def update_bg(self, dt):
self.bg.pos = (self.bg.pos[0] + 1, self.bg.pos[1] + 1)
MyApp().run()
This code just moves the image in a straight line, but you can improve that.
I was trying to make an window with two block with vertical box layout, with upper widget larger than lower one. But rather than that, the widgets are stacking on top of other at the bottom left corner, both being of same size.
Here is my code
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.codeinput import CodeInput
from kivy.core.window import Window
from kivy.uix.button import Button
Window.maximize()
class Editor(Widget):
def __init__(self, *arg, **kwarg):
super(Editor, self).__init__(*arg, **kwarg)
self.size_hint= (1, 0.8)
self.add_widget(Button(text= "1"))
class Output(Widget):
def __init__(self, *arg, **kwarg):
super(Output, self).__init__(*arg, **kwarg)
self.size_hint= (1, 0.2)
self.add_widget(Button(text= "2"))
class IDE(BoxLayout):
def __init__(self, *arg, **kwarg):
super(IDE, self).__init__(*arg, **kwarg)
self.orientation= "vertical"
box1= Editor()
self.add_widget(box1)
box2= Output()
self.add_widget(box2)
class MainApp(App):
def build(self):
return IDE()
if __name__=="__main__":
MainApp().run()
(The buttons are used in Output and Editor class just to check their respective parent widget position in resultant window)
Someone help me to figure out what am I doing wrong.
Your Editor and Output behave as expected, but are completely invisible. What you are seeing are their own child widgets, the two Buttons, each of which has no position or size applied to it and so takes the default of pos (0, 0) and size (100, 100).
Make the Editor and Output classes some kind of layout, e.g. FloatLayout, or simply remove them and add the Buttons directly to the BoxLayout.
I am hoping to eventually create a button to pause/start/end a pong app I built as a learning exercise. However, to understand the basics of how to create a button in the first place, and assign a response to it I am just wanting to create a button that notifies me every time the button state changes. Here is the python code I have so far:
import kivy.uix.button as kb
from kivy.app import App
from kivy.uix.widget import Widget
class Button_Widget(Widget):
def callback(instance, value):
print('The button <%s> state is <%s>' % (instance, value))
btn1 = kb.Button(text='Hello World 1')
btn1.bind(on_press=callback)
class ButtonApp(App):
def build(self):
button = Button_Widget()
return button
with the associated kv file:
#:kivy 1.0.9
<Button_Widget>:
size: 100, 100
canvas:
Rectangle:
pos = self.pos
size = self.size
So far the only resources I have found are these (1 and 2) Kivy turorials, which aren't too helpful. Or at least, I don't understand their language enough yet for them to be useful
Let's see the simplest code to create a button in kivy
from kivy.app import App
from kivy.uix.button import Button
class your_app_name(App):
def build(self):
button = Button(text="Button")
return button
your_app_name().run()
You can also add command or event to a button by adding on_press= function_name
Solution
In the class Button_Widget(), you have to override the constructor, __init__() so that you can add the button widget to the root widget. Please refer to the example for details.
References
Programming Guide » Kivy Basics » Create an application
Programming Guide » Kv language
Kivy Language
Event dispatcher » bind() Bind an event type or a property to a callback.
Example
main.py
import kivy.uix.button as kb
from kivy.app import App
from kivy.uix.widget import Widget
class Button_Widget(Widget):
def __init__(self, **kwargs):
super(Button_Widget, self).__init__(**kwargs)
btn1 = kb.Button(text='Hello World 1')
btn1.bind(on_press=self.callback)
self.add_widget(btn1)
def callback(self, instance):
print('The button %s state is <%s>' % (instance, instance.state))
class ButtonApp(App):
def build(self):
return Button_Widget()
if __name__ == "__main__":
ButtonApp().run()
button.kv
#:kivy 1.10.1
<Button_Widget>:
size: 100, 100
canvas:
Rectangle:
pos: self.pos
size: self.size
Output