The animation I currently have for opening and closing a button, is simply a sub button comes out from the main button and expands using size_hint. This animation simply enlarges the sublayout. I would like to 'unveil' the sublayout instead. What I mean by that, is to not mess with size_hint of the sub and instead of expanding itself, I want it to 'unveil' itself which makes for a much cleaner animation. Heres a link of a button animation that uses the same effect. https://codemyui.com/button-to-a-list-animation/.
In the linked animation, the bottom border goes down and reveals the list underneath, the same effect I want.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import *
from kivy.animation import Animation
from kivy.lang.builder import Builder
from kivy.uix.behaviors import *
from kivy.properties import *
Builder.load_string("""
<Root>
Button1:
size_hint: 0.2,0.1
pos_hint: {'top':0.9,'center_x':0.5}
canvas:
Color:
rgba: [0.4,0.4,0.4,1]
Rectangle
size: self.size
pos: self.pos
SubLayout:
pos_hint: {'top':1,'center_x':0.5}
canvas:
Color:
rgba: [0.7,0.7,0.7,1]
Rectangle
size: self.size
pos: self.pos
Color:
rgba: [1,1,1,1]
Ellipse:
pos: self.pos
size: self.size
""")
class Root(FloatLayout):
pass
class Button1(ToggleButtonBehavior, FloatLayout):
def __init__(self,**kwargs):
super(Button1,self).__init__(**kwargs)
def on_state(self, *args):
if self.state == 'down':
anim = Animation(pos_hint={'top':0},d=0.2)
anim += Animation(size_hint=(2,2),d=0.2) #: expanding sublayout, how do I unveil instead?
anim.start(self.children[0])
else:
anim = Animation(size_hint=(1,1),d=0.2)
anim += Animation(pos_hint={'top':1},d=0.2)
anim.start(self.children[0])
class SubLayout(FloatLayout):
pass
class TestApp(App):
def build(self):
return Root()
if __name__ == '__main__':
TestApp().run()
As you can see, SubLayout expands and you can see the ellipse inside expanding with the button. What I would like is for the borders to move away, revealing whats underneath them while leaving the ellipse's size the same, which is the 'unveiling' effect. Any ideas on how to do this would be appreciated. Maybe something to do with an Fbo?
Related
I am trying to make a trial canvas (having just yellow rectangle as bounding box) which should fit to the parent layout (in this case FloatLayout).
At the beginning, the canvas is created at [0,0] of size [100,100]. 2) If I resize by dragging the App window corners, the canvas resizes well. However, 3) & 4) if I maximize or minimize, the canvas fails to resize properly. So as weird canvas size is noticed if I drag too fast. Any help would be highly appreciated :)
Please note create_canvas method an example and it should be in the main.py.
main.py
from kivy.app import App
from kivy.graphics.vertex_instructions import Line
from kivy.metrics import dp
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics import InstructionGroup, Color
class TestOut(BoxLayout):
canvas_items = InstructionGroup()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.create_canvas(pos=[dp(150), dp(0)], size=[dp(650), dp(500)])
def on_size(self, *args):
self.can_obj = self.ids.can
# print(self.can_obj.pos, self.can_obj.size)
self.canvas_items.clear()
self.can_obj.canvas.clear()
self.create_canvas(self.can_obj.pos, self.can_obj.size)
def create_canvas(self, pos, size):
self.canvas_items.add(Color(1, 1, 0, 1))
self.canvas_items.add(Line(rectangle=(pos[0], pos[1], size[0], size[1]), width=5))
self.canvas.add(self.canvas_items)
class PlaygroundApp(App):
title = "blabla"
def build(self):
return TestOut()
if __name__ == "__main__":
PlaygroundApp().run()
playground.kv
<TestOut>:
orientation: 'vertical'
Button:
id: button1
text:'A'
height: dp(100)
size_hint: 1, None
pos_hint: {'top':1}
BoxLayout:
orientation: 'horizontal'
Button:
id: button2
text:'B'
width: dp(150)
size_hint: None, 1
FloatLayout:
id: can
The problem is that you want the rectangle size to follow the FloatLayout size, but your on_size() is triggered by changes in the size of TestOut. One easy fix is to just remove the canvas drawing from the python and put the rectangle in the kv, like this:
FloatLayout:
id: can
canvas:
Color:
rgba: 1, 1, 0, 1
Line:
width: 5
rectangle: self.x, self.y, self.width, self.height
A different fix (that leaves the canvas drawing in the python), is to trigger on the size of the FloatLayout. First, change the name of on_size() to something else, say do_on_size():
def do_on_size(self, *args): # changed name to eliminate dependency on size of TestOut
self.can_obj = self.ids.can
# print('on_size:', self.can_obj.pos, self.can_obj.size)
self.canvas_items.clear()
self.can_obj.canvas.clear()
self.create_canvas(self.can_obj.pos, self.can_obj.size)
Then add some code in the PlaygroundApp to set the binding to the size of the FloatLayout:
class PlaygroundApp(App):
title = "blabla"
def build(self):
Clock.schedule_once(self.set_binding)
return TestOut()
def set_binding(self, dt):
fl = self.root.ids.can
fl.bind(size=self.root.do_on_size)
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.
I'm trying to have a white background in window and I'm using Kivy modules and Kv language but the background still black.
main.py :
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
class MyUI(FloatLayout):
pass
class MyApp(App):
def build(self):
return MyUI()
def main():
MyApp().run()
if __name__ == '__main__':
main()
my.kv :
<MyUI>:
canvas:
Color:
rgba: 1,1,1,1
Color instructions are applied to shapes that you draw, e.g. if you write the following:
<MyUI>:
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
now you will see a white Rectangle filling the screen (as that's what its pos and size are set to do).
I add widgets to a GridLayout in a ScrollView, so its content expands dynamically.
By default, without user scrolling, the view stays at the top, no matter how many more widgets do you add. If the user scrolls, the view attaches to this point, but it seems a little annoying for me to have to scroll down (even a little bit) for the view to always show the latest content. How can I make it show the downmost part by default?
Here is the sample code just in case:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
Builder.load_string('''
<MessageView>:
canvas:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
<Message>:
canvas:
Color:
rgba: 0, 1, 0, 0.3
Rectangle:
pos: self.pos
size: self.size
''')
class Message(Widget):
pass
class MessageView(ScrollView):
pass
class TestApp(App):
def msg_in(self, btn):
msg = Message()
msg.size_hint = [None, None]
self.msg_layout.add_widget(msg)
def build(self):
self.scr = Screen()
self.sv1_main = MessageView(pos_hint={"top": 0.87, "center_x": 0.5},
size_hint=(0.97, 0.65))
self.msg_layout = GridLayout(cols=1,
size_hint_y=None)
self.msg_layout.bind(minimum_height=self.msg_layout.setter('height'))
self.bt1_main = Button(size_hint=(0.1, 0.078),
pos_hint={"top": 0.097, "center_x": 0.927},
on_press=self.msg_in)
self.scr.add_widget(self.sv1_main)
self.sv1_main.add_widget(self.msg_layout)
self.scr.add_widget(self.bt1_main)
return self.scr
TestApp().run()
You can use scroll_to method after adding a new content.
class TestApp(App):
def msg_in(self, btn):
msg = Message()
msg.size_hint = [None, None]
self.msg_layout.add_widget(msg)
self.sv1_main.scroll_to(msg)
# ...
You can call widget's canvas from kivy language using canvas[.before|.after] member like this.
<MyWidget>:
canvas:
Rectangle:
source: 'mylogo.png'
pos: self.pos
size: self.size
How can I clear the canvas before I put the instructions?
Use Clear:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
kv_string = '''
<MyWidget>:
canvas:
Color:
rgb: 0.1, 0.6, 0.3
Ellipse:
size: self.size
pos: self.pos
Clear
Color:
rgb: 0.6, 0.2, 0.1
Ellipse:
size: self.size
pos: self.center
'''
Builder.load_string(kv_string)
class MyWidget(Widget):
pass
class TestApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
TestApp().run()
In example above only one ellipse will be drawn since first one gets erased with Clear command. You can call it from Python using code like:
class SomeWidget(Widget):
def some_method(self):
self.canvas.clear()
with self.canvas:
# ...
i used
Window.clearcolor = (x,y,z,w)
and it works... so why use canvas?