KIVY Custom Widget pos_hint - kivy

While using Custom widget only 'x' and 'y' parameters are evaluated in pos_hint parameter. But if I use other keys like center_x, center_y or top the key values are not evaluated. In sample program given below, if I use pos_hint as center_x and center_y the line is not aligned in middle of the layout.
But if i Use x and y parameter alignment works.
from kivy.app import App
from kivy.graphics import Line
from kivy.uix.scatter import Scatter
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.scatterlayout import ScatterLayout
class MyPaintWidget(Scatter):
def __init__(self, **kwargs) :
super(MyPaintWidget, self).__init__(**kwargs)
def create_figure(self, **kwargs):
self.canvas.add(Line( points=[0, 10, 30, 10]))
return self
class MyPaintApp(App):
def build(self):
parent = RelativeLayout()
#self.painter = MyPaintWidget(pos_hint={'center_x': 0.5, 'center_y':0.5}) Not working with center_x and center_y alignment
self.painter = MyPaintWidget(pos_hint={'x': 0.5, 'y':0.5}) #working with x and y parameter
parent.add_widget(self.painter.create_figure())
return parent
if __name__ == '__main__':
MyPaintApp().run()
Even tried with sample KV file as mentioned in comments section
from kivy.app import App
from kivy.graphics import Line
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.lang import Builder
KV = '''
<LineRectangle>:
canvas:
Color:
rgba: .1, .1, 1, .9
Line:
width: 2.
rectangle: (self.x, self.y, self.width, self.height)
Label:
center: root.center
text: 'Rectangle'
'''
class LineRectangle(Scatter):
pass
class MyPaintApp(App):
def build(self):
Builder.load_string(KV)
root = RelativeLayout()
#root.add_widget(LineRectangle(pos_hint={'center_x':0.5, 'center_y':0.5}, size_hint=(0.2, 0.2)))
root.add_widget(LineRectangle(pos_hint={'center_x':0.5, 'center_y':0.5}, size_hint=(None, None)))
return root
if __name__ == '__main__':
MyPaintApp().run()

pos_hint property allows you to set the position of the widget inside its parent layout, in percent (similar to size_hint).

Related

KIVY DragBehavior Custom Widget

I am trying to use DragBehavior to help in moving my custom widget across RelativeLayout. Find below sample code. Why my widget is not moving on Drag action please. For simplicity I had included only rectangle in my custom widget MyPaintWidget
from kivy.app import App
from kivy.graphics import Line
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
from kivy.graphics import Color, Rectangle
Builder.load_string("""
<MyPaintWidget>:
# Define the properties for the DragLabel
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
""")
class MyPaintWidget(DragBehavior, Scatter):
def __init__(self, **kwargs) :
self.selected = None
self.touched = False
super(MyPaintWidget, self).__init__(**kwargs)
def create_figure(self, **kwargs):
print ('position is {}'.format(self.pos))
print ('width Height {}'.format(self.to_parent(self.width, self.height)))
self.canvas.add(Rectangle(pos = self.pos, size = self.size))
return self
def on_touch_move(self, touch):
print('Started to move x: {} y: {}'.format(touch.x, touch.y))
return super(MyPaintWidget, self).on_touch_move(touch)
class MyPaintApp(App):
def build(self):
parent = RelativeLayout()
self.painter = MyPaintWidget(pos_hint={"center_x": 0.5, 'center_y':0.5}, size_hint=(.2,.1))
parent.add_widget(self.painter.create_figure())
return parent
if __name__ == '__main__':
MyPaintApp().run()
The DragBehavior works by adjusting the pos of your MyPaintWidget, but you have set pos_hint on the MyPaintWidget. The pos_hint takes precedence over pos, so while the drag changes pos, it is ignored because there is a pos_hint. Also, the Rectangle that you draw in create_figure has its size and pos set when that method is called, and there is no mechanism to change it when the MyPaintWidget is moved. So, even if the Widget was being dragged, the Rectangle would not move.
Here is a version of your code with those problems corrected:
from kivy.app import App
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
Builder.load_string("""
<MyPaintWidget>:
# Define the properties for the DragLabel
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
canvas:
Color:
rgba: 1,0,0,1
Rectangle:
pos: 0,0 # only do this for RelativeLayout
size: self.size
""")
class MyPaintWidget(DragBehavior, Scatter):
def __init__(self, **kwargs) :
self.selected = None
self.touched = False
super(MyPaintWidget, self).__init__(**kwargs)
def on_touch_move(self, touch):
print('Started to move x: {} y: {}'.format(touch.x, touch.y))
return super(MyPaintWidget, self).on_touch_move(touch)
class MyPaintApp(App):
def build(self):
parent = RelativeLayout()
self.painter = MyPaintWidget( pos=(240, 200), size_hint=(.2,.1))
parent.add_widget(self.painter)
return parent
if __name__ == '__main__':
MyPaintApp().run()

KIVY : Scroll Bar missing

I am trying my hands on ScrollView within anchorlayout. Unable to get scroll bar in Relativelayout. Please find below my sample code. Why scrollbar is not visible.
import kivy
from kivy.app import App
from kivy.uix.scrollview import ScrollView
from kivy.lang import Builder
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.button import Button
from kivy.factory import Factory
from kivy.core.window import Window
Builder.load_string('''
<ScrollView>:
size_hint: 1,1
DrawingSpace:
size_hint: 1,1
''')
class Main(App):
def build(self):
self.root=DrawingSpace()
return self.root
class DrawingSpace(RelativeLayout):
def __init__(self, **kwargs):
super(RelativeLayout, self).__init__(**kwargs)
self.add_widget( Button(text = 'Scroll', size_hint=(None,None), size=(50,200), pos = (100, 100)))
self.add_widget( Button(text = 'Scroll', size_hint=(None,None), size=(50,200), pos = (250, 300)))
Factory.register('DrawingSpace', cls=DrawingSpace)
if __name__ in ('__main__'):
Window.size = (200, 200)
app = Main()
app.run()
Thanks
For ScrollView, in the direction you want scrolling, you have to set size_hint to None and specify the size. I used 500 for testing.
import kivy
from kivy.app import App
from kivy.uix.scrollview import ScrollView
from kivy.lang import Builder
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.button import Button
from kivy.factory import Factory
from kivy.core.window import Window
Builder.load_string('''
<ScrollView>:
size_hint: 1,1
DrawingSpace:
size_hint: 1,None
height: 500
''')
class Main(App):
def build(self):
self.root=ScrollView()
return self.root
class DrawingSpace(RelativeLayout):
def __init__(self, **kwargs):
super(RelativeLayout, self).__init__(**kwargs)
self.add_widget( Button(text = 'Scroll', size_hint=(None,None), size=(50,200), pos = (100, 100)))
self.add_widget( Button(text = 'Scroll', size_hint=(None,None), size=(50,200), pos = (250, 300)))
Factory.register('DrawingSpace', cls=DrawingSpace)
if __name__ in ('__main__'):
Window.size = (200, 200)
app = Main()
app.run()

Removed widget remains on parent

I have parent window that is Widget where places another widgets (parent.add_widget(...)) by right mouse click. Also children widget can be removed by middle click (parent.remove_widget(...)). And here I faced strange issue - child actually removed from children collection of parent but remains visible (but inactive. i.e. all interactions with removed widget image are impossible). I do not using any layouts because children are movable to any point and thus should not be aligned somehow.
Question: how to redraw parent after remove_widget?
Example code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.config import Config
from kivy.lang import Builder
from kivy.uix.behaviors import DragBehavior
from kivy.uix.relativelayout import RelativeLayout
Config.set('input', 'mouse', 'mouse,disable_multitouch')
Builder.load_string(f'''
<Node#RelativeLayout>:
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 10
canvas.before:
Color:
rgba: self.bcolor
RoundedRectangle:
pos: 0, 0
size: self.size
radius: [10,]
''')
class Node(DragBehavior, RelativeLayout):
def __init__(self, **kwargs):
self.bcolor = (0, 0, 1, 1)
super(Node, self).__init__(size=(100, 100), **kwargs)
def placement_pos(self, x, y):
return (x - self.node_width() / 2, y - self.node_height() / 2)
Builder.load_string(f'''
<GrapherUI#Widget>:
canvas.before:
Color:
rgba: {.5, 1, .5, 1}
Rectangle:
pos: self.pos
size: self.size
''')
class GrapherUI(Widget):
def __init__(self, graph=None, **kwargs):
super(GrapherUI, self).__init__(**kwargs)
def on_touch_down(self, touch):
touched_children = [x for x in self.walk(restrict=True) if x is not self and x.collide_point(*touch.pos)]
has_touched_children = any(touched_children)
if touch.button == 'right':
if not has_touched_children:
self.add_node(touch)
elif touch.button == 'middle':
self.remove_node(touched_children, touch)
super(GrapherUI, self).on_touch_down(touch)
def add_node(self, touch):
print('add node')
with self.canvas:
node = Node(pos=(touch.x, touch.y))
self.add_widget(node)
def remove_node(self, touched_children, touch):
for node in touched_children:
print('remove node')
self.remove_widget(node)
class Main(App):
def build(self):
return GrapherUI()
if __name__ == '__main__':
Main().run()
Well, as usual with Kivy nobody except yourself could help (even documentation). Here issue caused by with self.canvas: line under add_node method. If it removed widget disappears after removing.

Progress bar in kivy can't update using loop

im traying to do a progress bar in kivy, i can build the app with it but it can't be udpated when i run a function (loop) how can i do this please ?
here is my code :
import libraries :
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.properties import ObjectProperty
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.logger import Logger
from kivy.uix.progressbar import ProgressBar
My builder :
Builder.load_string("""
<RootWidget>:
pb : pb
some code here
ActionView:
ActionPrevious:
with_previous: False
ActionButton:
text: 'Run analysis'
color: 29/255, 185/255, 84/255, 1
on_release: root.clicked()
BoxLayout:
ProgressBar:
id : pb
min :0
max :1
pos_hint: {'x': .1}
size_hint_x :.8
size_hint_y :.5
value: 0
""")
other code with my update_bar function:
class Myapp(BoxLayout):
pb = ProgressBar()
box = ObjectProperty()
def __init__(self, *args, **kwargs):
super(Myapp, self).__init__(*args, **kwargs)
def update_bar(self,dt=None):
self.ids.pb.value = self.ids.pb.value
here is the problem : the progress bar is not updated each iteration, but it take the last value (for i = 9999)
def clicked(self):
for i in range(10000) :
self.ids.pb.value = i/10000
Clock.schedule_interval(self.update_bar,0.5)
self.update_bar()
Build my app :
class EventScreen(App,Myapp):
def on_stop(self):
Logger.critical("Good bye")
def build(self):
return Myapp()
if __name__ == "__main__":
EventScreen().run()
thank you in advance
Use Clock.create_trigger() to update progress bar.
Snippet
def __init__(self, *args, **kwargs):
super(Myapp, self).__init__(*args, **kwargs)
self.update_bar_trigger = Clock.create_trigger(self.update_bar)
def clicked(self):
self.i = 0
self.update_bar_trigger()
def update_bar(self, dt):
if self.i <= 10000:
self.ids.pb.value += self.i
self.i += 1
self.update_bar_trigger()

Kivy Image Widget On Scatter Layout: How Do I Increase Image Size?

I want to use Kivy to rotate an image of a 45RPM record. I discovered that this can be done using an image widget on a scatter layout. However I can't seem to find the code to make the image of the 45RPM record be resized to match the size of the window. I've tried for hours different iterations of code and I've become quite frustrated. Full code below with link to image.
Any Suggestions?
Appreciated In Advance.
....brad....
Image for code at: https://drive.google.com/open?id=0B-T2cvsAoZ2vQ2hmaHM0SnlQVlU
# Modified from https://gist.github.com/tshirtman/6222891
from kivy.app import App
from kivy.properties import NumericProperty
from kivy.lang import Builder
from kivy.clock import Clock
kv = """
BoxLayout:
Widget:
Scatter:
center: self.parent.center
do_rotation: False
do_translation: False
do_scale: False
rotation: app.angle
Image:
source: '45rpm.png'
"""
class RotateRecordApp(App):
angle = NumericProperty(0)
def build(self):
Clock.schedule_interval(self.update_angle, 0)
return Builder.load_string(kv)
def update_angle(self, dt, *args):
self.angle += dt * 100
if __name__ == '__main__':
RotateRecordApp().run()
Use scale property:
# Modified from https://gist.github.com/tshirtman/6222891
from kivy.app import App
from kivy.properties import NumericProperty
from kivy.lang import Builder
from kivy.clock import Clock
kv = """
BoxLayout:
Widget:
# Gray background
canvas.before:
Color:
rgba: 0.1, 0.1, 0.1, 1
Rectangle:
pos: self.pos
size: self.size
Scatter:
center: self.parent.center
do_rotation: False
do_translation: False
do_scale: False
rotation: app.angle
scale: min(self.parent.width/self.width,\
self.parent.height/self.height)
Image:
source: '45rpm.png'
"""
class RotateRecordApp(App):
angle = NumericProperty(0)
def build(self):
Clock.schedule_interval(self.update_angle, 0)
return Builder.load_string(kv)
def update_angle(self, dt, *args):
self.angle += dt * 100
if __name__ == '__main__':
RotateRecordApp().run()
Output:
I added a gray background just to improve image visibility.

Resources