I'm putting together a Kivy app and am having some problems working out how to change screens at an arbitrarily chosen point within the python code.
In the following example, I would like to know how to switch from Screen2 back to Screen1 by executing a function my main.py file.
Here's my main.py:
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
from kivy.properties import ObjectProperty
from functools import partial
import random
import time
class ScreenOne(Screen):
pass
class ScreenTwo(Screen):
def __init__(self,**kwargs):
super(ScreenTwo, self).__init__(**kwargs)
self.displayScreenThenLeave()
def displayScreenThenLeave(self):
# 'time.sleep(3)' is a stand-in for "execute some code here"
time.sleep(3)
self.changeScreen()
# I want this function to send the user back to ScreenOne.
def changeScreen(self):
pass
class Manager(ScreenManager):
screen_one = ObjectProperty(None)
screen_two = ObjectProperty(None)
class ScreensApp(App):
def build(self):
m = Manager(transition=NoTransition())
return m
if __name__ == "__main__":
ScreensApp().run()
And here's my screens.kv:
#:kivy 1.8.0
<ScreenOne>:
BoxLayout:
orientation: "vertical"
size: root.size
spacing: 20
padding: 20
Label:
text: "Main Menu"
Button:
text: "Button 1"
on_release: root.manager.current = "screen2"
<ScreenTwo>:
BoxLayout:
orientation: "vertical"
size: root.size
spacing: 20
padding: 20
Label:
id: label
text: "Welcome to Screen 2, please wait three seconds"
<Manager>:
id: screen_manager
screen_one: screen_one
screen_two: screen_two
ScreenOne:
id: screen_one
name: "screen1"
manager: screen_manager
ScreenTwo:
id: screen_two
name: "screen2"
manager: screen_manager
As should be pretty evident, I'm a total beginner at Kivy, so I'd really appreciate it if you could show me exactly what I need to change, including the specific syntax that should be used.
Thanks in advance for your time and wisdom.
Here is a simple 'game' example based on your additional info. When a player enters the game screen, their health points are bleeding. When the pool reaches zero, they are sent back to menu screen.
main.py:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
from kivy.properties import ObjectProperty, NumericProperty
import time
import threading
class ScreenOne(Screen):
pass
class ScreenTwo(Screen):
health_points = NumericProperty(100)
def __init__(self, **kwargs):
super(ScreenTwo, self).__init__(**kwargs)
def on_health_points(self, instance, value):
if value < 1:
self.changeScreen()
def on_enter(self, *args):
thread = threading.Thread(target=self.bleed)
thread.daemon = True
thread.start()
def bleed(self, *args):
while self.health_points > 0:
self.health_points -= 5
time.sleep(0.1)
def displayScreenThenLeave(self):
self.changeScreen()
def changeScreen(self):
if self.manager.current == 'screen1':
self.manager.current = 'screen2'
else:
self.manager.current = 'screen1'
class Manager(ScreenManager):
screen_one = ObjectProperty(None)
screen_two = ObjectProperty(None)
class ScreensApp(App):
def build(self):
m = Manager(transition=NoTransition())
return m
if __name__ == "__main__":
ScreensApp().run()
screens.kv:
#:kivy 1.8.0
<ScreenOne>:
BoxLayout:
orientation: "vertical"
size: root.size
spacing: 20
padding: 20
Label:
text: "Main Menu"
Button:
text: "Button 1"
on_release:
root.manager.current = "screen2"
# reset health_points
root.manager.ids.screen_two.health_points = 100
<ScreenTwo>:
BoxLayout:
orientation: "vertical"
size: root.size
spacing: 20
padding: 20
Label:
id: label
text: "health points: " + str(root.health_points)
<Manager>:
id: screen_manager
screen_one: screen_one
screen_two: screen_two
ScreenOne:
id: screen_one
name: "screen1"
manager: screen_manager
ScreenTwo:
id: screen_two
name: "screen2"
manager: screen_manager
Make these little changes
#import Clock to create a schedule
from kivy.clock import Clock
class ScreenTwo(Screen):
def __init__(self,**kwargs):
super(ScreenTwo, self).__init__(**kwargs)
#this is event that is fired when the screen is displayed.
def on_enter(self, *args):
self.displayScreenThenLeave()
def displayScreenThenLeave(self):
#schedued after 3 seconds
Clock.schedule_once(self.changeScreen, 3)
def changeScreen(self, *args):
#now switch to the screen 1
self.parent.current = "screen1"
for more imformation go to these links Screen Manager, Clock
Related
I am having trouble understanding how to inherit the functionality of one class into another with Kivy. I understand the error message ('LoadDialog_Directory' object has no attribute 'manager'), but I'm just not grasping exactly how to fix it. I believe I need to do something with the function below, but ultimately, I do not know.
def __init__(self, **kwargs):
super().__init__(**kwargs)
The goal of this script is to be able to select a specific driver as the path for the filechooser. I choose this method vs. others because most were using Kivy 1.11.1, this version has a lot of deprecated functions that do not work with 2.0.
.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.uix.spinner import SpinnerOption
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.utils import platform
from kivy.properties import StringProperty
import os
import string
class WindowManager(ScreenManager):
pass
class MyOption(SpinnerOption):
pass
class LoadDialog_Directory(FloatLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
def dir_driver(self):
x = self.manager.get_screen("first")
return str(x.ids.drive_dir.text)
class FirstWindow(Screen):
def get_drives(self):
drives = ['%s:' % d for d in string.ascii_uppercase if os.path.exists('%s:' % d)]
return drives
def dismiss_popup(self):
self._popup.dismiss()
def show_load_directory(self):
content = LoadDialog_Directory(load=self.directroy_path, cancel=self.dismiss_popup)
self._popup = Popup(title="Load file", content=content, size_hint=(0.9, 0.9))
self._popup.open()
def directroy_path(self, path, filename):
self.ids.text_input_directory.text = str(filename[0])
self.dismiss_popup()
kv_main = Builder.load_file('main.kv')
#
class MyApp(App):
def build(self):
return kv_main
if __name__ == '__main__':
MyApp().run()
main.kv
#:include first.kv
WindowManager:
FirstWindow:
<LoadDialog_Directory>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
dirselect: True
path: root.dir_driver()
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(filechooser.path, filechooser.selection)
first.kv
<FirstWindow>
name: 'first'
GridLayout:
cols: 1
BoxLayout:
orientation: "horizontal"
TextInput:
BoxLayout:
orientation: "horizontal"
Spinner:
id: drive_dir
text: "Root Drive"
halign: 'center'
option_cls: "MyOption"
values: root.get_drives()
Button:
text: "Set Result Directory"
on_release: root.show_load_directory()
TextInput:
id: text_input_directory
disabled: True
text: text_input_directory.text
BoxLayout:
size_hint: (0.01, 1)
orientation: "horizontal"
TextInput:
Side Note: The reason for the extra blank TextInput is because the Spinner will not function (show drivers) if it is taking too much of the App.
After a few hours of trail and error I finally got it to work, but I have no idea why it works. Here is what changed:
New variable in class LoadDialog_Directory(FloatLayout) - input_pth
Called input_pth into my content variable within class FirstWindow(Screen) function show_load_directory(self)
Set my filechooser.path to root.input_pth in the main.kv file.
I do not understand how the input_pth variable within content is able to reference the class LoadDialog_Directory(FloatLayout) without having to pass something like:
def __init__(self, input_pth, **kwargs):
super(LoadDialog_Directory, self).__init__()
self.input_pth = input_pth
.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.uix.spinner import SpinnerOption
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.utils import platform
from kivy.properties import StringProperty
import os
import string
class WindowManager(ScreenManager):
pass
class MyOption(SpinnerOption):
pass
class LoadDialog_Directory(FloatLayout):
input_pth = StringProperty()
load = ObjectProperty()
cancel = ObjectProperty()
class FirstWindow(Screen):
def drive_list(self):
drives = ['%s:' % d for d in string.ascii_uppercase if os.path.exists('%s:' % d)]
return drives
def dismiss_popup(self):
self._popup.dismiss()
def show_load_directory(self):
content = LoadDialog_Directory(load=self.directroy_path, cancel=self.dismiss_popup, input_pth=self.ids.drive_dir.text)
self._popup = Popup(title="Load file", content=content, size_hint=(0.9, 0.9))
self._popup.open()
def directroy_path(self, path, filename):
self.ids.text_input_directory.text = str(filename[0])
self.dismiss_popup()
kv_main = Builder.load_file('main.kv')
#
class MyApp(App):
def build(self):
return kv_main
if __name__ == '__main__':
MyApp().run()
main.kv
#:include first.kv
WindowManager:
FirstWindow:
<LoadDialog_Directory>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
dirselect: True
path: root.input_pth
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(filechooser.path, filechooser.selection)
first.kv
<FirstWindow>
name: 'first'
GridLayout:
cols: 1
BoxLayout:
orientation: "horizontal"
TextInput:
BoxLayout:
orientation: "horizontal"
Spinner:
id: drive_dir
halign: 'center'
option_cls: "MyOption"
values: root.drive_list()
Button:
text: "Set Result Directory"
on_release: root.show_load_directory()
TextInput:
id: text_input_directory
disabled: True
text: text_input_directory.text
BoxLayout:
size_hint: (0.01, 1)
orientation: "horizontal"
TextInput:
Problem
I have a screen (OrderScreen) that populates with buttons if there is data to be processed. I would like the user to click one of the buttons to be brought to another screen (MenuScreen) to process the data. While my intention is to populate the next screen with data from the button, I am currently just trying to get the ScreenManager to change to the next screen after a button press. I added a pass_data() method to the OrderButton and tried to trigger the screen manager there but self.manager.current and root.manager.current were throwing exceptions. Any help would be greatly appreciated.
recycleview_test.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.recycleview import RecycleView
from kivy.uix.screenmanager import ScreenManager, Screen
from random import randint
class MenuScreen(Screen):
def quit(self):
App.get_running_app.stop()
Window.close()
class OrderScreen(Screen):
pass
class OrderButton(Button):
def __init__(self, **kwargs):
super(OrderButton, self).__init__(**kwargs)
def pass_data(self):
print("button pushed")
class OrderScroll(RecycleView):
def __init__(self, **kwargs):
super(OrderScroll, self).__init__(**kwargs)
self.data = [{'text': str(f"Make {randint(10, 25)} items from package #{randint(1,4)}")} for x in range(12)]
class WindowManager(ScreenManager):
pass
class RecycleApp(App):
def build(self):
return WindowManager()
if __name__ == "__main__":
RecycleApp().run()
recycle.kv
#:import Factory kivy.factory.Factory
#: import ScreenManager kivy.uix.screenmanager.ScreenManager
#: import Screen kivy.uix.screenmanager.ScreenManager
#:import App kivy.app.App
<OrderScroll>:
viewclass: 'OrderButton'
manager: None
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
padding: 20
spacing: 10
<OrderButton>:
manager: None
font_size: 32
bold: True
on_release:
root.pass_data()
<WindowManager>:
id: screen_manager
OrderScreen:
id: order_screen
name: "OrderScreen"
manager: screen_manager
MenuScreen:
id: menu_screen
name: 'MenuScreen'
manager: screen_manager
<OrderScreen>:
BoxLayout:
orientation: "vertical"
Label:
text: "Data Buttons"
font_size: 64
size_hint_y: None
# pos_hint: {"x":0, "y":1}
height: 200
OrderScroll:
<MenuScreen>:
BoxLayout:
orientation: "vertical"
Label:
text: "Made it"
font_size: 64
FloatLayout:
Button:
text: 'Keep going'
font_size: 48
size_hint: .8,.5
pos_hint: {"center_x": .5, "center_y": .1}
FloatLayout:
Button:
text: 'Quit'
size_hint: .15,.3
pos_hint: {"center_x": .5, "center_y": .5}
on_release:
root.quit()
Try to create an object of screen manager:
class RecycleApp(App):
def build(self):
self.sm = WindowManager()
return self.sm
And then access it by:
App.get_running_app().sm.current = 'MenuScreen' # in Python
app.sm.current = 'MenuScreen' # in kv
My App has three screens, on running the App the first screen is always the same screen, on running the app, I need it to display different screens based on the return values of a specific function.
I have the UI to change screens and a function that returns a numerical value, based on this logic the screens should change.
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.core.window import Window
class ScreenMain(Screen):
pass
class ScreenOne(Screen):
pass
class ScreenTwo(Screen):
pass
class Manager(ScreenManager):
screen_main_id = ObjectProperty()
screen_one_id = ObjectProperty()
screen_two_id = ObjectProperty()
class ScreenmainApp(App):
def build(self):
'''This method returns the Manager class'''
self.auth()
return Manager()
def auth(self):
'''This function is called by build(), return
value should determine which screen is displayed on running the App,
by default the MAIN SCREEN IS FIRST SHOWN'''
a = 3
b = 5
value = a + b
if value >0 <= 5:
print('Show screen 1')
elif value >5<=10:
print('Show screen 2')
else:
print('Show main screen')
print('This is the return value: ',value)
if __name__ =="__main__":
ScreenmainApp().run()
screenmain.kv
#:kivy 1.10.0
#:include screenone.kv
#:include screentwo.kv
<Manager>:
screen_main: screen_main_id
screen_one: screen_one_id
screen_two: screen_two_id
# The order below determines which screen is displayed after app loads
ScreenMain:
id: screen_main_id
name: 'ScreenMain'
ScreenOne:
id: screen_one_id
name: 'Screen1'
ScreenTwo:
id: screen_two_id
name: 'Screen2'
<ScreenMain>:
BoxLayout:
orientation: 'vertical'
Label:
text:"WELCOME TO THE MAIN SCREEN"
Button:
text:'Go to Screen 1'
on_press: root.manager.current = 'Screen1'
Button:
text:'Go to Screen 2'
on_press: root.manager.current = 'Screen2'
Label:
screenone.kv
#:kivy 1.10.0
<ScreenOne>:
BoxLayout:
orientation: 'vertical'
Label:
text: 'This is SCREEN ONE'
BoxLayout:
orientation: 'horizontal'
Button:
text: "Go to Screen 2"
on_press: root.manager.current = 'Screen2'
Button:
text: "Back to Home"
on_press: root.manager.current = 'ScreenMain'
screentwo.kv
#:kivy 1.10.0
<ScreenTwo>:
BoxLayout:
orientation: 'vertical'
Label:
text: 'This is SCREEN TWO'
BoxLayout:
orientation: 'horizontal'
Button:
text: "Go to Screen 1"
on_press: root.manager.current = 'Screen1'
Button:
text: "Go to Home"
on_press: root.manager.current = 'ScreenMain'
Actual results: On App load up, the main screen always shows up first.
Expected results: Based on the value returned in auth() the screens are supposed to change each time on running the app.
The solution is instantiate the ScreenManager before calling self.auth() method.
Snippets
def build(self):
'''This method returns the Manager class'''
self.root = Manager()
self.auth()
return self.root
def auth(self):
'''This function is called by build(), return
value should determine which screen is displayed on running the App,
by default the MAIN SCREEN IS FIRST SHOWN'''
a = 3
b = 5
value = a + b
if value > 0 <= 5:
print('Show screen 1')
self.root.current = 'Screen1'
elif value > 5 <= 10:
print('Show screen 2')
self.root.current = 'Screen2'
else:
print('Show main screen')
self.root.current = 'ScreenMain'
print('This is the return value: ', value)
I'm bit confused why this is not working. Simple version of what I have is bellow. What I need is to screen manager start the main screen, on load it will start thread and do it thing updating the main screen labels... but also it should start clock. Lets say after 10 sec switch to second screen.. Until here it works... but the same needs to happen on the second screen, start clock and after 10 sec go back to main screen, this will happen in background but visually the screen will not change. Not sure what is going on :(
osd.py:
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.clock import Clock, mainthread
from kivy.config import Config
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
import MySQLdb
import threading
Config.set('graphics', 'width', '800')
Config.set('graphics', 'height', '480')
class PauseScreen(Screen):
def on_enter(self):
print('enter')
Clock.schedule_once(self.switch_back, 5)
def switch_back(self, *args):
print('back')
self.clear_widgets()
self.parent.current = "OSD_screen"
class MainScreen(Screen):
def on_enter(self):
print('start threading')
#Clock.schedule_once(self.update_thread, 0.1)
Clock.schedule_once(self.switch, 5)
# def __init__(self, **kwargs):
# super(MainScreen, self).__init__(**kwargs)
#Clock.schedule_interval(self.update_thread, 20)
# Clock.schedule_once(self.update_thread, 0.1)
# Clock.schedule_once(self.switch, 5)
def switch(self, *args):
self.parent.current = "pause"
class ScreenManagement(ScreenManager):
stop = threading.Event()
pass
presentation = Builder.load_file("osd.kv")
class osdApp(App):
def on_stop(self):
self.root.stop.set()
def build(self):
return presentation
if __name__ == '__main__':
osdApp().run()
osd.kv
ScreenManagement:
id: screen_manager
MainScreen:
id: main_screen
name: 'OSD_screen'
manager: 'screen_manager'
PauseScreen:
id: pause_screen
name: 'pause'
manager: 'screen_manager'
<MainScreen>:
BoxLayout:
orientation: 'vertical'
Label:
text_size: self.size
text: 'MainScreen'
font_size: 50
bold: True
halign: 'center'
valign: 'top'
size_hint: 1, .3
<PauseScreen>:
BoxLayout:
orientation: 'vertical'
Label:
text: 'Pause Screen'
font_size: 25
The file osd.kv is loaded multiples times, you might have unwanted behaviors (For example, you will have several Screen instances with the same name). You can declare ScreenManagement as a rule in your kv file and not use Builder.load_file method:
osd.py:
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.clock import Clock
from kivy.config import Config
from kivy.uix.screenmanager import ScreenManager, Screen
import threading
Config.set('graphics', 'width', '800')
Config.set('graphics', 'height', '480')
class PauseScreen(Screen):
def on_enter(self):
print('enter')
Clock.schedule_once(self.switch_back, 5)
def switch_back(self, *args):
print('back')
self.manager.current = "osd_screen"
class MainScreen(Screen):
def on_enter(self):
print('start threading')
Clock.schedule_once(self.switch, 5)
def switch(self, *args):
self.manager.current = "pause"
class ScreenManagement(ScreenManager):
stop = threading.Event()
class OsdApp(App):
def on_stop(self):
self.root.stop.set()
def build(self):
return ScreenManagement()
if __name__ == '__main__':
OsdApp().run()
osd.kv:
<ScreenManagement>:
id: screen_manager
MainScreen:
id: main_screen
name: 'osd_screen'
PauseScreen:
id: pause_screen
name: 'pause'
<MainScreen>:
BoxLayout:
orientation: 'vertical'
Label:
text_size: self.size
text: 'MainScreen'
font_size: 50
bold: True
halign: 'center'
valign: 'top'
size_hint: 1, .3
<PauseScreen>:
BoxLayout:
orientation: 'vertical'
Label:
text: 'Pause Screen'
font_size: 25
I have below files i am trying to switch to new windows once the button on homescreen is pressed but it throws error 'No Screen with name "VerifyScreen"'
I debugged a bit and copy pasted the Screen: part from verifyscreen.kv to our homescreen.kv file and it started working.
But why its not able to get the screen even i get the two kv files in starting only??
homescreen.kv
<HomeScreen>:
orientation: 'vertical'
screen_manager: screen_manager
ScreenManager:
size_hint: 1, 1
id: screen_manager
Screen:
BoxLayout:
orientation: 'vertical'
name: 'Home_Screen'
id: 'home'
Label:
markup: True
text: 'Home-Panel'
Button:
text: 'submitt'
size_hint: 1,1
id: submitt
pos_hint: {'center_x': .5}
on_press:
root.verify()
verifyscreen.kv
<VerifyScreen>:
orientation: 'vertical'
verify_screen_manager: verify_screen_manager
ScreenManager:
size_hint: 1, 1
id: verify_screen_manager
Screen:
name: 'VerifyScreen'
id: verify_screen
main.py
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
from functools import partial
import os
import random
import kivy
kivy.require('1.8.0')
#all required modules are imported
from kivy.uix.screenmanager import Screen
from kivy.uix.screenmanager import ScreenManager
slides = [ "HomeScreen","VerifyScreen" ]
for slide in slides:
kv_file = "{}.kv".format(slide.lower())
Builder.load_file(os.path.join("slides", kv_file))
class HomeScreen(Screen):
screen_manager = ObjectProperty(None)
verify_screen_manager = ObjectProperty(None)
verify_screen = ObjectProperty(None)
def __init__(self, **kwargs):
super(HomeScreen, self).__init__(**kwargs)
self.orientation = 'vertical'
def verify(self):
print "manish"
#self.screen_manager.current = 'VerifyScreen'
self.screen_manager.switch_to(self.verify_screen)
class HomeScreenApp(App):
def build(self):
return HomeScreen()
if __name__ == '__main__':
HomeScreenApp().run()