Background: I have been learning Python - and through it - Kivy, by making an app. I have been using the .kv file and Builder.load_string methods to create my graphics, but have decided to try using solely python, and moving all of my layouts over into python.
The Problem: When I began using screens, I was unable to bind the correct code to the buttons to make the screens transition. When I am writing the line, 'self.manager.etc...' auto-complete shows me a list of valid properties to use.
So after 'self.' it shows that I can use 'manager', and after 'manager.' it does not think that the screen's manager has a 'current' or 'transition' property. I must have messed up in how I connected the screen to the manager, but I cannot fathom how.
class HomePage(Screen):
def __init__(self, **kwargs):
super(HomePage, self).__init__(**kwargs)
layout = FloatLayout()
notification = Label(text='upcoming: ....', font_size='16sp', size_hint=(0,0), pos_hint={'center_x':.5, 'top':0.9})
layout.add_widget(notification)
button_row=BoxLayout(size_hint_y=.1, spacing=20)
profile_button=Label(text='Profile')
button_row.add_widget(profile_button)
layout.add_widget(button_row)
self.add_widget(layout)
def transit():
self.manager.current = profile_button # <- this should work, right?
profile_button.bind(on_press=transit)
class ScreenApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(HomePage(name='home'))
return sm
if __name__ == "__main__":
ScreenApp().run()
You should add in your imports the transition
from kivy.uix.screenmanager import Screen, ScreenManager, FadeTransition
And pass it in the builder
class ScreenApp(App):
def build(self):
sm = ScreenManager(transition=FadeTransition())
as for the current you should add a second screen, give it a name and and use that name to change to that screen.from the https://github.com/kivy/kivy/blob/master/kivy/uix/screenmanager.py
by default, the first added screen will be shown. If you want to
show another one, just set the 'current' property. sm.current = 'second'
Also current is a string property, you can not set it to a label
:attr:current is a :class:~kivy.properties.StringProperty and
defaults to None.
So your full code should be something like
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.screenmanager import Screen, ScreenManager, FadeTransition
class HomePage(Screen):
def __init__(self, **kwargs):
super(HomePage, self).__init__(**kwargs)
layout = FloatLayout()
notification = Label(text='upcoming: ....', font_size='16sp', size_hint=(0,0), pos_hint={'center_x':.5, 'top':0.9})
layout.add_widget(notification)
button_row=BoxLayout(size_hint_y=.1, spacing=20)
profile_button=Button(text='Profile') # changed to a button
button_row.add_widget(profile_button)
profile_button.bind(on_press=self.transit) # moved here the bind action
layout.add_widget(button_row)
self.add_widget(layout)
def transit(self, *args):
# unintended to become a class method and reference to it with self
print "ok"
self.manager.current = "screen2"
class ProfilePage(Screen):
def __init__(self, **kwargs):
super(ProfilePage, self).__init__(**kwargs)
layout = FloatLayout()
labelP = Label(text="Profile Page")
layout.add_widget(labelP)
self.add_widget(layout)
class ScreenApp(App):
def build(self):
sm = ScreenManager(transition=FadeTransition())
# create the first screen
screen1 = HomePage(name='Home') #your home page
screen2 = ProfilePage(name='screen2') # the second screen
sm.add_widget(screen1)
sm.add_widget(screen2)
return sm
if __name__ == "__main__":
ScreenApp().run()
Related
Like, can an output-only TextEntry(readonly=True) be changed with TextEntry.insert_text()? Does the readonly refers ONLY to the user part of the GUI?
I assume you actually mean TextInput. If so, the answer is: no, you cannot change the text in code using insert_text, but you can change it using a simple text=.
John Anderson has already given an answer.
You can download the Kivy Launcher or QPython from google playstore so that you can test your kivy codes without using a desktop.
Here is a simple code with TextInput using QPython2:
#-*-coding:utf8;-*-
#qpy:2
#qpy:kivy
#qpy:fullscreen
from kivy.app import App
from kivy.uix.textinput import TextInput
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.button import Button
class TestingText(Widget):
def __init__(self):
super(TestingText, self).__init__()
self.size = Window.size
self.t = TextInput(readonly=True)
self.b1 = Button(text="Insert")
self.b2 = Button(text="Set")
self.t.center = self.center
self.b2.right = self.right
self.b1.on_press = self.inserttext
self.b2.on_press = self.settext
self.add_widget(self.t)
self.add_widget(self.b1)
self.add_widget(self.b2)
def inserttext(self):
self.t.insert_text("insertme")
def settext(self):
self.t.text = "setme"
class Test(App):
def build(self):
return TestingText()
Test().run()
I am working on building a kivy app. The below code is a simple Hello World of sorts. Press a button. The label changes from 'Hello' to 'World'
import kivy
kivy.require('1.9.1') # replace with your current kivy version !
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
#from tasks import assign_task
class GetTask():
def __init__(self, **kwargs):
super(GetTask,self).__init__(**kwargs)
self.main_label = Label(text = "Hello")
button = Button(text="Press")
button.bind(on_press= self.update)
def update(self):
self.main_label.text = "World"
class MyApp(App):
def build(self):
return GetTask()
if __name__ == '__main__':
MyApp().run()
The error I get when I run it is:
raise Exception('Invalid instance in App.root')
Exception: Invalid instance in App.root
I looked at this- Kivy: Invalid instance in App.root
I still cannot figure what I am doing wrong. Please help. Thank you.
What does your GetTask inherit from? It seems to me that it doesn't inherit from anything at all. Try changing it to
class GetTask(Widget):
# The rest is like it's in your code.
Also have a look at my comment under the question. Not sure if it's still the issue in 1.9.1, though.
I have a kivy app with some textinput and I want to show in my smartphone a numeric keyboard. I've been reading about it and I think that with the property input_type=number I could get the right result but I realised that with the kivy updates doesn't work nowadays. How could I get the numeric keyboard when my textinput is focused? With the app in landscape mode it could be useful or the keyboard still will take half screen? Here do you have the code:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
class LoginScreen(GridLayout):
def __init__(self,**kwargs):
super(LoginScreen, self).__init__(**kwargs)
self.cols=2
self.add_widget(Label(text='Subject'))
self.add_widget(Label(text=''))
self.add_widget(Label(text='1'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='2'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='3'))
self.add_widget(TextInput(multiline=False))
self.add_widget(Label(text='4'))
self.add_widget(TextInput(multiline=False))
b1=Button(text='Exit',background_color=[0,1,0,1],height=int(Window.height)/9.0) #doesn't work properly
self.add_widget(b1)
b2=Button(text='Run',background_color=[0,1,0,1],height=int(Window.height)/9.0) #doesn't work properly
self.add_widget(b2)
b1.bind(on_press=exit)
class SimpleKivy(App):
def build(self):
return LoginScreen()
if __name__=='__main__':
SimpleKivy().run()
I think is a bit late, bu maybe someone looks for it tomorrow.
Is true you should change the input_type propery of your TextInput, in your case for example:
self.add_widget(TextInput(multiline=False, input_type = 'number'))
I suggest you create a new custom widget for that in order works in android and desktop, like this that implement a maxdigits property:
class IntegerInput(TextInput):
def __init__(self, **kwargs):
super(IntegerInput, self).__init__(**kwargs)
self.input_type = 'number'
def insert_text(self, substring, from_undo=False):
if substring.isnumeric():
if hasattr(self, "maxdigits"):
if len(self.text) < self.maxdigits:
return super(IntegerInput,self).insert_text(substring, from_undo=from_undo)
else:
return super(IntegerInput, self).insert_text(substring, from_undo=from_undo)
Background: I am using solely Python to implement the Kivy screens. I have two screens that both contain a list, and two rows of buttons. I thought it would be good programming practice to create a screen class that has those layouts, and then use inheritance to create the two screens and add buttons to the layouts as needed.
The Problem: However, when I do this I find that in the child screens I cannot access self.manager.current <-- specifically '.current' It also does not have access to self.manager.transition. I wish to understand why this happens, and how/what things are inherited here.
Question: Does anybody know why or how it is not inheriting the parent screen's manager's properties?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.listview import ListView, ListItemButton
from kivy.adapters import listadapter
from kivy.uix.button import Button
from kivy.properties import ListProperty, StringProperty
from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition
class ListScreen(Screen): # This is the super class that I am trying to inherit through
itemList = ListProperty([])
selected_value = StringProperty()
layout = BoxLayout(orientation ='vertical')
top_buttons=BoxLayout(size_hint_y=0.1)
scrollable_list=ListView(adapter=listadapter, size_hint_y=0.8)
scrollable_list.data=itemList
scrollable_list.selection_mode='single'
scrollable_list.allow_empty_selection=False
# scrollable_list.cls=ListItemButton <-- Unrelated bug here, ignore this line
bot_buttons=BoxLayout(size_hint_y=0.1)
def __init__(self, **kwargs):
super(ListScreen, self).__init__(**kwargs)
def finalize_widgets(self):
self.layout.add_widget(self.top_buttons)
self.layout.add_widget(self.scrollable_list)
self.layout.add_widget(self.bot_buttons)
self.add_widget(self.layout)
def change(self,change):
self.selected_value = 'Selected: {}'.format(change.text)
def change_screen(self, screen_name):
self.manager.current = screen_name # <-- Here is the problem
class SubScreen(ListScreen):
# This is one of the child classes, intended to inherit Screen through the parent ListScreen class.
def __init__(self, **kwargs):
super(SubScreen, self).__init__(**kwargs)
save = Button(text='Save')
load = Button(text='Load')
new_d = Button(text='New')
new_s = Button(text='New Search')
self.top_buttons.add_widget(save)
self.top_buttons.add_widget(load)
self.top_buttons.add_widget(new_d)
self.top_buttons.add_widget(new_s)
new_s.bind(on_press = self.change_screen('search'))
class ListBuilderApp(App):
def build(self):
sm = ScreenManager(transition=SlideTransition())
sm.add_widget(SubScreen(name='list'))
sm.add_widget(SearchResults(name='results'))
sm.add_widget(SearchScreen(name='search'))
return sm
if __name__ == "__main__":
ListBuilderApp().run()
Well, I don't see ScreenManager which is basically what you get with self.manager(object), so...
No ScreenManager with Screens added either in python or kv like widgets(add_widget()), no self.manager inside any of the Screens - you can't access something that isn't there.
Add some class which will be a root for your Screens and make it ScreenManager + add Screens as children.
Edit:
I was blind probably, but I didn't see on_press=.. stuff. There is the problem, because you didn't assign the function to an event, rather called the function right when you put ('search') at the end. (try do self.parent and you'll see)
Kivy events(at least "on_") catch the function, but not the parameters, not directly. That's why you need to use partial
from functools import partial
new_s.bind(on_press = partial(self.change_screen,'search'))
With this the error is gone, but some adapter binding jumps out which I really don't have a clue how to fix as I don't use it that much.
I've written a simple game that I want to restart after a certain button is pressed (so that EVERYTHING goes back to where it was when the app was run).
So the question is: what do I need to type into on_press to start Game() ance again?
class Game(Widget):
(...)
def but_add(self, player):
self.add_widget(Button(text=player,
font_size=30,
center_x=self.width/2,
center_y=self.height/2,
size=(self.height, self.height*7/20),
background_normal='katana.jpg',
background_down='katana.jpg',
markup=True,
on_press= ???????? ))
(...)
class Okiya(App):
def build(self):
return Game()
if __name__ == "__main__":
Okiya().run()
You probably don't really want to restart the entire app, but just to reset its state. The mechanism of doing so is up to you, you're the one that knows what the initial state should look like; for instance, you could do on_press=self.reset_func and define the reset_func method to perform all these tasks.
You could also remove the widget and add a new instance, which will have the default properties.
I don't know exactly what it's worth but the following works for an app I'm doing:
Add the following method to your application class:
class Okiya(App):
def restart(self):
self.root.clear_widgets()
self.stop()
return Okiya().run()
Then call the method from a python file where you want to restart the app with:
App.get_running_app().restart()
Here is my test example
from kivy.app import App
from kivy.uix.button import Button
from kivy import platform
if platform == 'android':
from jnius import autoclass
Intent = autoclass("android.content.Intent")
PythonActivity = autoclass("org.kivy.android.PythonActivity")
System = autoclass("java.lang.System")
class RestartApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = Button(text='Restart', on_release=self.restart)
def build(self):
return self.screen
def restart(self, *args):
if platform == 'android':
activity = PythonActivity.mActivity
intent = Intent(activity.getApplicationContext(), PythonActivity)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
activity.startActivity(intent)
System.exit(0)
else:
self.screen.clear_widgets()
self.stop()
RestartApp().run()
RestartApp().run()