Kivy DragBehavior property - simplest way. Why does it not work? - python

Everything seems to be ok (according to docs) (I know it isnt), but it's impossible to move my Image object.
Image object is visible on BrickCanvas but probably it is untouchable. I tried to print something after on_touch_down event on Image object and after touch down nothing happend.
memo.kv
<BrickCanvas>:
FloatLayout:
Brick
<Brick>:
drag_rectangle: 100 , 100 , 100 , 100
drag_timeout: 1000000000000000
drag_distance: 0
Image:
size: (150,150)
source: '/home/prezes/Desktop/KO.jpg'
main.py
#!/usr/bin/kivy
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.behaviors import DragBehavior
class BrickCanvas(Widget):
pass
class Brick(DragBehavior,Widget):
pass
class MemoApp(App):
def build(self):
return BrickCanvas()
if __name__ == '__main__':
MemoApp().run()

Apparently DragBehavior works but only for selected widget and not its children (in this case it's a Image) as you can test with this code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
Builder.load_string('''
<BrickCanvas>:
FloatLayout:
Brick
<Brick>:
canvas:
Color:
rgb: 0.5, 0.5, 0.5
Rectangle:
size: self.size
pos: self.pos
Image:
size: 50, 50
source: 'test.png' # change to your path
''')
class BrickCanvas(Widget):
pass
class Brick(DragBehavior,Widget):
pass
class MemoApp(App):
def build(self):
return BrickCanvas()
if __name__ == '__main__':
MemoApp().run()
You can use Image class directly to avoid this problem:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.uix.behaviors import DragBehavior
from kivy.lang import Builder
Builder.load_string('''
<BrickCanvas>:
Brick
<Brick>:
source: 'test.png' # change to your path
''')
class BrickCanvas(Widget):
pass
class Brick(DragBehavior, Image):
pass
class MemoApp(App):
def build(self):
return BrickCanvas()
if __name__ == '__main__':
MemoApp().run()
Still I'd say that for simple drag and drop functionality for an image it's better to just use Scatter widget:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.scatter import Scatter
from kivy.lang import Builder
Builder.load_string('''
<BrickCanvas>:
Brick
<Brick>:
do_scale: False
do_rotation: False
Image
source: 'test.png' # change to your path
''')
class BrickCanvas(Widget):
pass
class Brick(Scatter):
pass
class MemoApp(App):
def build(self):
return BrickCanvas()
if __name__ == '__main__':
MemoApp().run()

Related

How do I access ids in Kivy?

I am a beginner to Kivy (though not to Python), and I am struggling to get the ids from a kv string into my main code. I have the following, but the 'print' statement tells me that there are no IDs. The application itself runs with no errors.
import kivy
kivy.require('2.1.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image
kvString = """
MainScreen:
id: maincontainer
cols: 1
thumbnails: thumbnails.__self__
GridLayout:
id: thumbnails
cols: 3
rows: 3
Image:
source: "test.png"
"""
class MainScreen(GridLayout):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
# This prints 0
print("Ids: {}".format(len(self.ids.items())))
class ExampleApp(App):
def build(self):
root = Builder.load_string(kvString)
return root
if __name__ == "__main__":
ExampleApp().run()
When I ran your code, I got a critical warning that there are no screens, and therefore the app will terminate. As soon as I switched MainScreen to a screen, it worked out perfectly. Here is the code:
.py
import kivy
kivy.require('2.1.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.image import Image
sm = ScreenManager()
#I removed some unnecessary code such as the cols, thumbnail, etc.
kvString = """
<MainScreen>
GridLayout:
id: thumbnails
cols: 3
rows: 3
Image:
source: "test.png"
"""
#NEEDS TO INHERIT SCREEN
class MainScreen(Screen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
# This now prints 1
print(f'There are {len(self.ids.items())} id(s)')
class ExampleApp(App):
def build(self):
root = Builder.load_string(kvString)
#Adds the screen
sm.add_widget(MainScreen(name='screen'))
return sm
if __name__ == "__main__":
ExampleApp().run()

Problem when trying to make a circular button on kivy

from kivy.uix.button import Button
from kivy.uix.behaviors.button import ButtonBehavior
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.graphics import BorderImage
from kivy.graphics import Color, RoundedRectangle, Rectangle, Triangle
from kivy.core.window import Window
#from kivy.config import Config
from kivy.uix.checkbox import CheckBox
from kivy.uix.popup import Popup
from kivy.uix.scrollview import ScrollView
from kivy.uix.filechooser import FileChooserListView
from kivy.properties import ObjectProperty
class OpeningPage(FloatLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.but = RoButton(text = 'START', pos = (350,100), font_size=14, size=(100,60), size_hint=(None,None))
self.add_widget(self.but)
class RoButton(Button):
butt = ObjectProperty()
def __init__(self, **kwargs):
super(RoButton, self).__init__(**kwargs)
text = self.text
with self.canvas:
# Color(1., 0, 0)
self.butt = RoundedRectangle( size= self.size, pos = self.pos, radius =[400])
class UI(App):
def build(self):
self.screen_manager = ScreenManager()
self.opening_page = OpeningPage()
screen = Screen(name ='Opening_Page')
screen.add_widget(self.opening_page)
self.screen_manager.add_widget(screen)
return self.screen_manager
if __name__ == '__main__':
the_app = UI()
the_app.run()
Everytime I try to make a circular button using this code I get a box behind the circle. I tried doing self.canvas.before but still no luck if possible could answers be in python rather than .kv language thanks.
Attached image of problem]1
The box you see is the normal Button image. If you don't want that, probably don't use a Button, instead use class RoButton(ButtonBehavior, Widget):.

Reference from python into kivy gone wrong

The problem is in the .kv file. My problem is that i would like my .kv to notice the object-property in my .py. My code works if change the color in .kv to color: 0,0,0 which gives me black text as intend.
.py
from kivy import utils
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.uix.button import ButtonBehavior
Builder.load_file("FirebaseLoginScreen/themedwidgets.kv")
class ThemedButton(ButtonBehavior,Label):
colorchanged = ObjectProperty()
color = ObjectProperty([0,0,0,1])
def __init__(self, **kwargs):
super(ThemedButton, self).__init__(**kwargs)
Clock.schedule_once(self.start_pulsing, 1)
def start_pulsing(self, *args):
anim = Animation(color=[0,1,0,1]) + Animation(color=[0,0,1,1]) + Animation(color=[1,0,0,1])
anim.repeat = True
anim.start(self)
.kv
<ThemedButton#ButtonBehavior+Label>:
colorchanged: colorchanged
id: colorchanged
markup: True
color: self.color #root.color doesn't work either but 0,0,0 does give me black.
opacity: 1 if self.state == 'normal' else .8
font_size: 38
<ThemedButton#ButtonBehavior+Label>:
This is the kv syntax to create a new widget class inheriting from ButtonBehavior and Label, so it doesn't have any of the behaviour of your Python-defined class that happens to have the same name.
You want to just do <ThemedButton>: instead.

Kivy change label text [duplicate]

This question already has answers here:
What is the purpose of the `self` parameter? Why is it needed?
(26 answers)
Closed 3 years ago.
I would like to change a Label-text in Python/Kivy after a swipe event has been detected. Changing the text works basically via the following line
self.Translation.text = "test"
but I have to change the text after detecting a swipe event from another class, in which I call a function to change the label text:
MyWidget.ThisDoesntWork("self_dummy")
In this function the exact same line as above gives me an error.
How can I change the Label-text from class "Swiping_class" calling function "MyWidget.ThisDoesntWork("self_dummy")"?
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.base import EventLoop
from kivy.clock import Clock
from kivy.factory import Factory
from kivy.uix.stacklayout import StackLayout
from kivy.uix.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import *
from kivy.properties import ListProperty
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from random import random
import pickle
import random
kv = '''
<ColoredLabel>:
size: (self.size_x,self.size_y)
pos: (0,0) # no effect
background_color:
canvas.before:
Color:
rgba: self.background_color
Rectangle:
pos: self.pos
size: (self.size_x,self.size_y)
'''
Builder.load_string(kv)
class ColoredLabel(Label):
background_color = ListProperty((0,0,0,1))
s_global = Window.size
size_x = s_global[0]
size_y = s_global[1]/3
class MyWidget(BoxLayout):
#init
def __init__(self, **kwargs):
super().__init__(**kwargs)
s_global = Window.size
size_x = s_global[0]
size_y = s_global[1]/3
self.ForeignLanguage = ColoredLabel(text="str_fl", size_hint=(None, None),size = (size_x,size_y), background_color=(0/255,171/255,169/255, 1))
self.Translation = ColoredLabel(text="str_tr", size_hint=(None, None),size = (size_x,size_y), background_color=(45/255,137/255,239/255, 1))
self.Example = ColoredLabel(text="str_ex", size_hint=(None, None),size = (size_x,size_y), background_color=(43/255,87/255,151/255, 1))
self.verticalBox = BoxLayout(orientation='vertical')
self.verticalBox.add_widget(self.ForeignLanguage)
self.verticalBox.add_widget(self.Translation)
self.verticalBox.add_widget(self.Example)
self.Translation.text = "test"
s=Swiping_class()
s.add_widget(self.verticalBox)
self.add_widget(s)
def ThisDoesntWork(self):
print("this is printed")
self.Translation.text = "I wanna change this via fucntion"
print("this is not printed anymore")
class Swiping_class(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.initial = 0
def on_touch_down(self, touch):
self.initial = touch.x
def on_touch_up(self, touch):
if touch.x < self.initial:
print("swiped left")
MyWidget.ThisDoesntWork("self_dummy")
else:
print("swiped right")
class BoxLayoutDemo(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
BoxLayoutDemo().run()
I think I found a solution. Within your Swiping_class, replace this line:
MyWidget.ThisDoesntWork("self_dummy")
with this line:
MyWidget.ThisDoesntWork(self.parent)
That way, instead of passing a string to your method, you pass the label object, which contains the text attribute you are trying to modify.

Kivy - Adding elements in a Screen

import os,sys,random
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.graphics import Color, Rectangle
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.uix.screenmanager import ScreenManager, Screen , SlideTransition
from kivy.animation import Animation
from kivy.properties import StringProperty
class Page(Screen):
source = StringProperty()
class Imglayout(FloatLayout):
def __init__(self,**args):
super(Imglayout,self).__init__(**args)
with self.canvas.before:
Color(0,0,0,0)
self.rect=Rectangle(size=self.size,pos=self.pos)
self.rootpath = os.path.dirname(os.path.realpath(sys.argv[0]))
self.images=[]
for img in os.listdir(self.rootpath+'/images'):
self.images.append(self.rootpath+'/images/'+img)
self.index=random.randint(0,len(self.images)-1)
self.im=Image(source=self.images[self.index])
self.im.keep_ratio= False
self.im.allow_stretch = True
#self.add_widget(self.im)
self.sm = ScreenManager(transition=SlideTransition())
self.page1=Page(name='page1', source = self.images[self.index])
self.page2=Page(name='page2', source = self.images[self.index+1])
self.sm.add_widget(self.page1)
self.sm.add_widget(self.page2)
self.bind(size=self.updates,pos=self.updates)
def updates(self,instance,value):
self.rect.size=instance.size
self.rect.pos=instance.pos
def on_touch_down(self,touch):
if self.collide_point(*touch.pos):
if(self.sm.current == 'page1'):
next='page2'
page=self.page2
else:
next='page1'
page=self.page1
self.index=(self.index+1)%len(self.images)
page.source = self.images[self.index]
page.background.scale = 1.0
self.sm.transition=SlideTransition()
self.sm.current = next
anim = Animation(
scale=page.background.scale*1.3,
duration=15.0
)
anim.start(page.background)
return True
return False
class MainTApp(App):
def build(self):
root = BoxLayout(orientation='vertical',spacing=10)
c = Imglayout()
root.add_widget(c)
cat=Button(text="Categories",size_hint=(1,.05))
cat.bind(on_press=self.callback)
root.add_widget(cat);
return root
def callback(self,value):
print "CALLBACK CAT"
if __name__ == '__main__':
MainTApp().run()
Taking some hints from here i made the above program. It says that Page does not have a background attribute in both my and the referenced code. Its kind of obvious since there is no background attribute. I thought it inherited that from Screen. I am trying to make a slideshow kind of thing. But i cant find any information on how to set the background of a screen.
And if i comment out everything with .background and run the app. click on the black space, then i start getting this error continuously on the terminal
[ERROR ] [OSC ] Address 127.0.0.1:3333 already in use, retry
in 2 second
And i still dont get any background on the app.(its all black.)
and if i print self.sm.current on the touich function. Then i find that its always page1, it never changes.
The Kivy Guide explains how to add a background to a Widget. Briefly, you can do it in Python using the following code which binds to the position and size of the widget to make sure the background moves with the widget.
with widget_instance.canvas.before:
Color(0, 1, 0, 1) # green; colors range from 0-1 instead of 0-255
self.rect = Rectangle(size=layout_instance.size,
pos=layout_instance.pos)
widget_instance.bind(size=self._update_rect, pos=self._update_rect)
...
def _update_rect(self, instance, value):
self.rect.pos = instance.pos
self.rect.size = instance.size
Or you can do it more naturally with the kv language e.g.
MyWidget:
canvas.before:
Color:
rgba: 0, 1, 0, 1
Rectangle:
# self here refers to the widget i.e screen
pos: self.pos
size: self.size

Categories