Python / Kivy - Changing Screen with phone screen orientation (android) - python

I haven't found anything on the internet so far, all I want to know is if its possible to change the current screen in kivy depending on the orientation of the phone's screen (Landscape or portrait) and how to do it. or at least a link to a tutorial because I couldn't find one myself.

I haven't found a way of doing this by recognizing the android device's orientation. However, the following program changes it's contents by comparing the window's height and width. The program's Label changes dynamically, so when the width is greater (landscape mode) it's Label has a different text then when the height is greater (portrait mode). Currently it works on Windows, but unfortunately I don't have a Linux system to build an android version. Theoretically it should work, but i'm not 100% sure.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.graphics import Rectangle, Color
class MainScreen(FloatLayout, Label):
"""MAIN WINDOW CLASS"""
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
with self.canvas.before:
Color(0.988, 0.725, 0.074, 1, mode='rgba')
self.rect = Rectangle(pos=self.pos, size=self.size)
self.bind(size=self.update_rect)
self.titlos = Label(text="",
bold=True,
text_size=(None,None),
font_size="20sp",
pos_hint={'center_x': 0.5, 'center_y': .85},
size_hint_y=None,
size = self.size,
height=self.texture_size[1],
halign="center",
valign = "middle",
color=(0.055, 0.235, 0.541, 1))
self.add_widget(self.titlos)
self.bind(size=self.update_orientation)
def update_rect(self, *args):
"""FUNCTION TO UPDATE THE RECATANGLE OF CANVAS TO FIT THE WHOLE SCREEN OF MAINSCREEN ALWAYS"""
self.rect.pos = self.pos
self.rect.size = self.size
def update_orientation(self, *args):
"""FUNCTION TO UPDATE THE SCREEN CONTENTS WHEN THE WINDOW SIZE CHANGES"""
if self.parent.size[1] > self.parent.size[0]:
self.titlos.text = "This is\nPortrait\nOrientation"
else:
self.titlos.text = "This is Landscape Orientation"
# This is just for checking. Not essential to the program.
print("Width:", self.parent.size[0], ", Height:", self.parent.size[1])
class main(App):
"""BUILDING THE APP"""
def build(self):
return MainScreen()
if __name__ == "__main__":
main().run()
You can use the update_orientation function to include whatever you need to change when the orientation changes.

Related

KivyMD objects are not centered with pos_hint

So in the picture you see that the label and button are at the very end of the screen but i would like that they are both in the middle of their half. Since they are added on a BoxLayout the center of the label has to be on 25% and the center of the icon on 75% on the screens width.
PROBLEM: The label and icon are at edge of window but size_pos should center them
Is the Problem the interaction between kivy Layouts and kivymd objects?
I guess the Problem has to be inside DisconnectPage but just in case I shared the whole GUI part.
I hope someone can help me.
Picture of my problem
CENTERED = {"center_x": 0.5, "center_y": 0.5}
class Main(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.client = self.setup_client()
self.client.start_in_thread()
self.disconnect_card = DisconnectPage(self)
self.lb = MDLabel(text="No message", halign="center")
self.btn = MDFillRoundFlatButton(text="send",on_release = self.send)
self.add_widget(self.lb)
self.add_widget(self.btn)
Clock.schedule_once(self.check_disconnect)
def check_disconnect(self,_):
if self.client.thread.is_alive():
return Clock.schedule_once(self.check_disconnect,1)
self.open_disconnect_screen()
def setup_client(self):
loop = asyncio.get_event_loop()
ip = "127.0.0.1"
port = 1337
client = ClientObject(ip,port,loop,self)
return client
def send(self,_):
self.client.schedule_message("Hello My Friends")
def open_disconnect_screen(self):
self.clear_widgets()
self.add_widget(self.disconnect_card)
def reload(self,_):
print("reloading")
############### SHOWN IN IMAGE ####################
class DisconnectPage(BoxLayout):
def __init__(self, main, **kwargs):
super().__init__(**kwargs)
self.main = main
self.info = MDLabel(text="You disconnected", pos_hint = CENTERED)
self.reconnect_btn = MDIconButton(icon="sync", on_release = self.main.reload, pos_hint = CENTERED)
self.add_widget(self.info)
self.add_widget(self.reconnect_btn)
############ SHOWN IN IMAGE #######################
class MyApp(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
screen = Screen()
main = Main()
screen.add_widget(main)
return screen
app = MyApp()
app.run()
The BoxLayout assigns its horizontal space among its children according to its own rules. So the MDIcon has a default size, and the BoxLayout allocates that space for the MDIconButton. Then all the remaining space is allocated for the MDLabel. The default text positioning (halign) within a Label is left, so you can center the text within the Label by using halign='center'. If you want more control over the positioning and sizing, consider using a FloatLayout.

Unable to use "root.top-self.height" for placement properly in kivy

I am trying to create a menu using box layout in kivy. I wanted to use "root.top-self.height" so that it sticks the vertical layout from the top of screen but its still sticking from bottom. Also when I print(root.top) its strangely giving 100 which is not my screen resolution. Please let me know how can I place it accurately.
Furthermore I read somewhere that I need to use root=BoxLayout(), now after using this the button's are not clickable after adding that, before adding this I could use the buttons. Please do let me know how to deal with "root" ie screen or App size functionality.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.label import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.layout import Layout
from kivy.uix.button import Button
from kivy.lang import Builder
## CREATING A CLASS WHICH HAS SCREEN CONTENT:
class firstScreen(BoxLayout):
def __init__(self,**kwargs):
super(firstScreen, self).__init__(**kwargs)
self.orientation = 'vertical'
root = BoxLayout()
self.pos = (0 ,root.top-self.height)
print(root.top)
self.myButton1 = Button(text='Home',
color = (1,0,0,1),
size_hint = (0.1,None),
## pos_hint = {'x':.8, 'y':'.7'},
## pos_hint = {'x':0, 'top':'0'},
pos = (0,0)
)
self.myButton2 = Button(text='Buy Now',
color = (1,0,0,1),
size_hint = (0.1,None))
self.myButton3 = Button(text='Blog',
color = (1,0,0,1),
size_hint = (0.1,None))
self.myButton4 = Button(text='Contant Us',
color = (1,0,0,1),
size_hint = (0.1,None))
self.add_widget(self.myButton1)
self.add_widget(self.myButton2)
self.add_widget(self.myButton3)
self.add_widget(self.myButton4)
def on_touch_down(self,touch):
print(touch)
def on_touch_move(self,touch):
print(touch)
def on_touch_up(self,touch):
print(touch)
## CREATING A CLASS WHICH RETURNS SOME SCREEN:
class myKivyApp(App):
def build(self):
return firstScreen()
## THIS CODE RUNS THE CLASS WHICH HAS SOME SCREEN
if __name__ == "__main__":
myKivyApp().run()
Eliminating the unused BoxLayout and setting the y component of size_hint to 1.0 means that each button will share equally in the vertical space available for the firstScreen.
class firstScreen(BoxLayout):
def __init__(self,**kwargs):
super(firstScreen, self).__init__(**kwargs)
self.orientation = 'vertical'
self.myButton1 = Button(text='Home',
color = (1,0,0,1),
size_hint = (0.1,1.0))
self.myButton2 = Button(text='Buy Now',
color = (1,0,0,1),
size_hint = (0.1,1.0))
self.myButton3 = Button(text='Blog',
color = (1,0,0,1),
size_hint = (0.1,1.0))
self.myButton4 = Button(text='Contant Us',
color = (1,0,0,1),
size_hint = (0.1,1.0))
self.add_widget(self.myButton1)
self.add_widget(self.myButton2)
self.add_widget(self.myButton3)
self.add_widget(self.myButton4)
By the way, the root.top will always be 100 in the __init__() method. The 100 is the default value and is not updated until the app is actually displayed.

Dynamic grid in Kivy with each grid element containing multiple widgets

This is my first post here, but I will try to be as detailled as I can.
So my application is in python, and involves a grid in kivy, where each element in the grid is supposed to contain 4 additional widgets and possibility for a fifth. The four additional widgets are supposed to be in a cross shape at the edges and the fifth in the middle.
Problem is, whenever I add a sub widget it lands in the bottom left corner on position 0,0 of the main window
So far so good. Right now I am just trying to get even one widget inside another widget to display correctly.
Heres what I attempted:
<GridCell#Label>
BoxLayout:
orientation:'horizontal'
Button:
text:'One'
size:10,10
size_hint:None,None
Building a .kv file for my app, where I would put a box layout inside of it and then a button.
Also I tried the following class configuration:
class GridCell(Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.root = FloatLayout()
self.button = Button(text="test")
self.button.x = self.root.x
self.button.center_y = self.root.center_y
self.root.add_widget(self.button)
self.add_widget(self.root)
Also did not work.
I am adding the grid cells by just calling .add on the grid with a newly created widget for each iteration of a for loop.
All the child widgets are apparently created, but they all land in the bottom left corner!
This is the whole code of the gui right now:
import kivy
import World
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import Color, Rectangle
kivy.require('1.10.0')
class GridCell(Label):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.root = FloatLayout()
self.button = Button(text="blargh")
self.button.x = self.root.x
self.button.center_y = self.root.center_y
self.root.add_widget(self.button)
self.add_widget(self.root)
def on_size(self, *args):
self.canvas.before.clear()
if self.id is "cliff":
with self.canvas.before:
Color(249 / 255, 6 / 255, 6 / 255, 0.3)
Rectangle(pos=self.pos, size=self.size)
if self.id is "goal":
with self.canvas.before:
Color(6 / 255, 6 / 255, 249 / 255, 0.3)
Rectangle(pos=self.pos, size=self.size)
if self.id is "start":
with self.canvas.before:
Color(11 / 255, 174 / 255, 6 / 255, 0.3)
Rectangle(pos=self.pos, size=self.size)
if self.id is "normal":
with self.canvas.before:
Color(119 / 255, 115 / 255, 115 / 255, 0.3)
Rectangle(pos=self.pos, size=self.size)
class GameGridApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.grid = GridLayout(cols=8, rows=5)
self.load_into()
def load_into(self):
world = World.World(8, 5)
world.build_gamegrid()
for cell in world.gamegrid:
name = str(cell.name)
grid_cell = GridCell()
grid_cell.text = name
if cell.start:
grid_cell.id = "start"
if cell.goal:
grid_cell.id = "goal"
if cell.cliff:
grid_cell.id = "cliff"
if cell.field:
grid_cell.id = "normal"
self.grid.add_widget(grid_cell)
def build(self):
return self.grid
customLabel = GameGridApp()
customLabel.run()
I may give an idea , that create a 'subgrids' object and a 'main grid' object that contain the 'subgrids'. These two objects would be GridLayout objects.
Here is a simple example in python2.7 :
import kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
class SubGrids(GridLayout):
def __init__(self):
GridLayout.__init__(self, cols=3, rows=3);
self.add_widget(Label(text='1st'));
self.add_widget(Label(text=''));
self.add_widget(Label(text='2nd'));
self.add_widget(Label(text=''));
self.add_widget(Label(text='3rd'));
self.add_widget(Label(text=''));
self.add_widget(Label(text='4th'));
self.add_widget(Label(text=''));
self.add_widget(Label(text='5th'));
class Grids(GridLayout):
def __init__(self):
GridLayout.__init__(self, cols=2, rows = 2);
self.add_widget(SubGrids());
self.add_widget(SubGrids());
self.add_widget(SubGrids());
self.add_widget(SubGrids());
class Example(App):
def build(self):
return Grids()
if __name__ == '__main__':
x = Example();
x.run();
Hope this gives an idea.

Kivy places widgets slightly differently through Python and Kivy language. Am I missing something?

I just picked up Kivy and encountered this problem. If there is a better way to achieve what I'm trying to in general I'd love to hear about it, though.
What I've noticed is that, when I add a widget to another widget, if I do so through Python code it will be slightly at a different position than had I done so through Kivy. I'll paste my code below (it's pretty short right now) and you can just try it yourself and you'll see what I mean.
client.py:
import kivy
kivy.require('1.9.1') # current kivy version
from kivy.config import Config
Config.set('graphics', 'width', '360')
Config.set('graphics', 'height', '640')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, NumericProperty, ReferenceListProperty
from kivy.graphics import Color, Rectangle
from kivy.clock import Clock
from kivy.vector import Vector
from random import randint
class Bird(Widget):
'''
Bird Widget defines each sprite. / Test version: blue and red cards
Defined attributes:
SIZE
POSITION
(COLOR)
Upade the position of this widget individually every 0.7 seconds with 60 fps
'''
# set attributes
border_color = (1,1,1)
r = NumericProperty(0)
g = NumericProperty(0)
b = NumericProperty(0)
color = ReferenceListProperty(r, g, b) # initial color = red // maybe make it random
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(-3)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def __init__(self, **kwargs):
super(Bird, self).__init__(**kwargs)
self.pick_color()
# Randomly generate 0 or 1, and pick a color based on that
def pick_color(self):
color_num = randint(0,1)
if color_num == 0: # blue
self.color = (0,0,1)
elif color_num == 1: # red
self.color = (1,0,0)
# Move the widget by -3y increment at 60 fps
def increment(self, dt):
self.pos = Vector(*self.velocity) + self.pos
def move(self):
# While the sprite moves at 60 fps, the movement is "cancelled" after 0.3 seconds
# This event sequence is refreshed every 0.7 seoncds in MainApp class
move = Clock.schedule_interval(self.increment, 1.0/60.0)
stop = Clock.schedule_once(lambda dt: move.cancel(), 0.3)
class GameMain(Widget):
'''
Contains two functions: ADD_NEW_BIRD() and UPDATE().
All controls happen in this widget
Not using kivy.screen because there is only one screen used
ADD_NEW_BIRD() adds a new bird to list_of_birds AND add it as a child widget to the GameMain Widget. UPDATE() calls MOVE() (Bird Widget) and receives events
Create global variable limit = 0; if limit == 4, game over; variable accessed in update function, which checks whether the limit has been reached. If the player makes the right decision, then limit -= 1
'''
limit = 0
def add_new_bird(self):
self.new_bird = Bird(center_x=self.center_x, center_y=self.height/1.5)
print (self.center_x, self.height)
self.new_bird.pick_color()
self.add_widget(self.new_bird)
def update(self, dt):
for bird in self.children:
bird.move()
self.add_new_bird()
class MainApp(App):
def build(self):
game = GameMain()
Clock.schedule_interval(game.update, 0.7)
return game
if __name__ == '__main__':
MainApp().run()
main.kv:
#:kivy 1.9
<Bird>:
size: 70, 80
canvas:
Color:
rgb: self.border_color
Rectangle:
size: self.size
pos: self.pos
Color:
rgb: self.color
Rectangle:
size: root.width - 10, root.height - 10
pos: root.x + 5, root.y + 5
<GameMain>
Bird:
center_x: root.center_x
center_y: root.height / 1.5
The code does exactly what I want it to do (I'm going to touch on the z-values later) except that the very first card is slightly off to the left. I'm just really confused because center_x: root.center_x in main.kv should not be any different from Bird(center_x=self.center_x in client.py as far as I understand. I've tried initializing the first instance of Bird() inside of an init function like so:
def __init__(self, **kwargs):
super(GameMain, self).__init__(**kwargs)
self.bird = Bird(center_x=self.center_x, center_y=self.height/1.5)
self.bird.pick_color()
self.add_widget(self.bird)
And the problem was still there! If anyone could explain what's going on/what I'm doing wrong and maybe even suggest a better way to approach this, I'd appreciate it.
Just in case you're curious, I need to add widgets directly from Python code because I need the app to constantly produce a new card at a constant time interval. The first card, however, is initialized in the Kivy file for the sake of simplicity. To be fair it works pretty well except for the offset. And lastly I'm not using a Layout because I wasn't sure which one to use... I did lay my hands on FloatLayout for a bit but it didn't seem like it was going to fix my problem anyway.
When constructed, Widget has an initial size of (100, 100). If you change size from this:
<Bird>:
size: 70, 80
to this:
<Bird>:
size: 100, 80
rectangles will align correctly. Initial rectangle, created in kv file, is centered at the parent window, other ones that are created in Python code are offset to the left.
If you change Bird constructor in Python code from this:
def __init__(self, **kwargs):
super(Bird, self).__init__(**kwargs)
self.pick_color()
to this (effectively overriding the default widget size from (100, 100) to be (50,50)):
def __init__(self, **kwargs):
self.size = (50, 50)
super(Bird, self).__init__(**kwargs)
self.pick_color()
you'll notice that rectangles created in Python code will shift to the right. Change kv file from:
<Bird>:
size: 70, 80
to:
<Bird>:
size: 50, 80
which matches (new) initial widget size of (50,50) in width, all rectangles will be aligned again.
Solution to your problem would be to leave all as is, except to set size for new birds in Python to be equal to that in kv file:
def __init__(self, **kwargs):
self.size = (70, 80)
super(Bird, self).__init__(**kwargs)
self.pick_color()
and all will work as intended.
This all means that size property from kv file is not applied to your Python-side created Birds, only to the one created by kv declaration. Is this Kivy bug or maybe you are missing one more step in the Python code to make Builder apply size from kv file to Python-created Birds, I have no idea right now.
In my experience, at this point of Kivy development, mixing too much kv and Python code will result in these kind of weird issues you have here. It is best to either handle all view related stuff in kv or to ditch kv completely and build everything in Python.
Some things don't work at all in kv, i.e. setting cols property of GridLayout (v1.9.1).
Personally, for now, I stick to well organized Python code to build UI and don't use kv files almost at all.
Hope this helps a bit...

How to allow user to choose file as background image in kivy?

Or, how to do so using python, give it an ID, and set it as background image in kv language?
I would like to be able to draw on top of an image instead of a black screen, which I am doing here:
edited
new problem: upload button does not work, here is new code
from random import random
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.graphics import Color, Line, Rectangle
from kivy.uix.filechooser import FileChooserListView, FileChooserIconView
from kivy.uix.floatlayout import FloatLayout
class MyPaintWidget(Widget):
def on_touch_down(self, touch):
color = (random(), random(), random())
with self.canvas:
Color(*color)
d = 30.
touch.ud['line'] = Line(points=(touch.x, touch.y))
def on_touch_move(self, touch):
touch.ud['line'].points += [touch.x, touch.y]
class MyPaintApp(App):
def build(self):
parent = Widget()
painter = MyPaintWidget()
Choose = Button(text = 'upload image')
parent.add_widget(painter)
parent.add_widget(Choose)
def chooose_file(obj):
fc = FileChooserIconView(title= 'upload image')
image_path = self.fc.selection[0]
image_name = file_path.split('/')[-1]
with self.canvas.before:
Rectangle(
size=self.size,
pos=self.pos,
source=image_name)
Choose.bind(on_release=choose_file)
return parent
if __name__ == '__main__':
MyPaintApp().run()
What about this:
If you used the kivy filechooser to get the user to select an image file,
then you could use the .selection attribute of the filechooser to get the name and/or path of that file. Once you have that, you could use it to set the source of a Rectangle on the canvas of the layout etc. where you want the background image.
For instance, to set a background image on a BoxLayout, inside the class that inherits from the BoxLayout:
fc = FileChooserIconView(title="Choose Image")
image_path = self.fc.selection[0]
image_name = file_path.split('/')[-1]
with self.canvas.before:
Rectangle(
size=self.size,
pos=self.pos,
source=image_name)
This is of course a very simplistic example, and isn't really taking the rest of your code into account, but with the kivy docs on FileChooser you should get it. Worth noting also that you could do this in the kv file, perhaps much more cleanly.

Categories

Resources