I am building a multiple screen App with Kivy and I would like to use the ScreenManager to navigate between the multiple screens. I have seen examples and documentation for how to create the screens within a .kv file, but I want to know how to create them within the .py file.
Problem: When I create the screen subclasses as shown below, my app
window returns a blank screen.
Question: What is the correct way to
create the Screen subclasses within a .py file?
Right now I have two Screen subclasses defined: 'welcomeScreen', and 'functionScreen'. Each consists of a layout with some widgets.
kivy.require('1.9.1')
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
import kivy.uix.boxlayout
import kivy.uix.button
from kivy.uix.screenmanager import ScreenManager, Screen
class PanelBuilderApp(App): # display the welcome screen
def build(self):
# Create the screen manager and add widgets to the base sm widget
sm = kivy.uix.screenmanager.ScreenManager()
sm.add_widget(Screen(name='welcomeScreen'))
sm.add_widget(Screen(name='functionScreen'))
# sm.current= 'welcomeScreen'
return sm
class welcomeScreen(Screen): #welcomeScreen subclass
def __init__(self, **kwargs): #constructor method
super(welcomeScreen, self).__init__(**kwargs) #init parent
welcomePage = FloatLayout()
box = kivy.uix.boxlayout.BoxLayout(orientation='vertical', size_hint=(0.4, 0.3),
padding=8, pos_hint={'top': 0.5, 'center_x': 0.5})
welcomeLabel = Label(text='Hello and welcome to the Panel Builder version 1.0.\nApp by John Vorsten\nClick below to continue',
halign= 'center', valign= 'center', size_hint= (0.4, 0.2), pos_hint= {'top': 1, 'center_x': 0.5})
welcomeBox = kivy.uix.button.Button(text= 'Click to continue')
welcomeBox.bind(on_press= self.callback)
welcomeBox2 = kivy.uix.button.Button(text='not used')
welcomePage.add_widget(welcomeLabel)
box.add_widget(welcomeBox)
box.add_widget(welcomeBox2)
welcomePage.add_widget(box)
self.add_widget(welcomePage)
def callback(instance):
print('The button has been pressed')
sm.switch_to(Screen(name= 'functionScreen'))
# sm.current = Screen(name= 'functionScreen')
class functionScreen(Screen): #For later function navigation
def __init__(self, **kwargs): #constructor method
super(functionScreen, self).__init__(**kwargs) #init parent
functionPage = kivy.uix.floatlayout.FloatLayout()
functionLabel = Label(text='Welcome to the function page. Here you will choose what functions to use',
halign='center', valign='center', size_hint=(0.4,0.2), pox_hint={'top': 1, 'center_x': 0.5})
functionPage.add_widget(functionLabel)
self.add_widget(functionPage)
# sm.add_widget('Name') #Add more names later when you create more screens
# OR#
# for i in ScreenDirectory:
# sm.add_widget(ScreenDirectory[i])
PanelBuilderApp().run()
if __name__ == '__main__':
pass
I understand I can add the definitions to a .kv file, and I will probably do this as the app grows. However, I like being explicit as I am learning how to use kivy.
I think you think using Screen(name='welcomeScreen') you are using welcomeScreen but that is not true, if you want to use welcomeScreen you should use it directly.
On the other hand you have typographical errors so I have corrected, I recommend you follow the kivy tutorials, obviously you must have a solid OOP base (and I think you do not have it so your task is to reinforce).
import kivy
kivy.require('1.9.1')
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
class PanelBuilderApp(App): # display the welcome screen
def build(self):
sm = ScreenManager()
sm.add_widget(WelcomeScreen(name='welcomeScreen'))
sm.add_widget(FunctionScreen(name='functionScreen'))
return sm
class WelcomeScreen(Screen): #welcomeScreen subclass
def __init__(self, **kwargs): #constructor method
super(WelcomeScreen, self).__init__(**kwargs) #init parent
welcomePage = FloatLayout()
box = BoxLayout(orientation='vertical', size_hint=(0.4, 0.3),
padding=8, pos_hint={'top': 0.5, 'center_x': 0.5})
welcomeLabel = Label(text='Hello and welcome to the Panel Builder version 1.0.\nApp by John Vorsten\nClick below to continue',
halign= 'center', valign= 'center', size_hint= (0.4, 0.2), pos_hint= {'top': 1, 'center_x': 0.5})
welcomeBox = Button(text= 'Click to continue', on_press=self.callback)
welcomeBox2 = Button(text='not used')
welcomePage.add_widget(welcomeLabel)
box.add_widget(welcomeBox)
box.add_widget(welcomeBox2)
welcomePage.add_widget(box)
self.add_widget(welcomePage)
def callback(self, instance):
print('The button has been pressed')
self.manager.current = 'functionScreen'
class FunctionScreen(Screen): #For later function navigation
def __init__(self, **kwargs): #constructor method
super(FunctionScreen, self).__init__(**kwargs) #init parent
functionPage = FloatLayout()
functionLabel = Label(text='Welcome to the function page. Here you will choose what functions to use',
halign='center', valign='center', size_hint=(0.4,0.2), pos_hint={'top': 1, 'center_x': 0.5})
functionPage.add_widget(functionLabel)
self.add_widget(functionPage)
if __name__ == '__main__':
PanelBuilderApp().run()
Related
I am trying to make a program that reads the dictionary after the user inputs their name and assigns a random selection based on weighted values in the dictionary. As of now the logic for selecting a random value from the dictionary is working but I have it printing to the console. I would like it to appear in a popup window (which i have but cannot get the output variable to show up there)
four.kv
WindowManager:
MainWindow:
<MainWindow>:
name:'main'
player_python:player_kv
GridLayout:
cols:1
GridLayout:
cols:2
Label:
text:'Player:'
TextInput:
id: player_kv
multiline: False
Button:
text: 'Random'
on_press: root.btn()
<P>:
output:output
FloatLayout:
Label:
id: output
main4.py
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.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty
import random
#from Dict import *
#### example dictionary
character = {
'John':
{'Water': 2, #50%
'Fire': 1, #25%
'Earth': 1,}, #25%
'Bill':
{'Water': 1, #25%
'Fire': 2, #50%
'Earth': 1,}} #25%
####
class MainWindow(Screen):
player_python = ObjectProperty(None)
output = StringProperty('')
def btn(self):
show_popup()
player = self.player_python.text
weighted_list = []
for c in character[player]:
for w in range(character[player][c]):
weighted_list.append(c)
self.output= random.choice(weighted_list)
print(self.output) ###### instead of this printing to console I want it to display in popup window
self.player_python.text = ''
class P(FloatLayout):
pass
def show_popup():
show = P()
popupWindow = Popup(title='random character', content=show, size_hint=(None,None),size=(400,400) )
popupWindow.open()
class WindowManager(ScreenManager):
pass
kv = Builder.load_file('four.kv')
class FourApp(App):
def build(self):
return kv
if __name__ == '__main__':
FourApp().run()
https://gist.github.com/PatrickToole/00cc72cdd7ff5146976e5d92baad8e02
Thanks in advance
-P
I haven't tested this code, but try passing self.output to your show_popup() method. This would mean changing your btn() method to something like:
def btn(self):
player = self.player_python.text
weighted_list = []
for c in character[player]:
for w in range(character[player][c]):
weighted_list.append(c)
self.output= random.choice(weighted_list)
print(self.output) ###### instead of this printing to console I want it to display in popup window
self.player_python.text = ''
show_popup(self.output)
And in the show_popup() method:
def show_popup(output):
show = P()
show.output.text = output
popupWindow = Popup(title='random character', content=show, size_hint=(None,None),size=(400,400) )
popupWindow.open()
As I mentioned, I haven't tested this code, but something like this should work.
I'm trying to output text from button presses and add it to a textbox each time it's pressed as well as delete the text when the delete button is pressed - in normal python.
I've been trying to use kivy.uix.textinput, but I'm not sure on how to output the value from the buttons as well as delete it.
This is what I've done so far.
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
class CodeScreen(Screen):
def __init__(self):
super(CodeScreen, self).__init__(name='code_screen')
main_grid_lay = GridLayout(cols=2,cols_minimum={0:640, 1:175})
self.add_widget(main_grid_lay)
#Code ouput display
display_code = TextInput(text='Code!', readonly = True, id=output)
main_grid_lay.add_widget(display_code)
#Options Buttons
box_lay = BoxLayout(orientation='vertical')
main_grid_lay.add_widget(box_lay)
delete_button = Button(
text='Delete',
size_hint_x= None,
width=160,
id='' #unsure on how to delete
)
box_lay.add_widget(delete_button)
base_left = Button(
text='Base Left',
#on_release= b_left(),
#id='left',
)
base_right = Button(
text='Base Right',
on_release=self.degree_popup,
#on_release= b_right(),
)
#b_left = self.ids.output.text='left'
#b_left = self.ids.output.text='right'
box_lay.add_widget(base_left)
box_lay.add_widget(base_right)
# The app class
class MyMain(App):
def build(self):
return CodeScreen()
# Runs the App
if __name__ == '__main__':
MyMain().run()
It currently sends an error and is probably because of the ids. Not fully sure how ids work without using kv language. Any help is appreciated.
The id only makes sense in .kv, in python they are useless. In this case the solution is to access the objects in the methods connected to on_release but for this they must be class attributes.
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
class CodeScreen(Screen):
def __init__(self):
super(CodeScreen, self).__init__(name="code_screen")
self.display_code = TextInput(text="Code!", readonly=True)
self.delete_button = Button(
text="Delete", size_hint_x=None, width=160, on_release=self.delete_clicked
)
self.base_left = Button(text="Base Left", on_release=self.left_clicked)
self.base_right = Button(text="Base Right", on_release=self.right_clicked)
main_grid_lay = GridLayout(cols=2, cols_minimum={0: 640, 1: 175})
main_grid_lay.add_widget(self.display_code)
self.add_widget(main_grid_lay)
box_lay = BoxLayout(orientation="vertical")
box_lay.add_widget(self.delete_button)
main_grid_lay.add_widget(box_lay)
box_lay.add_widget(self.base_left)
box_lay.add_widget(self.base_right)
def delete_clicked(self, instance):
self.display_code.text = ""
def left_clicked(self, instance):
self.display_code.text += "left"
def right_clicked(self, instance):
self.display_code.text += "right"
# The app class
class MyMain(App):
def build(self):
return CodeScreen()
# Runs the App
if __name__ == "__main__":
MyMain().run()
I'm trying to implement FlatButton in my kv but I keep getting the same error that is AttributeError: 'NoneType' object has no attribute 'bind. It works fine with Button alone.
from flat_kivy.flatapp import FlatApp
from kivy.uix.touchripple import TouchRippleBehavior
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import (StringProperty, NumericProperty, ObjectProperty,
ListProperty, DictProperty, BooleanProperty)
class Login(Screen):
pass
class MainScreen(Screen):
pass
class ScreenManager(ScreenManager):
pass
theRoot = Builder.load_string('''
ScreenManager:
Login:
<Login>:
FlatButton:
text: 'Click Here'
size_hint: (.4,.25)
''')
class TouchRippleApp(FlatApp):
def build(self):
return theRoot
if __name__ == '__main__':
TouchRippleApp().run()
This is the FlatButton code in Flat_Kivy. I'm stuck at this problem.
class FlatButtonBase(GrabBehavior, LogBehavior, TouchRippleBehavior,
ThemeBehavior):
color = ListProperty([1., 1., 1.])
color_down = ListProperty([.7, .7, .7])
border_size = ListProperty([0, 0, 0, 0])
text = StringProperty('')
alpha = NumericProperty(1.0)
style = StringProperty(None, allownone=True)
color_tuple = ListProperty(['Grey', '500'])
font_color_tuple = ListProperty(['Grey', '1000'])
ripple_color_tuple = ListProperty(['Grey', '1000'])
font_ramp_tuple = ListProperty(None)
font_size = NumericProperty(12)
eat_touch = BooleanProperty(False)
def on_color(self, instance, value):
self.color_down = [x*.7 for x in value]
class FlatButton(FlatButtonBase, ButtonBehavior, AnchorLayout):
pass
class RaisedFlatButton(RaisedStyle, FlatButton):
pass
Perhaps an easier way to do this altogether is create your FlatButton class in the .kv language (inside your string you're loading with Builder.load_string)
Try adding this to your kv string:
<FlatButton#Button>: # create a class "FlatButton" that inherits the kivy Button
background_normal: "" # Get rid of the kivy Button's default background image
background_down: "" # Get rid of the kivy Button's default background image when clicked
# Set the background color to transparent if no action is happening to the button
# If the button is clicked, it will change it to fully white
background_color: (1,1,1,0) if self.state == 'normal' else (1,1,1,1)
and then you can remove all the code relating to your FlatButton class on the python side, with the exception of creating a base class for the kv to work with. E.g. all you need in the python code is
class FlatButton():
pass
Using Kivy 1.10.0 with Python 2.7.9 I am trying to get the TextInput value entered by user when Button (my_button2) is clicked .And although I have been able to get this working with GridLayout it seems like the method I am using is not working with ScreenManager with BoxLayout . Error received is : AttributeError: 'ScreenTwo' object has no attribute 'inpt' when my_button2
After clicking 'Next Screen ' button , it takes me to page where user enters text value , and 'print' button should print it
Please see below :
import kivy
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
class ScreenOne(Screen):
def __init__ (self,**kwargs):
super (ScreenOne, self).__init__(**kwargs)
my_box1 = BoxLayout(orientation='vertical')
my_button1 = Button(text="Next Screen ",size_hint_y=None,size_y=100)
my_button1.bind(on_press=self.changer)
my_box1.add_widget(my_button1)
self.add_widget(my_box1)
def changer(self,*args):
self.manager.current = 'screen2'
class ScreenTwo(Screen):
def __init__(self,**kwargs):
super (ScreenTwo,self).__init__(**kwargs)
layout = BoxLayout(orientation='vertical')
self.add_widget(layout)
inpt = TextInput(text="Some text ",size_y=50)
layout.add_widget(inpt)
my_button2 = Button(text="Print ")
my_button2.bind(on_press=self.click)
layout.add_widget(my_button2)
Home_btn = Button(text="Back")
Home_btn.bind(on_press=self.home)
layout.add_widget(Home_btn)
def click(self,my_button2):
entered_value = self.inpt.text
print entered_value
def home(self,*args):
self.manager.current = 'screen1'
class TestApp(App):
def build(self):
my_screenmanager = ScreenManager()
screen1 = ScreenOne(name='screen1')
screen2 = ScreenTwo(name='screen2')
my_screenmanager.add_widget(screen1)
my_screenmanager.add_widget(screen2)
return my_screenmanager
if __name__ == '__main__':
TestApp().run()
second screen
When you use self you are trying to access members of the class, but in your case inpt it is not since it is a variable any, if you want to be a member of the class you must put forward self, in your case change:
inpt = TextInput(text="Some text ",size_y=50)
layout.add_widget(inpt)
to:
self.inpt = TextInput(text="Some text ",size_y=50)
layout.add_widget(self.inpt)
Note: I recommend you read OOP basics, if you are not going to have many of these problems.
I am writing my first Kivy app in python only (I'm avoiding kv for now). I have created a custom widget called WorldviewWidget, and I'm trying to use it as a place to draw. With the button widgets, I just give a size_hint and a pos_hint, and they show up where I want them. But with my widget, I don't know how to use the size_hint and position_hint to size the rectangle I am drawing in my WorldviewWidget. Here is the code. Thanks in advance!
#! /usr/bin/python
from kivy.app import App
from kivy.graphics import *
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.uix.button import Button
class WorldviewWidget(Widget):
def __init__(self, **kwargs):
super(WorldviewWidget, self).__init__(**kwargs)
self.canvas.clear()
print self.size, self.pos
with self.canvas:
Color(1, 0, 0, 1, mode='rgba')
# HELP! I want the rectangle to be resized when the window changes size so that it always takes up the same proportion of the screen.
self.rect = Rectangle(size=???, pos=???)
class JFROCS_App(App):
def build(self):
Window.clearcolor = [1,1,1,1]
parent = FloatLayout(size=Window.size)
worldview = WorldviewWidget(size_hint=(0.4, 0.4), pos_hint = {'x':0.2, 'y':0.2})
parent.add_widget(worldview)
start_btn = Button(text='Start', size_hint=(0.1, 0.1), pos_hint={'x':.02, 'y':.7}, background_color=[0,1,0,1])
start_btn.bind(on_release=self.start_simulation)
parent.add_widget(start_btn)
pause_btn = Button(text='Pause', size_hint=(0.1,0.1), pos_hint={'x':.02, 'y':.6}, background_color=[1,1,0,1])
pause_btn.bind(on_release=self.pause_simulation)
parent.add_widget(pause_btn)
stop_btn = Button(text='Stop', size_hint=(0.1,0.1), pos_hint={'x':.02, 'y':.5}, background_color=[1,0,0,1])
stop_btn.bind(on_release=self.stop_simulation)
parent.add_widget(stop_btn)
return parent
def start_simulation(self, obj):
print "You pushed the start button!"
def pause_simulation(self, obj):
print "You pushed the pause button!"
def stop_simulation(self, obj):
print "You pushed the stop button!"
if __name__ == '__main__':
JFROCS_App().run()
I think that this sort of task is predestined for the kivy-language but here is a solution in Python. Basically, I have used the bind-method to make your widget listen for changes in its parent's size. Have a look at this for more information on this mechanism.
from kivy.app import App
from kivy.graphics import *
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.uix.button import Button
class WorldviewWidget(Widget):
def __init__(self, **kwargs):
super(WorldviewWidget, self).__init__(**kwargs)
self.canvas.clear()
print self.size, self.pos
with self.canvas:
Color(1, 0, 0, 1, mode='rgba')
# *changed* ##############################################
self.rect = Rectangle(size=self.size, pos=self.pos)
# *new* ##########################################################
def update_size(self, instance, new_size):
print "UPDATING SIZE", instance, new_size
self.size[0] = new_size[0] * 0.4
self.size[1] = new_size[1] * 0.4
self.rect.size = self.size
self.pos[0] = self.parent.size[0] * 0.2
self.pos[1] = self.parent.size[1] * 0.2
self.rect.pos = self.pos
class JFROCS_App(App):
def build(self):
Window.clearcolor = [1,1,1,1]
parent = FloatLayout(size=Window.size)
# *changed* ##################################################
worldview = WorldviewWidget(size=(0.4*parent.size[0], 0.4*parent.size[1]),
pos=(0.2*parent.size[0], 0.2*parent.size[1]))
# makes sure that the widget gets updated when parent's size changes:
parent.bind(size=worldview.update_size)
parent.add_widget(worldview)
start_btn = Button(text='Start', size_hint=(0.1, 0.1), pos_hint={'x':.02, 'y':.7}, background_color=[0,1,0,1])
start_btn.bind(on_release=self.start_simulation)
parent.add_widget(start_btn)
pause_btn = Button(text='Pause', size_hint=(0.1,0.1), pos_hint={'x':.02, 'y':.6}, background_color=[1,1,0,1])
pause_btn.bind(on_release=self.pause_simulation)
parent.add_widget(pause_btn)
stop_btn = Button(text='Stop', size_hint=(0.1,0.1), pos_hint={'x':.02, 'y':.5}, background_color=[1,0,0,1])
stop_btn.bind(on_release=self.stop_simulation)
parent.add_widget(stop_btn)
return parent
def start_simulation(self, obj):
print "You pushed the start button!"
def pause_simulation(self, obj):
print "You pushed the pause button!"
def stop_simulation(self, obj):
print "You pushed the stop button!"
if __name__ == '__main__':
JFROCS_App().run()
And have a look at the kivy-language -- it takes care of all the binding for you :)