For my game, I would like to to reset a screen when the user changes screen.
I have tried many things such as using the Clock and making a update function, but it never really work. The layout does not change even though I explicitly change it in python.
For example, if I have button that becomes disabled upon release, when I change screen, it should not be disabled anymore This something that has been on my mind for a long time and don't quite get it.
I have three small files for this main.py, screen_manager.kv and main.kv. Sorry for this noob question
main.py
from kivy.app import App
from kivy.clock import Clock
from kivy.config import Config
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.widget import Widget
# Setup the window
Config.set('graphics', 'resizable', False)
width = 550
height = 550
Window.size = (width, height)
class OptionWindow(Screen):
pass
class SecondWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
class Quiz(Widget):
def __init__(self, **kwargs):
super(Quiz, self).__init__(**kwargs)
def update(self, dt):
pass
kv = Builder.load_file('screen_manager.kv')
class Application(App):
CATEGORY = ''
def build(self):
game = Quiz()
Clock.schedule_interval(game.update, 1.0 / 60.0)
return kv
if __name__ == '__main__':
Application().run()
screen_manager.kv
# File name: screen_manager.kv
#:include main.kv
WindowManager:
OptionWindow:
SecondWindow:
<OptionWindow>:
name: 'first'
Button:
text: "Reset SecondWindow"
on_release:
app.root.current = 'second'
root.manager.transition.direction = "right"
<SecondWindow>:
name: 'second'
Quiz:
FloatLayout:
size: root.width, root.height
pos: 0, 0
Button:
text: "Go back"
pos_hint: {'x': 0.4, 'y': 0.2}
size_hint: 0.6, 0.6
on_release:
app.root.current = 'first'
root.manager.transition.direction = "right"
main.kv
<Quiz>:
FloatLayout:
id: thelayout
size: root.width, root.height
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
Button:
text: 'press me'
pos_hint: {'x': 0.0, 'y': 0.2}
size_hint: 0.3, 0.3
on_release: self.disabled = True
Thanks for you help
Your code provides for disabling the Button, but there is nothing in your code to re-enable it. Once a Button is disabled, it stays that way until something changes changes it. You can add something to your SecondWindow to re-enable the button every time the SecondWindow is displayed. You can use on_enter or on_pre_enter to trigger something to happen whenever that Screen is displayed. like this:
<SecondWindow>:
name: 'second'
on_pre_enter: quiz.ids.butt.disabled = False
Quiz:
id: quiz
FloatLayout:
size: root.width, root.height
pos: 0, 0
Button:
text: "Go back"
pos_hint: {'x': 0.4, 'y': 0.2}
size_hint: 0.6, 0.6
on_release:
app.root.current = 'first'
root.manager.transition.direction = "right"
Note the added id for the Quiz and the added on_pre_enter:. Also needed is a way to reference the Button. I have done that by adding an id to that Button:
<Quiz>:
FloatLayout:
id: thelayout
size: root.width, root.height
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
Button:
id: butt
text: 'press me'
pos_hint: {'x': 0.0, 'y': 0.2}
size_hint: 0.3, 0.3
on_release: self.disabled = True
Now, everytime before the SecondWindow is displayed, the on_pre_enter: is triggered, and the `Button`` is re-enabled.
Related
The widgets on my popup window do not resize when the root window resizes. The popup window and the labels on the popup window stay where they are. Does it have something to do with the size_hint and size of the popup window itself? It seems that the widgets(icons) are independent of the popup window.
main file
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.button import ButtonBehavior
from kivy.uix.image import Image
from kivy.properties import StringProperty, ObjectProperty,NumericProperty
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
class MainScreen(Screen, FloatLayout):
mantra_text = ObjectProperty(None)
def printMantra(self):
print(self.ids.mantra_text.text)
def icon_popup(self):
popup = Popup(title="Profile Icon", content=Popup_Content(), size_hint=(None, None), size=(300, 200))
popup.open()
class Popup_Content(FloatLayout):
pass
class ImageButton(ButtonBehavior, Image):
pass
class MainApp(App):
def build(self):
return MainScreen()
def set_profile_icon(self, image):
self.root.ids.profile_icon.source = image.source
print(image)
#print(self.root.ids.profile_icon)
MainApp().run()
kivy file
#:import utils kivy.utils
<MainScreen>
Popup_Content:
id: popup_content
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#ffbb99")
Rectangle:
pos: self.pos
size: self.size
GridLayout:
cols: 2
pos_hint: {"x":0.6, "top":1}
size_hint: 0.4,0.2
spacing_horizontal: [0.9*root.width]
Label:
text: "Name"
ImageButton:
id: profile_icon
source: "profile_icon"
on_release: root.icon_popup()
Label:
text: mantra_text.text
pos_hint: {"x":0, "top":0.8}
size_hint: 1, 0.2
text_size: self.size
halign: "center"
font_size: 25
TextInput:
id: mantra_text
pos_hint: {"x": 0.15, "top":0.6}
size_hint: 0.7, 0.1
#text_size: self.size
Label:
text: "Time"
pos_hint: {"x":0.3, "top":0.6}
size_hint: 0.4, 0.2
text_size: self.size
halign: "left"
font_size: 30
Button:
text: "Time"
pos_hint: {"x":0.3, "top":0.5}
size_hint: 0.4, 0.2
on_release: root.printMantra()
<Popup_Content>
#profile_icon: profile_icon
FloatLayout:
GridLayout:
cols: 5
pos_hint: {"x":0.95, "y":1.6}
ImageButton:
id: man_01
source: "icons/male_icon_01.png"
on_release: app.set_profile_icon(man_01)
ImageButton:
id: man_02
source: "icons/male_icon_02.png"
on_release: app.set_profile_icon(man_02)
ImageButton:
source: "icons/male_icon_01.png"
on_release: app.set_profile_icon()
ImageButton:
source: "icons/male_icon_01.png"
on_release: app.set_profile_icon()
ImageButton:
source: "icons/male_icon_01.png"
on_release: app.set_profile_icon()
ImageButton:
id: female_01
source: "icons/female_icon_01.png"
on_release: app.set_profile_icon(female_01)
If you want your Popup to change size when you resize the App, then use size_hint. Something like:
popup = Popup(title="Profile Icon", content=Popup_Content(), size_hint=(0.5, 0.5))
Using size_hint=(None, None), size=(300, 200) forces the Popup size to (300, 200) regardless of the size of MainScreen.
And to get the Popup content to follow the Popup, you can use RelativeLayout. In the documentation for RelativeLayout, it says:
When a widget with position = (0,0) is added to a RelativeLayout, the
child widget will also move when the position of the RelativeLayout is
changed. The child widgets coordinates remain (0,0) as they are always
relative to the parent layout.
So if you define your Popup_Content as a RelativeLayout, then the GridLayout will follow it. I suggest defining Popup_Content as:
class Popup_Content(RelativeLayout):
pass
Then, in the kv:
<Popup_Content>
#profile_icon: profile_icon
GridLayout:
cols: 5
# pos_hint: {"x":0.95, "y":1.6}
ImageButton:
id: man_01
.
.
.
I am a beginner in Language and I am trying to make a simple guessing game and I would like to know how I use the data entered in the Players Class in the Start_p1 Class. I want the name typed in the TextInput of the Players Class, to appear in the Label Text: of the Start_p1 Class.
Arquivo .py:
import kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.textinput import TextInput
from kivy.properties import StringProperty
class Home(Screen):
def next(self):
self.manager.current = "players"
class Players(Screen):
def start(self):
self.manager.current = 'st1'
class Start_p1(Screen):
def runn(self):
self.manager.current = 'st2'
class Start_p2(Screen):
def back(self):
self.manager.current = 'st1'
class Finish(Screen): pass
class Myapp(App):
sm = None
def build(self):
self.sm = ScreenManager()
self.sm.add_widget(Home(name = 'home'))
self.sm.add_widget(Players(name = 'players'))
self.sm.add_widget(Start_p1(name = 'st1'))
self.sm.add_widget(Start_p2(name = 'st2'))
self.sm.add_widget(Finish(name = 'finish'))
self.sm.current = 'home'
return self.sm
if __name__ == '__main__':
Myapp().run()
Arquivo.kv
#: import utils kivy.utils
<Home>:
FloatLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex('#2169af')
Rectangle:
pos: self.pos
size: self.size
Button:
text: 'Iniciar'
pos_hint: {'center_x': .5, 'center_y': .5}
size_hint: 0.2, .1
background_color: utils.get_color_from_hex('#13447d')
on_release: root.next()
Button:
text: 'Configurações'
pos_hint: {'center_x': .5, 'center_y': .4}
size_hint: 0.2, .1
background_color: utils.get_color_from_hex('#13447d')
<Players>:
FloatLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex('#2169af')
Rectangle:
pos: self.pos
size: self.size
Label:
pos: 0, 270
text: 'JOGADORES'
Label:
pos: 0, 150
text: 'Informe o nome do 1° Jogador:'
TextInput:
id: txtt
pos: 270, 400
size: 250, 30
multiline: False
Label:
pos: 0, -20
text: 'Informe o nome do 2° Jogador:'
TextInput:
id: txt
pos: 270, 230
size: 250, 30
multiline: False
Button:
text: 'Iniciar'
pos_hint: {'center_x': .5, 'center_y': .2}
size_hint: 0.2, .1
background_color: utils.get_color_from_hex('#13447d')
on_release: root.start()
<Start_p1>:
Players:
id: players
FloatLayout:
canvas.before:
Color:
rgb: utils.get_color_from_hex('#2169af')
Rectangle:
pos: self.pos
size: self.size
Label:
text:
That is actually a bit tricky. One would expect that you can use:
Label:
text: app.sm.get_screen('players').ids.txt.text
That will technically work, but it won't set up the automatic update if the text of the TextInput changes. A careful reading the documentation explains why, and suggests a work around. Using the suggestion from the documentation, you can do something like this:
Label:
players_screen: app.sm.get_screen('players')
text: self.players_screen.ids.txt.text
This produces the desired result with automatic updates.
I'm trying to have kivy select the text of a TextInput widget on focus but when I try it seems to select it when it unfocuses and retains the selection. Any ideas how I can select it on focus and on unfocus deselect? I've attached my code below if someone wants to have a play around.
kv file:
<TextInput>:
size_hint: 0.9, 0.5
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
multiline: False
<Button>:
text: "Press Me"
size_hint: (0.1, 0.5)
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
<MainLayout>:
canvas.before:
Color:
rgba: 0.15, 0.15, 0.16, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'vertical'
padding: 10
BoxLayout:
padding: 10
TextInput:
text: "Directory"
Button:
text: "Browse"
on_press: root.browse_btn()
BoxLayout:
padding: 10
TextInput:
text: "Prefix"
on_focus: self.select_all()
TextInput:
text: "File"
on_focus: self.select_all()
TextInput:
text: "Suffix"
on_focus: self.select_all()
BoxLayout:
padding: 10
Button:
id: button_one
text: "Confirm"
on_press: root.confirm_btn()
Button:
text: "Cancel"
on_press: root.cancel_btn()
python file:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.core.window import Window
from kivy.config import Config
Config.set('graphics', 'resizable', 0)
class MainLayout(BoxLayout):
button_id = ObjectProperty(None)
def browse_btn(self):
print("Hey")
def confirm_btn(self):
print("Confirm")
def cancel_btn(self):
print("Cancel")
class BatchRenameApp(App):
def build(self):
self.title = "Batch File Rename"
Window.size = (750, 250)
return MainLayout()
if __name__ == '__main__':
app = BatchRenameApp()
app.run()
Well hidden in the TextInput documentation:
Selection is cancelled when TextInput is focused. If you need to show
selection when TextInput is focused, you should delay (use
Clock.schedule) the call to the functions for selecting text
(select_all, select_text).
So, in your kv, start by importing Clock:
#: import Clock kivy.clock.Clock
Then you can use it in a TextInput rule:
TextInput:
text: "Prefix"
on_focus: Clock.schedule_once(lambda dt: self.select_all()) if self.focus else None
The if self.focus makes sure the select_all only happens when the TextInput gains focus.
I am trying to create a layout with 2 sections that have a label over each and a view below (TreeView on the left and a window showing details on the right, e.g. "click on this node and see details"). I've got the labels sized and positioned, but when I add the TreeView, instead of fitting under the label, it pushes both labels over, no matter the position.
Here's the current result:
My main.py file--
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.treeview import TreeView, TreeViewLabel
class MainScreen(BoxLayout):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
bridge_view = TreeView(root_options=dict(text='Clients'),
hide_root=False, indent_level=4,
pos_hint={"x": 0, "y": 0},
size_hint=(0.1, 0.5))
clients = {'client1': 'Connection A',
'client2': 'Connection B'}
for client in clients.keys():
node = bridge_view.add_node(TreeViewLabel(text=client))
bridge_name = clients[client]
bridge = bridge_view.add_node(TreeViewLabel(text=bridge_name), node)
self.add_widget(bridge_view)
class ConnectionApp(App):
def build(self):
self.title = 'My Support App'
return MainScreen()
if __name__ == '__main__':
ConnectionApp().run()
This is my kv file--
<Label>:
font_size: 30
<MainScreen>:
Label:
text: "Connections"
size_hint: 0.1, 0.5
pos_hint: {"left": 0.2, "top": 1.2}
color: 1,0,1,1
outline_color: 1,0,1,1
Label:
text: "Modules"
size_hint: 0.1, 0.5
pos_hint: {"right": 0.2, "top": 1.2}
Another thing that seems strange to me was how I had to use pos_hint to get my label spacing to work. As I understand it, these values should be on a scale between 0-1. A tutorial I read indicated as much--
Pos_hint gives a hint at the position, which is measured relatively
between 0 and 1, where 1 is "completely" something and 0 is "not"
something.
Does anyone know why it took using a value greater than 1 for "top" to get these labels at the top? I'm guessing this may be a hint as to why my layout isn't showing up correctly.
TreeView under Labels
The solution is to use GridLayout as child of BoxLayout (root widget, MainScreen), Labels and TreeViews as child of GridLayout. Added an ObjectProperty, container to hook it up to the GridLayout widget (place holder for the TreeView widget) created in the kv file. Please to the example for details.
Example - TreeView under Labels
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.treeview import TreeView, TreeViewLabel
from kivy.properties import ObjectProperty
from kivy.lang import Builder
Builder.load_string('''
#:kivy 1.10.0
<Label>:
font_size: 30
<MainScreen>:
container: tree_view
orientation: 'vertical'
GridLayout:
cols: 2
size_hint: 0.5, 0.1
Label:
canvas.before:
Color:
rgba: 1, 0, 0, 1 # red
Rectangle:
size: self.size
pos: self.pos
text: "Connections"
color: 1,0,1,1
outline_color: 1,0,1,1
Label:
canvas.before:
Color:
rgba: 0, 0, 1, 1 # blue
Rectangle:
size: self.size
pos: self.pos
text: "Modules"
GridLayout:
cols: 1
id: tree_view
''')
class MainScreen(BoxLayout):
container = ObjectProperty(None)
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
bridge_view = TreeView(root_options=dict(text='Clients'),
hide_root=False, indent_level=4,
pos_hint={"x": 0, "y": 0},
size_hint=(0.1, 0.5))
clients = {'client1': 'Connection A',
'client2': 'Connection B'}
for client in clients.keys():
node = bridge_view.add_node(TreeViewLabel(text=client))
bridge_name = clients[client]
bridge = bridge_view.add_node(TreeViewLabel(text=bridge_name), node)
self.container.add_widget(bridge_view)
class DemoApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
DemoApp().run()
Output - TreeView under Labels
pos_hint: {'top': 1}
The reason you have to use a value greater than 1 for top because the Label widget's height is 0.5 (size_hint: 0.1, 0.5). The solution to use pos_hint: {'top': 1} is to reduce size_hint_y as shown in the snippets.
In my example, Label's canvas color were added for visualization/demonstration.
Snippets
Label:
...
size_hint: 0.1, 0.1
pos_hint: {"top": 1}
Example - pos_hint
kv file
#:kivy 1.10.0
<Label>:
font_size: 30
<MainScreen>:
Label:
canvas.before:
Color:
rgba: 1, 0, 0, 1 # red
Rectangle:
size: self.size
pos: self.pos
text: "Connections"
size_hint: 0.1, 0.1
pos_hint: {"left": 0.2, "top": 1}
color: 1,0,1,1
outline_color: 1,0,1,1
Label:
canvas.before:
Color:
rgba: 0, 0, 1, 1 # blue
Rectangle:
size: self.size
pos: self.pos
text: "Modules"
size_hint: 0.1, 0.1
pos_hint: {"right": 0.2, "top": 1}
Output - pos_hint
I'm trying to start a game when I click on "start game". Menu screen is removed and game launches (I can hear the sounds and then being killed by an enemy) but can't see a thing. All I get is a black screen. It's like the game is launched in the background.
What am I doing wrong ?
updated kv file:
<Game_Screen>:
name: 'game_screen'
on_enter: app.launch_game()
<Game_Menu>:
FloatLayout:
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
size_hint: None, None
canvas.before:
Color:
rgba: 1, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'SPACE SHOOTER'
font_size: 40
font_name: "fonts/Alexis Italic.ttf"
color: 0, 1, 0, 1
pos_hint: {'center_x': 0.5, 'center_y': 1.7}
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
size: self.parent.size
pos: self.parent.pos
orientation: 'vertical'
Label:
text: 'menu'
font_name: "fonts/Alexis Bold.ttf"
font_size: 30
color: 0, 1, 0, 1
Button:
text: 'start game'
on_press: app.root.current = 'game_screen'
Button:
text: 'highest score'
on_release:
Button:
text: 'credits'
on_release: app.root.current = 'credits'
updated Python code:
class Game(Widget):
# works fine on its own
class Game_Menu(Screen):
pass
class Game_Screen(Screen):
pass
class Menu_UI(Widget):
pass
class Credits_Screen(Screen):
pass
class GameApp(App):
sm = ScreenManager()
sm.add_widget(Game_Menu(name='menu'))
sm.add_widget(Credits_Screen(name='credits'))
sm.add_widget(Game_Screen(name='game_screen'))
def launch_game(self):
game = Game()
Window.size = game.size
engines1.loop = True
engines1.play()
return game
def build(self):
return self.sm
if __name__ == '__main__':
GameApp().run()
Problem
Programming Guide » Events and Properties » Main loop
In Kivy applications, avoid long/infinite loops or sleeping. The
program will never exit your loop, preventing Kivy from doing all of
the other things that need doing. As a result, all you’ll see is a
black window which you won’t be able to interact with.
In your kv file, you have FloatLayout:, Kivy will consider that as a root widget.
Rule context
The root rule is declared by declaring the class of your root widget,
without any indentation, followed by : and will be set as the root
attribute of the App instance.
Solution
Use Clock schedule functions e.g. schedule_interval(), schedule_once(), or create_trigger() in replacement of long/infinite loops or sleeping.
Move codes for sm immediately after GameApp into the build method.
Indent all the codes after <Game_Menu>
Snippets - main.py
class GameApp(App):
def launch_game(self):
game = Game()
Window.size = game.size
engines1.loop = True
engines1.play()
return game
def build(self):
self.sm = ScreenManager()
self.sm.add_widget(Game_Menu(name='menu'))
self.sm.add_widget(Credits_Screen(name='credits'))
self.sm.add_widget(Game_Screen(name='game_screen'))
return self.sm
Snippets - game.kv
#:kivy 1.10.0
<Game_Screen>:
name: 'game_screen'
on_enter: app.launch_game()
<Game_Menu>:
FloatLayout:
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
size_hint: None, None
canvas.before:
Color:
rgba: 1, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'SPACE SHOOTER'
font_size: 40
font_name: "fonts/alexisv3ital.ttf" # "fonts/Alexis Italic.ttf"
color: 0, 1, 0, 1
pos_hint: {'center_x': 0.5, 'center_y': 1.7}
Output