Related
I am new in kivy and I am trying to change image of float layout with a button
I tried everything I can but it didn't worked out.
I am getting the below error
AttributeError: 'Chat_Bot' object has no attribute 'ids'
May be I need to extend the class but I am not sure
Below is my main.py file
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager
from kivy.clock import Clock
from kivy.core.text import LabelBase
from kivymd.uix.label import MDLabel
from kivymd.uix.label import MDLabel
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty,NumericProperty
from kivymd.uix.textfield import *
import lk_k
Window.keyboard_anim_args = {"d":.2,"t":"in_out_quart"}
Window.softinput_mode = ""
class Command(MDLabel):
text = StringProperty()
size_hint_x = NumericProperty()
halign = StringProperty()
font_size=26
class Response(MDLabel):
text = StringProperty()
size_hint_x = NumericProperty()
halign = StringProperty()
font_size=26
class main(MDApp):
def change_screen(self, name):
screen_manager.current = name
def build(self):
global screen_manager
screen_manager = ScreenManager()
screen_manager.add_widget(Builder.load_file("Chats.kv"))
return screen_manager
def response(self, *args):
q=lk_k.get_response(message)
if len(q)<6:
s=.22
h="center"
elif len(q)<11:
s=.32
h="center"
elif len(q) < 16:
s = .45
h = "center"
elif len(q) < 21:
s = .58
h = "center"
elif len(q) < 26:
s = .71
h = "center"
else:
s = .77
h = "center"
screen_manager.get_screen('chats').chat_list.add_widget(Response(text=q, size_hint_x=s,halign=h))
def send(self):
global message, size, halign
if screen_manager.get_screen('chats').text_input != "":
message = screen_manager.get_screen('chats').text_input.text
if len(message)<6:
size=.22
halign="center"
elif len(message)<11:
size=.32
halign="center"
elif len(message) < 16:
size = .45
halign = "center"
elif len(message) < 21:
size = .58
halign = "center"
elif len(message) < 26:
size = .71
halign = "center"
else:
size = .77
halign = "left"
screen_manager.get_screen('chats').chat_list.add_widget(Command(text=message,size_hint_x=size,halign=halign))
Clock.schedule_once(response, 1)
screen_manager.get_screen('chats').text_input.text = ""
global counter
counter = 0
def clear_image(self):
global counter
if counter==0:
self.ids.img2.source ='LOGO.png'
self.ids.img2.reload()
counter += 1
elif counter==1:
self.ids.img2.source ='on.png'
self.ids.img2.reload()
counter += 1
elif counter==2:
self.ids.img2.source ='off.png'
self.ids.img2.reload()
counter += 1
elif counter==3:
self.ids.img2.source =''
self.ids.img2.reload()
counter =0
if __name__ == "__main__":
main().run()
My chats.kv file
<Command>
size_hint_y:None
pos_hint:{"right": .99}
height: self.texture_size[1]
padding: 12,10
theme_text_color: "Custom"
text_color:53/255,56/255,60/255,1
canvas.before:
Color:
rgb: (1, 1,1,1)
RoundedRectangle:
size:self.width,self.height
pos:self.pos
radius:[23,0,23, 23]
<Response>
size_hint_y:None
pos_hint:{"x": .01}
height: self.texture_size[1]
padding: 12,10
theme_text_color: "Custom"
text_color: 53/255,56/255,60/255,1
canvas.before:
Color:
rgb: ( 0,1,1, 1)
RoundedRectangle:
size:self.width,self.height
pos:self.pos
radius:[0,23,23,23]
MDScreen:
bot_name: bot_name
text_input: text_input
chat_list: chat_list
name: "chats"
MDFloatLayout:
canvas :
Color:
rgb:1,1,1, 1
Rectangle:
id: img2
source:'q3.png'
size:self.size
pos:self.pos
MDFloatLayout:
md_bg_color: 0,1,1,1
size_hint_y:.11
pos_hint: {"center_y":.96}
MDLabel:
id: bot_name
text:"OLIVIA"
right_action_items: [["dots-vertical", lambda x: app.callback(x)]]
font_size: "25sp"
pos_hint: {"center_y": .43}
halign: "center"
theme_text_color: "Custom"
text_color: 53/255,56/255,60/255,1
MDIconButton:
icon:"emma.png"
pos_hint:{"center_x":.2,"center_y":.43}
user_font_size:"15sp"
theme_text_color:"Custom"
text_color:53/255,56/255,60/255,1
md_bg_color: 127/255,1, 212/255, 1
MDIconButton:
icon:"video-outline"
pos_hint:{"center_x":.80,"center_y":.43}
user_font_size:"31sp"
theme_text_color: "Custom"
text_color:53/255,56/255,60/255,1
MDIconButton:
text:"M"
pos_hint:{"center_x":.90,"center_y":.43}
user_font_size:"31sp"
theme_text_color: "Custom"
text_color:53/255,56/255,60/255,1
on_release: app.clear_image()
ScrollView:
size_hint_y:.78
background_color:1,1,1,1
pos_hint:{"x":0,"y":.116}
do_scroll_x:False
do_scroll_y:True
BoxLayout:
id:chat_list
orientation:'vertical'
size:(root.width,root.height)
height:self.minimum_height
size_hint:None, None
pos_hint:{"top": 1}
cols:1
spacing:3
MDFloatLayout:
size_hint_y:.08
md_bg_color:0,1,1,1
MDFloatLayout:
size_hint:.8, .75
pos_hint:{"center_x":.43,"center_y":.5}
md_bg_color:0,1,1,1
canvas:
Color:
rgb:1,1,1, 1
RoundedRectangle:
size:self.size
pos:self.pos
radius:[23, 23, 23, 23]
TextInput:
id:text_input
hint_text:"Type your message"
size_hint:1, None
pos_hint:{"center_x":.5,"center_y":.5}
multiline:False
font_size:"18sp"
height:self.minimum_height
cursor_color:1, 170/255, 23/255, 1
cursor_width:"2sp"
foreground_color:53/255,56/255,60/255,1
background_color:0,0,0,0
padding:30
MDIconButton:
icon:"send-outline"
pos_hint:{"center_x":.91,"center_y":.5}
user_font_size:"23sp"
theme_text_color:"Custom"
text_color:1,1,1,1
md_bg_color: 0,1,1,1
on_press:app.send()
Any help would be great
You cannot assign an id to a canvas instruction, but you can assign one to the widget that contains the canvas instruction. And, if you want to change the canvas instruct using python, it will be easier if the canvas instructions are defined in python rather than in kv. In order to do that, you can define an extension of MDFloatLayout that does the canvas instructions:
class MyMDFloatLayout(MDFloatLayout):
def __init__(self, **kwargs):
super(MyMDFloatLayout, self).__init__(**kwargs)
with self.canvas:
Color(1, 1, 1, 1) # set the colour
# Setting the size, position, and source of canvas
self.rect = Rectangle(pos=self.pos,
size=self.size,
source='q3.png')
# Update the canvas when the position/size changes
self.bind(pos=self.update_rect,
size=self.update_rect)
# update function which makes the canvas adjustable.
def update_rect(self, *args):
self.rect.pos = self.pos
self.rect.size = self.size
Then you can use this widget in your kv file in place of the MDFloatLayout that contains the canvas that we want to adjust:
MyMDFloatLayout:
id: img2
# canvas :
# Color:
# rgb:1,1,1, 1
# Rectangle:
# id: img2
# source:'q3.png'
# size:self.size
# pos:self.pos
Note that these canvas instructions in the kv are no longer required
Then, in your python code:
def clear_image(self):
global counter
if counter == 0:
widget = self.root.get_screen('chats').ids.img2
widget.rect.source = 'LOGO.png'
# self.ids.img2.source = 'LOGO.png'
# self.ids.img2.reload()
counter += 1
and similar for the other counter values.
I can see the effects from the CanvasWidget() class but not any effects from the MyApp() class. Why is it so
#Importing kivy module
import kivy
kivy.require("1.10.1")
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color
from kivy.uix.image import Image
from kivy.config import Config
#Creating the canvas
class CanvasWidget(Widget):
def __init__(self, **kwargs):
super(CanvasWidget, self).__init__(**kwargs)
with self.canvas:
Color(1, 1, 1, 1)
self.rect = Rectangle(pos = self.center, size = (self.width / 2., self.height / 2.))
self.bind(pos = self.update_rect, size = self.update_rect)
def update_rect(self, *args):
self.rect.pos = self.pos
self.rect.size = self.size
class CanvasApp(App):
def build(self):
return CanvasWidget()
CanvasApp().run()
#Putting the image
Config.set("graphics", "resizable", True)
class MyApp(App):
def build(self):
self.img = Image(source = "voithos.jpg")
self.img.allow_stretch = True
self.img.keep_ratio = False
self.img.size_hint_x = 1
self.img.size_hint_y = 1
self.img.pos = (200, 100)
self.img.opacity = 1
s = Widget()
s.add_widget(self.img)
return s
MyApp().run()
This is the output i get
The line:
CanvasApp().run()
will not return until the CanvasApp is shut down. So nothing after that line will be executed until you kill CanvasApp.
I am trying to add 4 boxes in Relativelayout. Able to horizontally move only the box which gets added last in relativelayout. All other box gets strucked up when it is moved closer to its box which got added later. In given sample code below 'Actor 400' freely moves horizontally across the scene. 'Actor 300' able to move freely over 'Actor 200' and 'Actor 100'. But when I try to move Actor 300 above Actor 400, it get strucked and only after mouse moves beyond Actor 400, I am able to move.
Can I change the zindex of the widget dynamically when I touch it.
from kivy.app import App
from kivy.graphics import Line, Color
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.lang import Builder
KV = '''
<Actor>:
canvas:
Color:
rgba: 0,1,0,.8
Rectangle:
id: _rect_
size: 100, 30
pos: 0, root.height - 30
Line:
points: 50, root.height - 30, 50, 20
width:2
Label:
id: _actr_lbl
text: 'Hello World'
color: 0,0,0,1
size_hint: None, None
size: 100, 30
pos: 0, root.height - 30
'''
Builder.load_string(KV)
class Actor(Scatter):
def __init__(self, **kwargs) :
Name = kwargs.pop('Name', 'Actor-NoName')
super(Actor, self).__init__(**kwargs)
self.Name = Name
self.ids._actr_lbl.text = Name
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.selected = True
self.pos_hint = {'top':1}
self.hint_diff_touch_to_x = self.right - touch.x
return True
return super(Actor, self).on_touch_down(touch)
def on_touch_move(self, touch):
if (self.selected) :
future_right = touch.x + self.hint_diff_touch_to_x
if (future_right <= self.parent.size[0]) and ((future_right - self.width) >= 0) :
self.right = future_right
self.pos_hint = {'top':1}
return True
return super(Actor, self).on_touch_move(touch)
def on_touch_up(self, touch):
if (self.selected) :
self.selected = False
self.hint_diff_touch_to_x = 0
self.iy = 0
return True
return super(Actor, self).on_touch_up(touch)
class MyPaintApp(App):
def build(self):
root = RelativeLayout()
(ix, iy) = (100,100)
clr = Color(0.2, 0.2, 1)
for ix in [100, 200, 300, 400 ] :
root.add_widget(Actor(Name = 'Actor ' + str(ix), pos=(ix,0), size_hint=(None,1)))
return root
if __name__ == '__main__':
Window.clearcolor = (1, 1, 1, 1)
MyPaintApp().run()
When one of your Actor Widgets is selected, you can move it to the top by removing it and re-adding it, like this:
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.selected = True
self.pos_hint = {'top':1}
self.hint_diff_touch_to_x = self.right - touch.x
parent = self.parent
# move this Actor to the front
parent.remove_widget(self)
parent.add_widget(self)
return True
return super(Actor, self).on_touch_down(touch)
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()
How can make a menu with two or more options when I click on FileBox object in below code? Plesae have a look on the attached pic if you don't know what I mean.
CODE
from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
"""
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()
John must have answered while I was looking at the question. John's answer is perfectly reasonable. My answer is a bit different because it depends on Popup.
from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.core.window import Window
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
<MenuPopup>:
BoxLayout:
orientation: "vertical"
Button:
text: "Delete"
on_press: print("Delete")
Button:
text: "Copy"
on_press: print("Copy")
"""
class MenuPopup(Popup):
pass
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
print(touch.pos)
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
popup = MenuPopup(title='Menu',
auto_dismiss=True,
size_hint=(None, None),
size=(180, 180),
pos_hint={'x': touch.pos[0] / Window.width,
'y':(touch.pos[1] - self.height)/ Window.height})
popup.open()
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()
Update
If you don't like the frame by Popup you can also use the class on which Popup is based. It is called ModalView. You also need to delete the title since ModalView has no title.
from kivy.config import Config
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.modalview import ModalView
from kivy.uix.label import Label
from kivy.core.window import Window
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
<MenuPopup>:
BoxLayout:
orientation: "vertical"
Button:
text: "Delete"
on_press: print("Delete")
Button:
text: "Copy"
on_press: print("Copy")
"""
class MenuPopup(ModalView):
pass
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
print(touch.pos)
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
popup = MenuPopup(
auto_dismiss=True,
size_hint=(None, None),
size=(180, 180),
pos_hint={'x': touch.pos[0] / Window.width,
'y':(touch.pos[1] - self.height)/ Window.height})
popup.open()
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()
Typically, a DropDown is attached to a Button, but that is not necessary. You can create the DropDown as described in the documentation and instead of binding it to a Button to open it, you can just call open() in your on_touch_down() method. Here is a modified version of your code that does that:
from kivy.config import Config
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
Config.set('graphics', 'multisamples', '0')
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.lang.builder import Builder
from kivy.app import App
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.boxlayout import BoxLayout
kv = """
<FileBox>
size_hint: None, None
size: 100, 100
canvas.before:
Color:
rgba: (0.337, 0.760, 0.968, 1)
Rectangle:
pos: self.pos
size: self.size
RelativeLayout:
FileBox:
pos_hint: {'center_x':0.5, 'center_y': 0.5}
"""
class FileBox(BoxLayout):
def __init__(self, **kwargs):
super(FileBox, self).__init__(**kwargs)
self.oryg_text = ''
def on_touch_down(self, touch):
if touch.button == 'right':
print('DROP A MENU\n Delete \n Copy')
# create the DropDown
self.dropdown = DropDown(auto_dismiss=False)
# add Buttons
btn = Button(text='Delete', size_hint_y=None, height=44)
btn.bind(on_release=self.function_called_from_drop_menu)
self.dropdown.add_widget(btn)
btn = Button(text='Copy', size_hint_y=None, height=44)
btn.bind(on_release=self.function_called_from_drop_menu)
self.dropdown.add_widget(btn)
# open the DropDown
self.dropdown.open(self)
def function_called_from_drop_menu(self, choosed):
print('CHOOSED', choosed)
# dismiss the DropDown
self.dropdown.dismiss()
sm = Builder.load_string(kv)
class NewApp(App):
def build(self):
self.title = 'Drop Menu'
return sm
if __name__ == '__main__':
NewApp().run()