I'm trying to have a button which can be clicked to add two buttons below the most recently added buttons. I'm doing this with a float height which starts at .9 and decrements from there. I'm getting the error TypeError: Cannot convert float to kivy.properties.Property.
class TasksWindow(FloatLayout):
height = .9
def __init__(self, **kwargs):
super(TasksWindow, self).__init__(**kwargs)
addBtn = Button(
text="+", pos_hint={'x': .9, 'top': 1}, size_hint=(.1, .1))
self.add_widget(addBtn)
addBtn.bind(on_press=self.clkAdd)
def clkAdd(self, obj):
height = obj.height
editBtn = Button(text="Tap to Edit", pos_hint={
'x': 0.0, 'top': height}, size_hint=(.9, .1))
delBtn = Button(text="X", pos_hint={
'x': .9, 'top': height}, size_hint=(.1, .1))
height -= .1
self.add_widget(editBtn)
self.add_widget(delBtn)
class TasksApp(App):
def build(self):
window = TasksWindow()
return window
if __name__ == '__main__':
TasksApp().run()
Use kivy.properties.NumericProperty. Ensure not to override a property name. FloatLayout has a height property.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import NumericProperty
class TasksWindow(FloatLayout):
btn_height = NumericProperty(0.9)
def __init__(self, **kwargs):
super(TasksWindow, self).__init__(**kwargs)
add_btn = Button(text="+", pos_hint={'x': .9, 'top': 1}, size_hint=(.1, .1))
self.add_widget(add_btn)
add_btn.bind(on_press=self.click_add)
def click_add(self, obj):
height = self.btn_height
edit_btn = Button(text="Tap to Edit", pos_hint={
'x': 0.0, 'top': height}, size_hint=(.9, .1))
del_btn = Button(text="X", pos_hint={
'x': .9, 'top': height}, size_hint=(.1, .1))
self.btn_height -= .1
self.add_widget(edit_btn)
self.add_widget(del_btn)
class TasksApp(App):
def build(self):
return TasksWindow()
if __name__ == '__main__':
TasksApp().run()
You are encountering the following error because you are using Kivy keyword, height
TypeError: Cannot convert float to kivy.properties.Property
Replace:
height = .9
with:
parent_height = NumericProperty(.9) # 90% height of its parent layout
Example
main.py
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.properties import NumericProperty
class TasksWindow(FloatLayout):
parent_height = NumericProperty(.9) # 90% height of its parent layout
def __init__(self, **kwargs):
super(TasksWindow, self).__init__(**kwargs)
addBtn = Button(
text="+", pos_hint={'x': .9, 'top': 1}, size_hint=(.1, .1))
self.add_widget(addBtn)
addBtn.bind(on_press=self.clkAdd)
def clkAdd(self, obj):
editBtn = Button(text="Tap to Edit", pos_hint={
'x': 0.0, 'top': self.parent_height}, size_hint=(.9, .1))
delBtn = Button(text="X", pos_hint={
'x': .9, 'top': self.parent_height}, size_hint=(.1, .1))
self.parent_height -= .1
self.add_widget(editBtn)
self.add_widget(delBtn)
class TasksApp(App):
def build(self):
window = TasksWindow()
return window
if __name__ == '__main__':
TasksApp().run()
Output
Related
The current value could displayed immediately above the slider,such like this:
enter image description here
Thanks so much!
I had try like it but it show not good enough.
Here is my codes:
import sys
from PySide2.QtWidgets import QApplication, QWidget, QSlider, QLabel
from PySide2.QtCore import Qt
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.slider = QSlider(Qt.Horizontal, self)
self.slider.setRange(0, 100)
self.slider.setValue(50)
self.slider.setGeometry(30, 40, 100, 30)
self.slider.valueChanged[int].connect(self.changeValue)
self.label = QLabel(self)
self.label.setText("50")
self.label.setGeometry(140, 40, 30, 30)
self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('QSlider')
self.show()
def changeValue(self, value):
self.label.setText(str(value))
self.label.move(self.slider.x() + value, self.slider.y() - 20)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
sys.exit(app.exec_())
Using kivy, I am trying to create my own dropdown menu to solve some issues I have with the built in DropDown widget. However, when resizing my widget to animate an opening sequence, the widget size remains the same. Upon further investigation I realised that the widget is always the same size as the parent RelativeLayout but I can't find a reason why. Here is a minimum working code
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.uix.relativelayout import RelativeLayout
from kivy.lang import Builder
import kivy.properties as props
KV = '''
#:import MaterialWidget materialwidget.MaterialWidget
<DropDown#RelativeLayout>
_background: background
canvas:
Color:
rgba: 1, 0, 0, 1
Rectangle:
pos: root._pos
size: root._size
Button:
id: background
size: root._size
pos: root._pos
on_size: print("size_changed", self.size)
'''
class DropDown(RelativeLayout):
opened = props.BooleanProperty(False)
anchor = props.OptionProperty("tl", options=["tr", "tl", "br", "bl"])
_size = props.ListProperty([0, 0])
_pos = props.ListProperty([0, 0])
_background = props.ObjectProperty(None)
def __init__(self, **kwargs):
super(DropDown, self).__init__(**kwargs)
#self.add_widget(self._background)
trigger = Clock.create_trigger(lambda *args: self.on_pos())
trig = Clock.create_trigger(lambda *args: self.on_size())
self.bind(
anchor=trigger,
_size=trigger,
opened=trig
)
def on_pos(self, *args):
# calculate new pos
invert_x = "r" in self.anchor
invert_y = "t" in self.anchor
self._pos = (
self.pos[0] - self._size[0] if invert_x else 0,
self.pos[1] - self._size[1] if invert_y else 0
)
def on_size(self, *args):
anim = Animation(
_size=self.size if self.opened else (0, 0),
duration=0.5,
transition="in_out_circ"
)
anim.start(self)
def open(self, *args):
# toggle the value of open state
self.opened = not self.opened
Builder.load_string(KV)
if __name__ == "__main__":
from kivy.app import runTouchApp
from kivy.uix.button import Button
from kivy.core.window import Window
class Test(Button):
def __init__(self, **kwargs):
super(Test, self).__init__(**kwargs)
self.size = (100, 100)
self.size_hint = (None, None)
self.dropdown = DropDown(pos=(200, 100), size=(400, 300))
self.add_widget(self.dropdown)
self.bind(on_release=lambda *args: self.dropdown.open(self))
Window.clearcolor = (1, 1, 1, 1)
runTouchApp(Test())
You probably need to set size_hint: None, None on your DropDown. If size_hint is not None, it will over-ride any size setting (including animations of the size property).
I'm new to Kivy and I'm looking for a way to drag Images only inside choosen Layout, not around all the window. I tried to manipulate with drag_rectangle option in kv file, but this not ended with expected behavior. Is there any way to set area when user could drag items and if it exists, how it could be done?
Thanks for your reply.
Here is my code:
from kivy.config import Config
Config.set('input', 'mouse', 'mouse,disable_multitouch')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.behaviors import DragBehavior
from kivy.uix.image import Image
class DragLabel(DragBehavior, Image):
pass
class Pattern(FloatLayout):
def create_pattern(self):
A = DragLabel(source="some.png", pos=(0, 0))
self.add_widget(A)
class MainScreen(BoxLayout):
pass
class AberationsApp(App):
def build(self):
return MainScreen()
window = AberationsApp()
window.run()
My kv file:
<DragLabel>:
drag_rectangle: self.x/10, self.y/10, root.width, root.height
drag_timeout: 10000000
drag_distance: 0
on_touch_move: print(self.x, self.y, self.size)
<Pattern>:
<MainScreen>:
size_hint: 1, 1
Pattern:
size_hint: .8, 1
id: Created_Pattern
Button:
size_hint: .2, 1
text:"Load_points!"
on_press: print(self.size, root.size)
on_release: Created_Pattern.create_pattern()
Ok, I solved this.
I had to understand how DragBehavior works. Apparently, there is documentation on Kivy website, when you can find every possible interaction with draggable items.
(https://kivy.org/doc/stable/_modules/kivy/uix/behaviors/drag.html)
Function on_touch_move(self, touch) solved my problem.
Additonaly I used some methods from Layouts in order to make sure, that my Images will change their position proportionally to size of my window.
Here is my code:
from kivy.config import Config
Config.set('input', 'mouse', 'mouse,disable_multitouch')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.behaviors import DragBehavior
from kivy.uix.image import Image
from kivy.metrics import sp
class DragLabel(DragBehavior, Image):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.previous_size = [1, 1]
def do_layout(self, *args):
self.x = self.x * self.width / self.previous_size[0]
self.y = self.x * self.height / self.previous_size[1]
if self.x > self.width:
self.x = .85 * self.width
if self.y > self.height:
self.y = .85 * self.height
self.previous_size = [self.width, self.height]
def on_size(self, *args):
self.do_layout()
def on_touch_move(self, touch):
if self._get_uid('svavoid') in touch.ud or \
self._drag_touch is not touch:
return super(DragBehavior, self).on_touch_move(touch) or \
self._get_uid() in touch.ud
if touch.grab_current is not self:
return True
uid = self._get_uid()
ud = touch.ud[uid]
mode = ud['mode']
if mode == 'unknown':
ud['dx'] += abs(touch.dx)
ud['dy'] += abs(touch.dy)
if ud['dx'] > sp(self.drag_distance):
mode = 'drag'
if ud['dy'] > sp(self.drag_distance):
mode = 'drag'
ud['mode'] = mode
if mode == 'drag':
previous_x_position = self.x
previous_y_position = self.y
new_x_position = previous_x_position + touch.dx
new_y_position = previous_y_position + touch.dy
if .85 * self.size[0] >= new_x_position >= 0 and .85 * self.size[1] >= new_y_position >= 0:
self.x = new_x_position
self.y = new_y_position
return True
class Pattern(FloatLayout):
def create_pattern(self):
A = DragLabel(source="some.png", pos=(0, 0))
self.add_widget(A)
class MainScreen(BoxLayout):
pass
class AberationsApp(App):
def build(self):
return MainScreen()
window = AberationsApp()
window.run()
And kv file:
<DragLabel>:
drag_rectangle: self.x, self.y, root.width, root.height
<Pattern>:
<MainScreen>:
size_hint: 1, 1
Pattern:
size_hint: .8, 1
id: Created_Pattern
Button:
size_hint: .2, 1
text:"Load points!"
on_release: Created_Pattern.create_pattern()
I'm trying to create a button using touchripple.py found in kivy.uix.behaviors. However, I ended up unsuccessful. Can anyone show an easy example of touchripple with buttons using Kivy lang? Thanks in advance.
Now, only the ripple effect isn't showing still. Please advice. Thanks.
In rippleexample2.py:
from kivy.app import App
from kivy.uix.touchripple import TouchRippleBehavior
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import (StringProperty, NumericProperty, ObjectProperty,
ListProperty, DictProperty, BooleanProperty)
class RippleButton(TouchRippleBehavior, Button):
isRippled = BooleanProperty(False)
def __init__(self, **kwargs):
super(RippleButton, self).__init__(**kwargs)
def on_touch_down(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point and not self.isRippled:
self.isRippled = True
self.ripple_show(touch)
return super(RippleButton, self).on_touch_down(touch)
def on_touch_up(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point and self.isRippled:
self.isRippled = False
self.ripple_fade()
return super(RippleButton, self).on_touch_up(touch)
def doit(self, *args):
print('in doit')
class Login(Screen):
pass
class MainScreen(Screen):
pass
class ScreenManager(ScreenManager):
pass
MainScreen = Builder.load_file("rippleexample2.kv")
class SimpleKivy4(App):
def build(self):
return MainScreen
if __name__ == "__main__":
SimpleKivy4().run()
In rippleexample2.kv:
ScreenManager:
Login:
MainScreen:
<Login>:
name:"login"
RippleButton:
text:'Login'
font_size: 24
on_release: app.root.current = "main"
<MainScreen>:
name: "main"
RippleButton:
text: 'back'
on_release: app.root.current = "login"
Below is a snippet for creating a Button which renders the touch ripple animation on interaction:
Snippet
class RippleButton(TouchRippleBehavior, Button):
def on_touch_down(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point:
touch.grab(self)
# The background_color (r, g, b, a) of button widget defaults to [1, 1, 1, 1]
# where 'a' (alpha compositing or transparency) is 1 i.e. not transparent
self.transparency = self.background_color[3] # backup original transparency / alpha compositing
self.background_color[3] = 0.5 # set transparency to half (0.5)
self.ripple_show(touch)
# dispatch on_press event because we have consumed on_touch_down
self.dispatch('on_press')
# consumed touch down and don’t want it to propagate any further.
return True
return False
def on_touch_up(self, touch):
if touch.grab_current is self:
touch.ungrab(self)
self.ripple_fade()
# defer on_release until ripple_fade has completed
def defer_release(dt):
self.background_color[3] = self.transparency # restore transparency / alpha compositing
self.dispatch('on_release')
Clock.schedule_once(defer_release, self.ripple_duration_out)
# consumed touch up and don’t want it to propagate any further.
return True
return False
Example
main.py
from kivy.app import App
from kivy.uix.behaviors.touchripple import TouchRippleBehavior
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
from kivy.clock import Clock
class RippleButton(TouchRippleBehavior, Button):
def on_touch_down(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point:
touch.grab(self)
# The background_color (r, g, b, a) of button widget defaults to [1, 1, 1, 1]
# where 'a' (alpha compositing or transparency) is 1 i.e. not transparent
self.transparency = self.background_color[3] # backup original transparency / alpha compositing
self.background_color[3] = 0.5 # set transparency to half (0.5)
self.ripple_show(touch)
# dispatch on_press event because we have consumed on_touch_down
self.dispatch('on_press')
# consumed touch down and don’t want it to propagate any further.
return True
return False
def on_touch_up(self, touch):
if touch.grab_current is self:
touch.ungrab(self)
self.ripple_fade()
# defer on_release until ripple_fade has completed
def defer_release(dt):
self.background_color[3] = self.transparency # restore transparency / alpha compositing
self.dispatch('on_release')
Clock.schedule_once(defer_release, self.ripple_duration_out)
# consumed touch up and don’t want it to propagate any further.
return True
return False
def doit(self, *args):
print('in doit')
class Login(Screen):
pass
class MainScreen(Screen):
pass
class SimpleKivy4(App):
def build(self):
return Builder.load_file("main.kv")
if __name__ == "__main__":
SimpleKivy4().run()
main.kv
#:kivy 1.11.0
ScreenManager:
Login:
MainScreen:
<Login>:
name:"login"
RippleButton:
text:'Login'
font_size: 24
on_release: root.manager.current = "main"
<MainScreen>:
name: "main"
RippleButton:
text: 'back'
on_release: root.manager.current = "login"
Output
After looking a bit closer at the example, I noticed that they are doing some odd things in the RippleButton. They were stopping the dispatching of the touch events for some unknown reason. I have modified the code to continue the dispatching (so now the on_release should work). And I have added a BooleanProperty to keep track of whether the TouchRipple Behavior is in effect.
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import BooleanProperty
from kivy.uix.behaviors import TouchRippleBehavior
from kivy.uix.button import Button
class RippleButton(TouchRippleBehavior, Button):
isRippled = BooleanProperty(False)
def __init__(self, **kwargs):
super(RippleButton, self).__init__(**kwargs)
def on_touch_down(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point and not self.isRippled:
self.isRippled = True
self.ripple_show(touch)
return super(RippleButton, self).on_touch_down(touch)
def on_touch_up(self, touch):
collide_point = self.collide_point(touch.x, touch.y)
if collide_point and self.isRippled:
self.isRippled = False
self.ripple_fade()
return super(RippleButton, self).on_touch_up(touch)
def doit(self, *args):
print('in doit')
theRoot = Builder.load_string('''
RippleButton:
text: 'Click Here'
on_release: self.doit()
''')
class TouchRippleApp(App):
def build(self):
return theRoot
if __name__ == '__main__':
TouchRippleApp().run()
In my exercise, the widget "TextInput" doesn't update its position and its size, when i resize the window; instead the widget "Label" updates correctly its size and its position. Why? Can someone help me, please?
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.graphics import Color, Rectangle
class MyWidget(FloatLayout):
def __init__(self, *args):
super(MyWidget, self).__init__(*args)
with self.canvas.before:
Color(255, 255, 255, 1)
self.rect = Rectangle(size=self.size, pos=self.pos)
def update_rect(instance, value):
instance.rect.pos = instance.pos
instance.rect.size = instance.size
self.bind(pos=update_rect, size=update_rect)
self.add_widget(TextInput(text='InsertText', multiline=False, size_hint=(1, 0.04),
pos_hint={'right': 1, 'y': 0.879}, font_size='12sp'))
self.add_widget(Label(text='label example', color=(0,0,255,0.5), pos_hint={'center_x': 0.5, 'center_y': 0.5}))
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()