Kivy Bubble arrow blur - python

I created a Bubble widget around a custom widget to display some options, but the arrow of that Bubble turned out unexpectedly big and blurry. All those widgets are positioned in a GridLayout that is put inside of a ScrollView. What could cause this issue?
Here is the code:
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
from kivy.uix.bubble import Bubble
from kivy.properties import ListProperty
Builder.load_string('''
<PopupBubble>:
size_hint: None, None
<ScrollView>:
canvas:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
<Message>:
width: 500
BoxLayout:
pos: root.pos
height: self.height
canvas:
Color:
rgba: 0.8, 0.8, 0.8, 1
RoundedRectangle:
pos: root.pos
size: self.size
TextInput:
pos: root.pos
size: root.size
id: msg
background_color: 0, 0, 0, 0
cursor_color: 0, 0, 0, 0
PopupBubble:
pos: root.x, self.height + root.y + root.height
size: 100, 40
''')
class Message(Widget):
bg_color = ListProperty([0.99, 0.99, 0.99, 1])
class PopupBubble(Bubble):
pass
class Chat(Screen):
pass
class TestApp(App):
def msg_in(self):
msg_stack = self.msg_stack
msg = "test"
msg_stack.append(Message())
msg_stack[-1].ids['msg'].text = msg
msg_stack[-1].width = 160
msg_stack[-1].height = (len(msg_stack[-1].ids['msg']._lines_labels) + 1) * (msg_stack[-1].ids['msg'].line_height + 5)
for i in msg_stack[-1].walk():
i.height = msg_stack[-1].height
i.width = msg_stack[-1].width
self.msg_layout.add_widget(msg_stack[-1])
def build(self):
self.msg_stack = []
self.chat = Chat()
self.sv1_main = ScrollView(pos_hint = {"top":1, "center_x":0.5},
size_hint = (0.97, 0.65))
self.msg_layout = GridLayout(height = 10,
cols = 1,
padding = 10,
spacing = 10,
size_hint_y = None)
self.chat.add_widget(self.sv1_main)
self.sv1_main.add_widget(self.msg_layout)
for i in range(15):
self.msg_layout.add_widget(Widget())
# Just to bring the next widget down
self.msg_in()
return self.chat
TestApp().run()

Your line msg_stack[-1].width = 160 cripples it. Or rather the loop after it, where you set Bubble's width from that variable.
You set Bubble's size in kv, then in python you play its width again as you add Message as a widget to GridLayout and the result is clearly visible. Set its size once, in kv or in python and/or try to set one property only. Otherwise it works as expected:
from kivy.lang import Builder
from kivy.base import runTouchApp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.bubble import Bubble, BubbleButton
Builder.load_string('''
<Test>:
Button:
on_release: root.show_bubble()
FloatLayout:
id: box
''')
class Test(BoxLayout):
def show_bubble(self):
bubble = Bubble(size_hint=[None,None],size=[100,40],pos=[100,300])
bubble.add_widget(BubbleButton(text='Copy', size=[30,10]))
bubble.add_widget(BubbleButton(text='Paste'))
self.ids.box.add_widget(bubble)
runTouchApp(Test())

Related

Get canvas size and draw rectangle in Kivy

I would like to draw something in canvas from .py by using class 'BotRightCanvas'. The initial widgets are all defined in .kv
Problem is when I finally declare BotRightCanvas in .kv, the size of the canvas widget is not coming proper (still 100x100). I would like the green rectangle to coincide with the cyan one.
Here's .py
from kivy.graphics import Color
from kivy.graphics.vertex_instructions import Line, Rectangle, Ellipse
from kivy.metrics import dp
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
class TestOut(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def getSize(self, id):
cSize = id.size
print('win ' + str(Window.size))
print('canvas ' + str(id.size))
print('cSize ' + str(cSize))
class BotRightCanvas(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
with self.canvas:
Color(0, 1, 0, 1)
Line(circle=(self.width / 2, self.height / 2, 25), width=2)
Line(rectangle=(0, 0, self.width, self.height), width=5)
class PlaygroundApp(App):
title = "blabla"
def build(self):
return TestOut()
if __name__ == "__main__":
PlaygroundApp().run()
and .kv
<TestOut>:
BoxLayout:
orientation: 'vertical'
Button:
text:'A'
height: dp(100)
size_hint: 1, None
BoxLayout:
Button:
text:'B'
width: dp(150)
size_hint: None, 1
RelativeLayout:
id: rel_lo
on_size: root.getSize(rel_lo)
Widget:
canvas:
Line:
rectangle: (0,0, self.width, self.height)
width: 2
canvas.before:
Color:
rgba: 0,1,1,1
BotRightCanvas:
I tried printing the final canvas shape in console, seems it initializes with size of 0x0 but later on gets correct size 650x500.
The console output
canvas [0.0, 0]
cSize [0.0, 0]
win (800, 600)
canvas [650.0, 500.0]
cSize [650.0, 500.0]
Any help? Thanks !!
EDIT: Would like to control canvas items from .py
from kivy.graphics import Color
from kivy.graphics.vertex_instructions import Line, Rectangle, Ellipse
from kivy.metrics import dp
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
from kivy.lang.builder import Builder
from kivy.app import App
class TestOut(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def getSize(self, id):
cSize = id.size
print('win ' + str(Window.size))
print('canvas ' + str(id.size))
print('cSize ' + str(cSize))
class BotRightCanvas(Widget):
pass
Builder.load_string(
'''
<BotRightCanvas>:
canvas:
Color:
rgba:0, 1, 0, 1
Line:
circle:self.width / 2, self.height / 2, 25
width:2
Line:
rectangle:0, 0, self.width, self.height
width:5
<TestOut>:
BoxLayout:
orientation: 'vertical'
Button:
text:'A'
height: dp(100)
size_hint: 1, None
BoxLayout:
Button:
text:'B'
width: dp(150)
size_hint: None, 1
RelativeLayout:
id: rel_lo
on_size: root.getSize(rel_lo)
Widget:
canvas:
Line:
rectangle: (0,0, self.width, self.height)
width: 2
canvas.before:
Color:
rgba: 0,1,1,1
BotRightCanvas:
size:rel_lo.size
'''
)
class PlaygroundApp(App):
title = "blabla"
def build(self):
return TestOut()
for line in open('2.py').readlines():
print(' '+line)
if __name__ == "__main__":
PlaygroundApp().run()

How can i prevent my label from stretching when i resize the window in kivy?

In this program when i want to resize the window i do not want to my label to resize or stretch and i want it to have fixed width How can i do that?
(I set the size_hint: (.1, None) but i do not know why it stretches with changing window size)
code:
import kivy
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.textinput import TextInput
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.graphics import Color, Rectangle
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.relativelayout import RelativeLayout
Builder.load_string("""
<relative>
RelativeLayout:
size: root.width,root.height
Label:
pos_hint:{'x':0,'y':.01}
size_hint: (.1, None)
height: 22
text:'1'
background_color: 6/255, 61/255, 81/255, 1
canvas.before:
Color:
rgba:self.background_color
Rectangle:
size: self.size
pos: self.pos
TextInput:
cursor_color: 255/255, 143/255, 5/255, 0.8
pos_hint:{'x':.1,'y':0}
multiline:False
height: 33
size_hint: (5, None)
background_color: 0,0,0,0
foreground_color: 255/255, 167/255, 167/255, 0.51
""")
class relative(Widget):
pass
class foo(App):
def build(self):
Window.clearcolor='#1618388'
return relative()
if __name__ == '__main__':
foo().run()
before changing the window size:
after changing the window size:
The Label changes size because size_hint: (.1, None) tells kivy that the Label width should be 0.1 times the RelativeLayout. If you want the size to be constant, do not use size_hint. Try this:
Label:
pos_hint:{'x':0,'y':.01}
size_hint: (None, None)
width: 100
height: 22
text:'1'
background_color: 6/255, 61/255, 81/255, 1
canvas.before:
Color:
rgba:self.background_color
Rectangle:
size: self.size
pos: self.pos

How to get a Background color on a Kivy Label?

I am using Kivy to make an app, and I'm trying to display a label. Here's my main code
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.popup import Popup
class storageApp(App):
def build(self):
return kvfile
class LoginPage(Screen):
pass
class WindowManager(ScreenManager):
pass
kvfile = Builder.load_file('storage.kv')
storageApp().run()
Here's my .kv file
WindowManager:
current: 'login'
LoginPage:
<LoginPage>:
name: 'login'
FloatLayout:
Label:
text: 'Welcome to the secret app!'
size_hint: 0.5, 0.1
pos_hint: {'center_x': 0.5, 'top': 0.95}
background_color: 1, 0, 1, 1
And I get the following output on my screen
The color of the label is still black. However, when Ichange the Label to Button it seems to work.
This seems very odd. Any idea how to fix it?
The Label doesn't have a background colour property. If you want a background, draw one:
<KvBackgroundRule>:
canvas.before:
Color:
rgba: 1, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size

Kivy GUI - fill color in circle like water fill in circle container level wise for two or more tank

from kivy.app import App
from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.uix.anchorlayout import AnchorLayout
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
Builder.load_string("""
<WaterFill>:
id: c_canvas
level: 0.0
width: self.height
size_hint: None, 1
color: 0, 0, 1
canvas:
StencilPush
Ellipse:
group: 'a'
size: (200, 200)
pos: 150,270
StencilUse
Color:
rgb: root.color
Rectangle:
group: 'a'
pos: root.pos
size: (root.width, root.level*root.height)
StencilUnUse
StencilPop
Ellipse:
group: 'b'
size: (200, 200)
pos: 450,270
Button:
text: 'Try me!'
on_release: root.press_me()
""")
class WaterFill(Widget):
def __init__(self, *args, **kwargs):
Widget.__init__(self, *args, **kwargs)
self.delta = 0.01
Clock.schedule_interval(self.on_timeout, 0.05)
def on_timeout(self, *args):
#self.root.ids.c_canvas.canvas.get_group('a').level += self.delta #i tried by id and by group but unabel to find solution
self.level += self.delta
if self.level >= 1:
#self.delta = -0.01
self.delta = -1.1
elif self.level <= 0:
self.delta = 0.01
def press_me(self):
print("pressed") # on press need to fill circle
class TestApp(App):
def build(self):
#lay = AnchorLayout(anchor_x='center', anchor_y='center')
#lay.add_widget(WaterFill())
return WaterFill()
if __name__ == "__main__":
TestApp().run()
how i change 2 or more circle fill layout with Kivy GUI - fill color in circle like water fill in circle container level wise same logic. i tried by defining id but fails any way to do this??? please help how to use canvas design for 2 or more circle !!!
If you want to handle it, each one should easily consider the drawing independently, and that is what I proposed in my previous answer.
In the following example we use a layout to locate it and use the ids to access the values.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
root = Builder.load_string("""
<WaterFill#Widget>:
level: 0
color: 0, 0, 1
canvas:
StencilPush
Ellipse:
pos: root.pos
size: root.size
StencilUse
Color:
rgb: root.color
Rectangle:
pos: root.pos
size: (root.width, root.level*root.height)
StencilUnUse
StencilPop
<FloatLayout>:
WaterFill:
id: left
level: 0.4
pos_hint: {'x' : 0.10, 'y': 0.4}
size_hint: 0.4, 0.4
WaterFill:
id: right
pos_hint: {'x' : 0.55, 'y': 0.4}
size_hint: 0.4, 0.4
Button:
id: button
text: 'text'
size_hint: .25, .25
on_press: root.press_me()
""")
class Principal(FloatLayout):
def press_me(self):
current_level = self.ids["right"].level
self.ids["right"].level = 0.0 if current_level >= 1.0 else current_level + 1.0/9
class TestApp(App):
def build(self):
return Principal()
if __name__ == "__main__":
TestApp().run()
Note:
I recommend reading about the kivy language, how the design of the .kv is linked with the .py

Kivy widget out of expected boundaries

I'm making custom touch handling for my widget. The way I have it set up is that the initial size of the widget is set and fixed, however, later, I change it dynamically depending on the contents. I turned that feature down for now to shorten the code, it doesn't affect the problem. I've noticed something strange when I clicked at a blank spot to the right of the widget and the touch was registered, so I added a canvas background to inspect. Apparently, the actual widget boundaries are set much bigger than what the actual widget instance is, and I don't really know what's causing this. Those widgets are positioned in a FloatLayout, but I never set size_hints, only sizes. Could that be the case? How do I shorten the widget boundaries to what they're supposed to be?
Here is the entire source code, the problem is most likely in the kv definition:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager, NoTransition
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
global pr_msg_y
pr_msg_y = 5
Builder.load_string('''
<ScrollView>:
canvas:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
<Message>:
width: 500
canvas:
Color:
rgba: 1, 0, 0, 0.3
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
pos: root.pos
height: self.height
canvas:
Color:
rgba: 0.6, 0.6, 0.6, 1
Rectangle:
pos: root.pos
size: self.size
TextInput:
pos: root.pos
size: root.size
id: msg
background_color: 0, 0, 0, 0
text: str(msg)
''')
class Message(Widget):
def __init__(self, **kwargs):
super(Message, self).__init__(**kwargs)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
print("Touch within {}".format(self))
class Chat(Screen):
pass
class MessageInput(TextInput):
def __init__(self, **kwargs):
super(MessageInput, self).__init__(**kwargs)
class ChatApp(App):
def build(self):
def msg_in(btn):
global pr_msg_y
inst = Message()
inst.ids['msg'].text = "test" * 15
inst.width = 456 # something random, the sizing should be dynamic
inst.height = 40
for i in inst.walk():
i.height = inst.height
i.width = inst.width
inst.y = sv1_main.height - 5 - pr_msg_y - inst.height
msg_float.add_widget(inst)
pr_msg_y += inst.height + 5
Screens = ScreenManager(transition = NoTransition())
chat = Chat(name = "main")
sv1_main = ScrollView(pos_hint = {"top":0.87, "center_x":0.5},
size_hint = (0.97, 0.65))
msg_float = FloatLayout()
bt1_main = Button(pos_hint = {"top":0.097, "center_x":0.951},
on_press = msg_in)
Screens.add_widget(chat)
chat.add_widget(sv1_main)
sv1_main.add_widget(msg_float)
chat.add_widget(bt1_main)
return Screens
ChatApp().run()
size_hint does "overwrite" size. By default size_hint is set to 1,1, so if you only set size and you put the widget into a layout that recognizes size_hints, the widget will get the maximal available size.
Set size_hint = None, None to fix.

Categories

Resources