Kivy ScrollView with over lapping layouts - python

I am trying to draw 10 images on the screen, then draw 10 more images (of the same size and shape) over them, in the same locations. This is fine but I also want it all the be scrollable but I get the error Exception: ScrollView accept only one widget
This is my code:
root = ScrollView(size_hint=(1,1), scroll_wheel_distance=40)
layout1 = GridLayout(cols=2, spacing=0, size_hint=(1, None), row_force_default=True, row_default_height=270)
layout1.bind(minimum_height=layout1.setter('height'))
for i in range(10):
img = Image(source=UI_bottom_path, size_hint_y=1, allow_stretch=True)
layout1.add_widget(img)
layout2 = GridLayout(cols=2, spacing=0, size_hint=(1, None), row_force_default=True, row_default_height=270)
for i in range(10):
img = Image(source=UI_top_path, size_hint_y=1, allow_stretch=True)
layout2.add_widget(img)
root.add_widget(layout1)
root.add_widget(layout2)
self.add_widget(root)
If I comment out "root.add_widget(layout2)" it works fine but doesn't draw the images.
This is how it currently looks:
This is how each should look:
Is there a way to get ScrollView and have the layers on top of each other?

Like your error says, ScrollView accepts only one widget. When you commented out "root.add_widget(layout2)" Scrollview has only one widget and that is why it works.
So you can add only one layout to Scrollview like BoxLayout or GridLayout and add the images to the same layout.
And please add your complete code and then we can show an example.

Related

No effects on size_hint , python, kivy

I try to change the size_hint of a button in Python, Kivy, but every value i put there the size of the button remain the same.. for the pos is changing but for the size_hint no, and if i change from pos to pos_hint the button is stuck in the corner of the window and from there i cant change nothing... also i tried to stick the pos of the button on a position where is a text in the photo but every time when i resize the kivy window the button and image are changing theyr position.. how i can solve this probelm ?? THANKSSS !!
from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.button import Button
from random import choice
class MyImage(Image):
images = ["Q1.png", "Q2.png", "Q3.png"] # ........
def __init__(self, **kwargs):
super().__init__(**kwargs)
correct = Button(pos=(200, 200), size_hint=(.1, .1), on_release=self.random_image, background_color=(1, 1, 1, 0.2))
self.add_widget(correct)
self.random_image()
def random_image(self, *_):
self.source = choice(self.images)
class RandomQuestionApp(App):
def build(self):
return MyImage()
randomApp = RandomQuestionApp()
RandomQuestionApp().run()
Try size instead of size_hint.
correct = Button(
pos=(200, 200),
size=(100, 100),
on_release=self.random_image,
background_color=(1, 1, 1, 0.2)
)
size : This is for static sizing of widgets and takes two arguments
i.e. (width, height). Default size of the button = (100, 100).
size_hint : This is for dynamic sizing of the button and provide hint
of size. It contains two arguments i.e. width and height it can be
floating values.By default, all widgets have their size_hint=(1, 1).
If there is nothing that influences the button size, your dynamics won't do anything. Size on the other hand would define a fixed size of the button where 100x100 is the default.

How do I position a Label in Kivy Python?

Alright, my question is "How can I position a Label in Kivy?" like I do know, that in Tkinter we have a "Place" attribute in Labels. But, I am a new Kivy learner so I can't figure it out the placement of a kivy label in a kivy window. Here's a simple program I built.
import kivy
from kivy.app import App
from kivy.uix.label import Label
class MyApp(App):
def build(self):
return Label(text = "Hey there! I am a Label! [Place Me!]")
MyApp().run()
According to the official kivy documentation about widget positioning, there's 2 ways to do it:
Using the pos argument to assign widget's position by pixels, similar to the x and y argument from tkinter:
def build(self):
return Label(text = "Hey there! I am a Label! [Place Me!]",
pos = (200, 300),
)
Using the pos_hint argument to assign widget's position as a fraction between 0.0 and 1.0 of the height and width of the window/parent widget, similar to the relx and rely argument from tkinter:
def build(self):
return Label(text = "Hey there! I am a Label! [Place Me!]",
pos_hint = (0.5, 0.35),
)
Keep in mind that though it's pretty similar to how it works in tkinter, the kivy's widgets are positioned based from the left and the bottom side of the window, different from the left and top fashion of tkinter, for example relx=0.5, rely=0.3 would be at the same place as pos_hint=(0.5,0.7).

Kivy. Why size_hint doesn't work at widget?

I want to scale an image in Widget using size_hint, but it doesn't work. What did I do wrong?
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
class Game(Widget):
def __init__(self):
super().__init__(size=[1280, 960])
self.add_widget(Image(source='img.jpg', size_hint=(1, 1)))
class GameApp(App):
def build(self):
gm = Game()
return gm
GameApp().run()
As a result I see a black window with a 100x100 image.
The problem is that you are adding your Image to a class that extends Widget. The Widget class is not intended as a container, and does not handle things like size_hint. So, I suggest changing Game to extend some type of container, like FloatLayout. Here is your code with that change:
class Game(FloatLayout):
def __init__(self):
super().__init__(size=[1280, 960])
self.add_widget(Image(source='img.jpg', size_hint=(1,1), allow_stretch=True))
You may need the allow_stretch=True to allow the Image to stretch the source.
Question
Why size_hint doesn't work for Widget?
Answer
The keyword, size_hint that you have provided, is not applying to the
Widget. It is applying to the Image object. The base class of Image widget is a Widget. The default size of a widget is (100, 100) and default size_hint is (1, 1). Therefore, by providing size_hint=(1, 1) when instantiating an Image widget, you are not changing the default size_hint or size.
When you provided size=[1280, 960] to the super constructor, you have already overrode the default size of the widget of (100, 100) or default size_hint of (1, 1)
Solution
If you add pos=self.pos, size=self.size, allow_stretch=True) to Image, you will get an image displayed.
But there is still a black strip at the bottom.
Snippets
self.add_widget(Image(source='Jupiter.png', pos=self.pos, size=self.size, allow_stretch=True))
Recommended Solution.
You can replace Image object by doing the following:
Use the Widget's canvas to display an image in a Rectangle
Bind the Widget's pos and size to a method to redraw the image
Add import statement, from kivy.graphics import Rectangle
Snippets
from kivy.graphics import Rectangle
class Game(Widget):
def __init__(self):
super().__init__(size=[1280, 960])
with self.canvas:
self.rect = Rectangle(source="Jupiter.png", pos=self.pos, size=self.size)
self.bind(pos=self.redraw, size=self.redraw)
def redraw(self, *args):
self.rect.size = self.size
self.rect.pos = self.pos
Output

Unable to size widgets properly

In the app I am creating I have a screen manager that holds a few screen instances. In one of them I want to have 2 standard widgets, and a number of buttons that are added dynamically by the user. If those buttons are too many, the user should be able to scroll through them till he finds the one he wants.
To accomplish that I use a grid layout that holds two objects: another grid layout and a scrollview. The grid layout is in charge of the 2 standard widgets(a text input and a button). The scrollview is in charge of the dynamically added buttons.
I want the scrollview part to occupy the bigger part of the window (say 75%) so the user can see the buttons more clearly, and the gridlayout with the 2 standard widgets should occupy the remaining part. However, the gridlayout ends up taking the bigger part of the window for itself.
Here's the piece of code (assume that the dynamically added buttons right now are 15):
sm = ScreenManager()
class scr1(Screen):
pass
#layout that occupies the entire window. Everything else will be added on this
glayout = GridLayout(cols=1)
#layout that should occupy 25% of the window
layout1 = GridLayout(cols=1,size_hint_y=0.25)
#layout to be added on the scrollview
layout2 = GridLayout(cols=1,size_hint_y=None)
layout2.bind(minimum_height=layout2.setter('height'))
#screen to hold the glayout
screen1 = scr1(name = '1')
#adding a couple of widgets to layout1
layout1.add_widget(Button(text = 'hey'))
layout1.add_widget(TextInput(text = 'hey'))
#scroller that should occupy 75% of the window
scroller = ScrollView(size_hint = (1,None))
scroller.add_widget(layout2)
#adding buttons to be scrolled
for i in range(15):
layout2.add_widget(Button(text=str(i),size_hint_y=None))
#adding scroller and layout1 to glayout and then the glayout to the screen
glayout.add_widget(scroller)
glayout.add_widget(layout1)
screen1.add_widget(glayout)
#adding the screen to the screen manager
sm.add_widget(screen1)
I am not very familiar with the positioning system that kivy uses, and I am not sure how I should go about solving this problem. Here's what the program looks like when I run it.
You set size_hint_y of ScrollView to None.
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
sm = ScreenManager()
class scr1(Screen):
pass
#layout that occupies the entire window. Everything else will be added on this
glayout = GridLayout(cols=1)
#layout that should occupy 25% of the window
layout1 = GridLayout(cols=1,size_hint_y=0.25)
#layout to be added on the scrollview
layout2 = GridLayout(cols=1,size_hint_y=None)
layout2.bind(minimum_height=layout2.setter('height'))
#screen to hold the glayout
screen1 = scr1(name = '1')
#adding a couple of widgets to layout1
layout1.add_widget(Button(text = 'hey'))
layout1.add_widget(TextInput(text = 'hey'))
#scroller that should occupy 75% of the window
scroller = ScrollView(size_hint_y=.75)
scroller.add_widget(layout2)
#adding buttons to be scrolled
for i in range(15):
layout2.add_widget(Button(text=str(i),height=70,size_hint_y=None))
#adding scroller and layout1 to glayout and then the glayout to the screen
glayout.add_widget(scroller)
glayout.add_widget(layout1)
screen1.add_widget(glayout)
#adding the screen to the screen manager
sm.add_widget(screen1)
from kivy.app import App
class Ap(App):
def build(self):
return sm
Ap().run()

Anchor a label to the corner?

In Kivy:
from kivy.app import App
from kivy.uix.label import Label
class TestApp(App):
def build(self):
label = Label(text="TEST")
return label
TestApp().run()
My label is centred in the window:
How can I instead anchor my label to the bottom right corner of the window?
You'd think
label.halign = 'right'
label.valign = 'bottom'
would do the trick, but as the Label documentation points out,
The valign property will have no effect and halign will only have an effect if your text has newlines; a single line of text will appear to be centered even though halign is set to left (by default).
It looks like adding the label to an AnchorLayout, then shrinking the size of the label relative to its parent widget, together achieves what I want.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.anchorlayout import AnchorLayout
class TestApp(App):
def build(self):
anchor_layout = AnchorLayout(anchor_x='right', anchor_y='bottom')
label = Label(text="TEST")
label.size_hint = (0.1, 0.1)
anchor_layout.add_widget(label)
return anchor_layout
TestApp().run()
Produces:
Set the Label's text_size to its size, e.g. in kv text_size: self.size. The text_size controls the bounding box within which text is wrapped.

Categories