How to use the screen manager without .kv file - python-3.x

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()

Related

Python kivy RecursionError: maximum recursion depth exceeded in comparison

I am trying to create a simple app with kivy in python
but when i run this code i get following error
RecursionError: maximum recursion depth exceeded in comparison
import wikipedia
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
class GridLayout(GridLayout):
def __init__(self, **kwargs):
super(GridLayout, self).__init__()
# Number of columns
self.cols = 1
# Second grid Layout
self.second_layout = GridLayout()
self.second_layout.cols = 2
# Creating a text field to show the result of entered query
self.query_result = TextInput(text='', size_hint_y=0.8)
self.second_layout.add_widget(self.query_result) # Adding query result on the screen
# Creating a text input field to get the query from user
self.query = TextInput(text='', multiline=False, hint_text="Enter your Query", size_hint_y=0.1, font_size=20)
self.second_layout.add_widget(self.query)
# Adding Second layout on the screen
self.add_widget(second_layout)
# Creating a submit button
self.submit_button = Button(text="Submit", size_hint_y=0.1, font_size=40, on_press=self.submit)
self.add_widget(self.submit_button)
def submit(self, instance):
try:
query_result_from_wikipedia = wikipedia.page(self.query.text).summary
self.query_result.text = query_result_from_wikipedia
except:
popup = Popup(title='Query Not Found',
content=Label(text='Try to Search Anything else'),
size_hint=(None, None), size=(400, 400))
popup.open()
class MyApp(App):
def build(self):
return GridLayout()
if __name__ == '__main__':
MyApp().run()
But when i remove the second gridlayout from it it runs without errors
The first problem is the name of your class. Don't name a class the same as its subclass:
class GridLayout(GridLayout):
is likely to cause problems. Just change it to something like:
class MyGridLayout(GridLayout):

How do we initiate other actions after switching to the second screen in Python Kivy Screen Manager?

I'm giving an attempt at using the ScreenManager in Kivy.
To my expectation, the code below is supposed to accommodate two different screens: the first screen ScreenOne is a Play button and the second screen ScreenTwo is where the gameplay action is supposed to begin right away.
Problem is, I'm getting just an empty black screen when I switch to ScreenTwo.
# ---------- Original file: kivytut2.py ----------
# Importations
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.core.audio import SoundLoader
# Creating my kv code in the Python file
Builder.load_string("""
<ScreenOne>:
BoxLayout:
Button:
text: "Play"
background_normal: ''
background_color: 0, 255, 255, .85
on_press:
# You can define the duration of the change
# and the direction of the slide
root.manager.transition.direction = 'up'
root.manager.transition.duration = 0.5
root.manager.current = 'screen_two'
""")
# Create a class for all screens in which you can include
# helpful methods specific to that screen
class ScreenOne(Screen):
pass
class ScreenTwo(Screen):
# Detect collision, to comment out
def collides(rect1, rect2):
r1x = rect1[0][0]
r1y = rect1[0][1]
r2x = rect2[0][0]
r2y = rect2[0][1]
r1w = rect1[1][0]
r1h = rect1[1][1]
r2w = rect2[1][0]
r2h = rect2[1][1]
if (r1x < r2x + r2w and r1x + r1w > r2x and r1y < r2y + r2h and r1y + r1h > r2y):
return True
else:
return False
def keyboard_events(self, **kwargs):
super().keyboard_events(**kwargs)
# Requesting keyboard event and setting its callbacks
self._keyboard = Window.request_keyboard(self._on_keyboard_closed, self)
self._keyboard.bind(on_key_down=self._on_key_down)
self._keyboard.bind(on_key_up=self._on_key_up)
# The Fly rectangle
with self.canvas:
self.enemy = Rectangle(source="assets/theenemy.png", pos=(350, 350), size=(100, 85))
self.player = Rectangle(source="assets/theplayer.png", pos=(150, 150), size=(100, 85))
self.keysPressed = set()
# Refresh frame every "second argument" seconds
Clock.schedule_interval(self.move_step, 0)
# Background musics
sound = SoundLoader.load("assets/gp.wav")
if sound:
print("Sound found at %s" % sound.source)
print("Sound is %.3f seconds" % sound.length)
sound.play()
# The keyboard callback when closed?
def _on_keyboard_closed(self):
self.keyboard.unbind(on_key_down=self.on_key_down)
self.keyboard.unbind(on_key_up=self.on_key_up)
self.keyboard = None
# The keyboard callback to change player position on key down
def _on_key_down(self, keyboard, keycode, text, modifiers):
self.keysPressed.add(text)
def _on_key_up(self, keyboard, keycode):
text = keycode[1]
if text in self.keysPressed:
self.keysPressed.remove(text)
def move_step(self, dt):
# Bottom left is origin, +x is right, -x is left, +y is up, -y is down
currentx = self.player.pos[0]
currenty = self.player.pos[1]
# Move hundred pixels per second
step_size = 100 * dt
if "w" in self.keysPressed:
currenty += step_size
if "s" in self.keysPressed:
currenty -= step_size
if "a" in self.keysPressed:
currentx -= step_size
if "d" in self.keysPressed:
currentx += step_size
self.player.pos = (currentx, currenty)
if collides((self.player.pos, self.player.size), (self.enemy.pos, self.player.size)):
print("colliding, sire!")
else:
print("not colliding, sire.")
# The ScreenManager controls moving between screens
screen_manager = ScreenManager()
# Add the screens to the manager and then supply a name
# that is used to switch screens
screen_manager.add_widget(ScreenOne(name="screen_one"))
screen_manager.add_widget(ScreenTwo(name="screen_two"))
class KivyTut2App(App):
def build(self):
return screen_manager
sample_app = KivyTut2App()
sample_app.run()
In this, I'm using the Kivy Example file named kivytut2.py and also a tutorial from YT https://www.youtube.com/watch?v=5t1VXHICv-Q&t=388s
The whole idea is to use the ScreenManager in a very basic main menu.
I do appreciate every comment and directions to solve this one out! Cheers!
You can define the <ScreenTwo> rule just as you have done for ScreenOne in your Builder.load_string() call. Just add <ScreenTwo>: to the end of that string, and then define what you want to appear in ScreenTwo (as you have done for ScreenOne). Of course, that is only useful if you want something to appear in ScreenTwo.
I suggest that you start by adding an on_enter() method to ScreenTwo like:
class ScreenTwo(Screen):
def on_enter(self, *args):
self.keyboard_events()
This will be executed whenever you switch to ScreenTwo and it calls your keyboard_events() method of ScreenTwo. I think that will begin your game (after you fix some errors in that method).

how to reference kivy widget ids dynamically created using kv language through a for loop in main python file?

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.

How can I add widgets to an existing screen?

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))

How do I create a button with Kivy?

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

Resources