I would like to create a scrollable Floatlayout with dynamically created buttons (kind of paint where I can scroll the drawing board). Unfortunately, the code that I come out doesn't work and the buttons don't move while scrolling the FloatLayout. How can I attach the button to the FloatLayout?
import kivy
kivy.require('1.0.7')
from kivy.app import App
from kivy.uix.scrollview import ScrollView
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
Builder.load_string('''
<Scroller>
view: glayout
FloatLayout:
id: glayout
width: 2000
height: 2000
size_hint: (None, None)
''')
class Main(App):
def build(self):
self.root=Scroller()
return self.root
class Scroller(ScrollView):
def __init__(self, **kwargs):
super(Scroller, self).__init__(**kwargs)
a = Button(size_hint=(None,None), width=200, height=200)
self.ids.glayout.add_widget(a)
a.bind(pos=self.ids.glayout.pos)
if __name__ in ('__main__'):
app = Main()
app.run()
The FloatLayout handle the size, not the position. The most simple solution is to replace your FloatLayout with a RelativeLayout, which handle size, and position is relative to the RelativeLayout origin.
Related
I created a stack layout with 100 buttons in it. This alone works but when I add the stack layout under scroll view layout nothing is displayed on the screen.without using scroll view
when scroll view is used
Python code
from kivy.app import App
from kivy.metrics import dp
from kivy.uix.stacklayout import StackLayout
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
class stacklayoutex(StackLayout):
def __init__(self,**kwargs):
super().__init__(**kwargs)
for i in range(100):
b=Button(text=str(i+1),size_hint=(None,None),size=(dp(100),dp(100)))
self.add_widget(b)
class MainWidget(Widget):
pass
class TheLabApp(App):
pass
TheLabApp().run()
KV file
scrollviewex:
<scrollviewex#ScrollView>:
stacklayoutex:
size_hint: 1,None
height:4000
<stacklayoutex>:
A couple problems:
Your class names must begin with upper case. Not following this rule can cause syntax errors in your kv.
Also, indentation error in your kv, it should be:
Scrollviewex:
<Scrollviewex#ScrollView>:
Stacklayoutex:
size_hint: 1,None
height:4000
<Stacklayoutex>:
I have a custom button. I cannot put it on screen via kv file. I studied a lot topics. No useful info can find. Here is a simple example:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string('''
#:kivy 1.11.0
<MyGrid>:
Button
text: 'hello'
''')
class MyGrid(BoxLayout):
pass
class DropApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
DropApp().run()
Note that the Button in this kv file is native Kivy button. I run this code, I can see this button on screen. But now I have a custom Button:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string('''
#:kivy 1.11.0
<MyGrid>:
customButton:
''')
class MyGrid(BoxLayout):
pass
class customButton(Button):
def __init__(self, **kwargs):
self.text = 'hi'
super(Button, self).__init__(**kwargs)
class DropApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
DropApp().run()
I run this code, I cannot see this customButton on screen. Note that the custom widget is complex. I have to define it in py file. For example:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
class DropApp(App):
def build(self):
layout = GridLayout(cols=1, spacing=10)
for i in range(100):
btn = Button(text=str(i), size_hint_y=None, height=40)
layout.add_widget(btn)
return layout
if __name__ == '__main__':
DropApp().run()
Building this layout need for loop. I cannot find a way to build it in kv file. So I define it in Py file. But if I define it in py file, I cannot work with it in kv file.
Question 1: What's wrong with the second code?
Question 2: If I can't make it work, can I achieve the third code in kvlang(in kv file not in python)?
I am new to Kivy, hope someone can help.
When I run the code on Kivy v1.11.1 , Python v3.7.5:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
class DropApp(App):
def build(self):
layout = GridLayout(cols=1, spacing=10)
for i in range(100):
btn = Button(text=str(i), size_hint_y=None, height=40)
layout.add_widget(btn)
return layout
if __name__ == '__main__':
DropApp().run()
The output is:
If this is the output you require, try running:
pip install --upgrade kivy
For the second code, python is case-sensitive. So instead of customButton, use CustomButton.
If you want to do the 3rd code using .kv file, here is an example:
from kivy.app import App
from kivy.uix.recycleview import RecycleView
from kivy.lang import Builder
Builder.load_string('''
<ExampleRV>:
viewclass: 'Button'
RecycleBoxLayout:
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
''')
class ExampleRV(RecycleView):
def __init__(self, **kwargs):
super(ExampleRV, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in range(20)]
class RecycleApp(App):
def build(self):
return ExampleRV()
RecycleApp().run()
For reference, go HERE.
I want to have opportunity to change or delete rectangle in builder, but I get AttributeError.
How can I fix it? I hope you help me
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.graphics import Color, Rectangle
Builder.load_string('''
<RootWidget>
FloatLayout:
id: layout
canvas.before:
Rectangle:
id: image
size: (100, 180)
pos: (223, 191)
source:'image.gif'
''')
class RootWidget(FloatLayout):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
self.ids.layout.remove_widget(self.ids.image)
class WindowApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
WindowApp().run()
Unfortunately, you can't set the id of a canvas object. Here is a workaround: Consider you want to change the source attribute of your Rectangle. Set rectangle_image: "image.gif" in your FloatLayout, then set source: self.rectangle_image in your Rectangle. Now you can reference the FloatLayout by its id and change its rectangle_image variable, and it will automatically update the Rectangle's source attribute!
Using this code:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
class Main(App):
def build(self):
root = FloatLayout(size=(100, 100))
root.add_widget(TextInput(pos=(0, 0)))
root.add_widget(TextInput(pos=(50, 50)))
return root
if __name__ == '__main__':
Main().run()
I get two TextInputs, one on top of the other. When I click on the top TextInput (by clicking somewhere in the middle of the screen), the focus goes to the lower TextInput for some reason. In fact, the only way I can get focus on the top TextInput is by clicking entirely outside of the lower TextInput (by clicking right at the top of the screen). Why does this happen, and how can I circumvent this?
Your problem can be approached in two ways.Float layout honors the pos_hint and the size_hint properties of its children.So you need to set size_hint for textinput.
ie-
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
class Main(App):
def build(self):
root = FloatLayout(size=(100, 100))
root.add_widget(TextInput(pos=(0, 0),size_hint=(0.5,0.5)))
root.add_widget(TextInput(pos=(100, 100),size_hint=(0.5,0.5)))
return root
if __name__ == '__main__':
Main().run()
or use boxlayout instead of floatlayout
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
class Main(App):
def build(self):
root = BoxLayout(size=(100, 100))
root.add_widget(TextInput(pos=(0, 0)))
root.add_widget(TextInput(pos=(50, 50)))
return root
if __name__ == '__main__':
Main().run()
I'm trying to draw a rectangle in the center of my widget:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle
class MyWidget(Widget):
def __init__(self):
super(MyWidget, self).__init__()
with self.canvas:
Rectangle(pos=(self.center_x, self.center_y)
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()
This is what I'm getting:
Doing the exact same thing using a .kv file works:
<MyWidget>:
canvas:
Rectangle:
pos: self.center_x, self.center_y
Please explain how to achieve what I'm trying to do, and why it does work using a .kv file, as opposed to Python code. Thank you
If you add the Widget from a kv file the the widget will be automatically be attached to the App’s root attribute and used as the base of the application widget tree. So in your situation from the kv file the size of the widget is automatically bind to the application windows size and becomes root widget. Taking that into consideration the self.center_x and self.center_y works. You can read this at https://kivy.org/docs/guide/lang.html under the line `MyApp -> my.kv.
When not using a kv file this will not happen and the default size of the widget will be (100,100). To properly place your rectangle use a layout so you can refer use the size_hint to properly place or re size any child widget. As stated in the documentation
FloatLayout honors the pos_hint and the size_hint properties of its children.
https://kivy.org/docs/api-kivy.uix.floatlayout.html#kivy.uix.floatlayout.FloatLayout
So create a Float layout in example, add a widget with pos_hint equals to center_x and center_y that will refer to the layout being a parent and then draw a rectangle to the widget.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import Rectangle
from kivy.core.window import Window
class myLayout(FloatLayout):
def __init__(self):
super(myLayout, self).__init__()
self.size = Window.size
self.myWidget = Widget(size=(100,100))
self.add_widget(self.myWidget)
with self.myWidget.canvas:
Rectangle(pos=(self.center_x, self.center_y))
class MyApp(App):
def build(self):
return myLayout()
if __name__ == '__main__':
MyApp().run()