As far as I am aware, kv language is useful when making a static display, e.g. not when making game which need much widget's positioning during runtime. Here I try to make a simple game, but still need much positioning, so kv language is out of context for the widget, but not for the screen. I use screen to differentiate the main menu and game screen. But when I try to use 'add_widget' to insert my image, it always positioned at the middle of the window. Later I found out that the screen size is only 100x100.
Below are the only way that I can thought of, but still with no luck:
class HomeScreen(Screen):
pass
class GameScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation=Builder.load_file('ProjectSD.kv')
class ProjectSDApp(App):
def build(self):
A=presentation
A.screens[0].size=(Window.size)
A.screens[0].add_widget(Label(text='hello',font_Size=80,pos=(0,0)))
return A
if __name__=='__main__':
print(Window.size)
ProjectSDApp().run()
and my ProjectSD.kv file:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
HomeScreen:
GameScreen:
<Button>:
font_name:'attackofthecucumbers.ttf'
<HomeScreen>:
name:'home'
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'nature.jpg'
Label:
text: 'Monopoly GX'
font_name:'KBDunkTank.ttf'
font_size:100
size_hint:0.7,0.2
pos:root.width*0.15,root.height*0.70
Button:
on_release: app.root.current = "game"
text: 'Play Game'
font_size:root.width/20
size_hint:0.3,0.15
pos:root.width*0.35,root.height*0.45
Button:
on_release: app.stop()
text: 'exit'
font_size:root.width/20
size_hint:0.3,0.15
pos:root.width*0.35,root.height*0.20
<GameScreen>:
name:'game'
Button:
on_release: app.root.current = "home"
background_color: (1,0.15,0.2,0.8)
text: 'X'
font_size:root.width/40
size_hint:0.05,0.05
pos:root.width*0.95,root.height*0.95
Since there is no 'pos' method in screen object, I put my widget to position (0,0) manually.
The only way I found is just below:
https://kivyspacegame.wordpress.com/2014/08/10/tutorial-flappy-ship-part-2-build-simple-menus-and-animate-your-games-using-clock/
So my question is, if I use screen object from kivy's build in, how to achieve the same result? So I can still adding and remove widget as I want it later?
I'm not entirely clear on what you're asking; I don't see anywhere in your code where you are trying to add an Image to a Layout. You add a label to the middle and that works fine.
I think because you are creating screens without any layouts and not setting a default window size, that the screens just take up their minimum default size. You need to add a layout and fill it with stuff that has a defined size, or set a window size at the beginning e.g:
# window import
from kivy.core.window import Window
from kivy.utils import get_color_from_hex
Window.size = (1920/2,1080/2)
Window.clearcolor = get_color_from_hex('#000000') # black
Here is some code that creates your two Screens, and adds a draggable and a static Image at a position to a FloatLayout.
from kivy.app import App
from kivy.uix.screenmanager import (ScreenManager, Screen)
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
class HomeScreen(Screen):
pass
class GameScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
class MovingImage(DragBehavior,Image):
pass
class DragLabel(DragBehavior, Label):
pass
class StaticImage(Image):
pass
class MyApp(App):
def build(self):
A = ScreenManagement()
A.current = 'game'
A.screens[1].ids.gamefloat.add_widget(MovingImage(size_hint = [0.3,0.3]))
A.screens[1].ids.gamefloat.add_widget(StaticImage(pos = (150,300)))
return A
if __name__=='__main__':
MyApp().run()
and the .kv file called myapp.kv
<ScreenManagement>:
HomeScreen:
GameScreen:
<Button>:
<HomeScreen>:
name:'home'
canvas:
Rectangle:
pos: self.pos
size: self.size
source: 'image.jpg'
Label:
text: 'Monopoly GX'
font_size:100
size_hint:0.7,0.2
pos:root.width*0.15,root.height*0.70
Button:
on_release: app.root.current = "game"
text: 'Play Game'
font_size:root.width/20
size_hint:0.3,0.15
pos:root.width*0.35,root.height*0.45
Button:
on_release: app.stop()
text: 'exit'
font_size:root.width/20
size_hint:0.3,0.15
pos:root.width*0.35,root.height*0.20
<GameScreen>:
name:'game'
FloatLayout:
id: gamefloat
Button:
on_release: app.root.current = "home"
background_color: (1,0.15,0.2,0.8)
text: 'X'
font_size:root.width/40
size_hint:0.05,0.05
pos:root.width*0.95,root.height*0.95
<MovingImage>:
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 1000000
drag_distance: 0
source: 'image.jpg'
<StaticImage>:
source: 'image.jpg'
Related
I'm making an app in python kivy and in my tacscreen I have a draggable Image and an Label, whenever I drag my draggable Image my label drags with it as well. The position of the label is just below the draggable Image. The problem that I'm facing is whenever I resize my window and drag the image the label goes from this to this the space between the Image and the label is too much. How can I fix this? I want the label to always be like how it is in the first screenshot even after I resize the screen and drag. Below is my code. I have been trying to find a solution to this for a months now. I really appreciate any help. Thanks!
main.py
from kivy.properties import BooleanProperty
from kivy.properties import ObjectProperty
from kivy.metrics import dp
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
from kivy.uix.behaviors import DragBehavior
from kivy.uix.image import Image
class DragImage1(DragBehavior, Image):
dragging = BooleanProperty(False)
drag_label = ObjectProperty()
def on_touch_move(self, touch):
if touch.grab_current is self:
self.drag_label.pos = self.x, self.y - dp(300)
return super().on_touch_move(touch)
def on_touch_up(self, touch):
uid = self._get_uid()
if uid in touch.ud:
# do something on drop
print(self.source, 'dropped at', touch.x, touch.y)
return super(DragImage1, self).on_touch_up(touch)
class TacScreen(Screen):
Pass
GUI = Builder.load_file("main.kv")
class MainApp(App):
def build(self):
return GUI
def change_screen(self, screen_name, *args):
self.root.current = screen_name
MainApp().run()
main.kv
#:include tacscreen.kv
ScreenManager:
id: screen_manager
TacScreen:
name: "tac_screen"
id: tac_screen
tacscreen.kv
<tacscreen>:
#:import utils kivy.utils
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: "Background.png"
DragImage1:
drag_label: per
pos: root.center_x - self.width/2, root.center_y - self.height/0.3
size_hint: 1, .1
source: "Image.png"
Label:
id: per
text: "Hello"
font_size: "20dp"
pos: root.center_x - self.width/2, root.center_y - self.height/1.2
You can modify your kv to do the positioning for you:
DragImage1:
id: di # added id for use by the Label
drag_label: per
pos: root.center_x - self.width/2, root.center_y - self.height/0.3
size_hint: 1, .1
source: "Image.png"
Label:
id: per
text: "Hello"
font_size: "20dp"
size_hint: None, None
size: self.texture_size # set size to just contain the text
pos: di.center_x - self.width/2, di.y - self.height # adjust postion based on the DragImage1
And, since kv is now handling the positioning, you should remove the on_touch_move() method from the DragImage1 class.
Note that this will handle positioning when resizing before dragging. But if you drag before resizing, then the resizing will revert the position to that defined in the kv.
This should be a pretty easy fix. I'm new to Kivy. I'm trying to have the canvas cleared on a button press, and then display a new widget to essentially move to another page. When I run it and press the button, the canvas is cleared, but I get nothing from IntroPage.
Python Script:
import kivy
kivy.require('2.0.0')
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
class ABT(App):
def build(self):
return WelcomePage()
class WelcomePage(Widget):
def btn(self):
self.canvas.clear()
print('pressed')
return IntroPage()
class IntroPage(Widget):
def __init__(self):
pass
if __name__ == '__main__':
ABT().run()
KV File:
<WelcomePage>
canvas.before:
Color:
rgba: (.43,.51,.92,.26)
Rectangle:
pos: self.pos
size: self.size
GridLayout:
cols:1
size: root.width, root.height
Image:
source: 'abt1t.png'
size_hint: (1,.8)
Button:
text:"begin"
background_color: (.43,.51,.92,.26)
size_hint: (1,.2)
on_press: root.btn()
<IntroPage>
GridLayout:
cols:1
Label:
text:"This won't show up!"
Returning IntroPage from btn won't work, as btn isn't a build method.
The best way to implement multiple pages is probably to use ScreenManager:
from kivy.uix.screenmanager import ScreenManager, Screen
class ABT(App):
def build(self):
sm = ScreenManager()
sm.add_widget(WelcomePage(name="welcome"))
sm.add_widget(IntroPage(name="intro"))
return sm
class WelcomePage(Screen):
def btn(self):
print('pressed')
self.manager.current = "intro"
class IntroPage(Screen):
def __init__(self):
pass
I am developing an app using Kivy for Android and IOS, i haven't tried it on any mobile yet but i have been noticing that i need to add some sort off 'touch' events so the buttons, swipes and etc will work on a mobile touchscreen.
i've tried searching around for examples but i didn't exactly get the concept, and i was wondering what exactly do i need to do for it to be ready for touch events, i am thinking of making a 'swiping touch' to move between pages on both sides, what do i need to do to make this happen?
When you want to swipe through pages as you have mentioned. It could be a nice option to use PageLayout. Check the docs:https://kivy.org/doc/stable/api-kivy.uix.pagelayout.html
PageLayout is where you have several pages and you can swipe through them. It could be suitable for you.
Here is a very simple example.Where you will have three pages
PageLayout:
Button:
text: 'page1'
Button:
text: 'page2'
Button:
text: 'page3'
Here is a advanced pagelayout example from kv file. The pagelyout then consists of floatlayouts. But you could use other layouts too
<Example>:
PageLayout:
FloatLayout:
id: string1
canvas:
Color:
rgb:utils.get_color_from_hex("#2BFFFA")
Rectangle:
size: self.size
pos:self.pos
Image:
size_hint:.9,1
pos_hint:{"top": 1,"right": 1}
source: "string/str1.png"
FloatLayout:
canvas:
Color:
rgb:utils.get_color_from_hex("#39FF22")
Rectangle:
size: self.size
pos:self.pos
Image:
size_hint:.9,1
pos_hint:{"top": 1,"right": 1}
source: "string/str1_sol.png"
FloatLayout:
canvas:
Color:
rgb:utils.get_color_from_hex("#2BFFFA")
Rectangle:
size: self.size
pos:self.pos
Image:
size_hint:.9,1
pos_hint:{"top": 1,"right": 1}
source: "string/str2.png"
Notice that my main layout is a pagelayout and in the floatlayout I have floatlayouts. So now I have three pages to swipe through
Or you could use a screemanger to manage your screens. . And then you just have to click on a button and then you will automatically switch screens. Here is a example. This is better than pagelayouts.
import kivy
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
class ScreenOne(Screen):
def __init__ (self,**kwargs):
super (ScreenOne, self).__init__(**kwargs)
my_box1 = BoxLayout(orientation='vertical')
my_label1 = Label(text="BlaBlaBla on screen 1", font_size='24dp')
my_button1 = Button(text="Go to screen 2",size_hint_y=None, size_y=100)
my_button1.bind(on_press=self.changer)
my_box1.add_widget(my_label1)
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)
my_box1 = BoxLayout(orientation='vertical')
my_label1 = Label(text="BlaBlaBla on screen 2",font_size='24dp')
my_button1 = Button(text="Go to screen 1",size_hint_y=None, size_y=100)
my_button1.bind(on_press=self.changer)
my_box1.add_widget(my_label1)
my_box1.add_widget(my_button1)
self.add_widget(my_box1)
def changer(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()
I am new to Kivy and need some help in understanding function scope. I have built a simple app with two screens. The first screen has two buttons and the second screen has a text label at the centre. On the first screen, I have used app.root.current='new_screen_name' with the on_release attribute for one button and it works fine. It takes me to the next screen which is the intended function. For the second button, I have used a function call which has been defined in the Python file under the class definition of the first screen (the root widget of the button). However, this method does not work and the app window simply closes. I guess I am making a mistake in the function scope and call But I cannot figure out what. Any help would be greatly appreciated.
Python file:
from kivy.config import Config
# Config.set should be used before importing any other Kivy module.
Config.set('kivy','window_icon','sivaicon.png')
# Config set for resizing image button
Config.set('graphics', 'resizable', True)
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.lang.builder import Builder
class SivaLoginScreen(Screen):
def func_authentication(self):
app.root.current='tabbed_screen'
class SivaTabbedScreen(Screen):
pass
class SivaScreenManager(ScreenManager):
pass
class ImageButton(ButtonBehavior, Image):
pass
# Tell Kivy to directly load a file. If this file defines a root widget, it will be returned by the method.
root_widget = Builder.load_file('siva.kv')
class SivaApp(App):
def build(self):
# Initialize root widget
return root_widget
if __name__ == '__main__':
# Run application
SivaApp().run()
Kivy file (.kv):
SivaScreenManager:
SivaLoginScreen:
SivaTabbedScreen:
<ImageButton>:
keep_ratio: True
<SivaLoginScreen>:
name: 'login_screen'
canvas.before:
Color:
rgba: 195/255, 60/255, 35/255, 1
Rectangle:
pos: self.pos
size: self.size
FloatLayout:
size: root.width, root.height
Image:
id: login_logo_siva
source: 'images/sivalogo4.png'
keep_ratio: True
size_hint: 0.2, 0.2
pos_hint: {'center_x':0.5, 'center_y':0.75}
Label:
id: login_label_siva
pos: self.x*0.5-4, self.y*0.5+15
markup: True
font_name: 'roboto/Roboto-Medium.ttf'
text: '[color=#FDFD98]S.[/color][color=#B29DD9]I[/color][color=#FDFD98].[/color][color=#77DD77]V[/color][color=#FDFD98].[/color][color=#779ECB]A[/color]'
font_size: '50sp'
Label:
id: login_label_slogan1
pos: self.x*0.5-3, self.y*0.5-6
markup: True
font_name: 'roboto/Roboto-Regular.ttf'
text: '[color=#FDFD98]SLOGAN TEXT[/color]'
font_size: '15sp'
Label:
id: login_label_slogan2
pos: self.x*0.5-3, self.y*0.5-20
markup: True
font_name: 'roboto/Roboto-Regular.ttf'
text: '[color=#FDFD98]HEADLINE TEXT[/color]'
font_size: '15sp'
BoxLayout:
id:login_button_layout
orientation: 'horizontal'
size_hint: 0.2, 0.2
pos_hint: {'center_x':0.5, 'center_y':0.25}
ImageButton:
id: first_button
source: {'normal': 'images/first.png', 'down': 'images/first-down.png'} [self.state]
on_release: app.root.current='tabbed_screen'
ImageButton:
id: second_button
source: {'normal': 'images/second.png', 'down': 'images/second-down.png'} [self.state]
on_release: app.root.func_authentication()
<SivaTabbedScreen>:
name: 'tabbed_screen'
FloatLayout:
size: root.width, root.height
Label:
pos: self.x*0.5, self.y*0.5
text: 'SECOND SCREEN'
font_size: '50sp'
In your case, app.root link to SivaScreenManager which is the root widget of your application. And in these class, there is not a func_authenticationfunction, why you app crashed.
To refer a class itself in a KV definition, you must just use root, so right code must be :
on_release: root.func_authentication()
see Kivy Language - Reserved Keywords
Definition of func_authentication is not correct also, app is unknown. Use either :
App.get_running_app().root.current='tabbed_screen' or
self.manager.current='tabbed_screen'
I have a canvas background but I am trying to switch the back ground when a person his a correct button to move on to the next level. I am trying to do this all within one class. Is there a way to assign an image to a canvas rectangle and then on the press of a button the canvas image will change to a new source.
main.py
class MazeSolution(Screen):
def CheckDoor1(self):
if self.ids.door1.source == "correct":
print("correct")
self.ids.change.source = 's-curvee selection.png'
else:
print("incorrect")
main.kv
#:import utils kivy.utils
<MazeSolution>:
FloatLayout:
canvas:
Rectangle:
id: change
source: 'selection grass.png'
pos: self.pos
size: self.size
Button:
pos_hint: {"top": .8, "right": .75}
size_hint: .5, .1
text:
"Door 1"
source: "<random_name>"
id: door1
on_press:
root.CheckDoor1()
I don't think assigning ids within canvas instructions is supported. But you can accomplish the same thing by creating the Rectangle in python:
from kivy.app import App
from kivy.graphics.vertex_instructions import Rectangle
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
class MazeSolution(Screen):
def __init__(self):
super(MazeSolution, self).__init__()
# make the Rectangle here and save a reference to it
with self.canvas.before:
self.rect = Rectangle(source='selection grass.png')
def on_pos(self, *args):
# update Rectangle position when MazeSolution position changes
self.rect.pos = self.pos
def on_size(self, *args):
# update Rectangle size when MazeSolution size changes
self.rect.size = self.size
def CheckDoor1(self):
if self.ids.door1.source == "correct":
print("correct")
# use the saved reference to change the background
self.rect.source = 's-curvee selection.png'
else:
print("incorrect")
Builder.load_string('''
<MazeSolution>:
FloatLayout:
Button:
pos_hint: {"top": .8, "right": .75}
size_hint: .5, .1
text:
"Door 1"
source: "correct"
id: door1
on_press:
root.CheckDoor1()
''')
class TestApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MazeSolution())
return sm
TestApp().run()
The on_pos() and on_size() methods perform the operations that kv would set up for you automatically, but must be set up manually since the Rectangle is not created in kv.