I've created two Scatter widgets that can be resized, what I'm looking for is a way to spawn more Scatter widgets (potentially up to 20-ish) each time a button is pressed. I tried to write and bind an addfunction() to a Button, but it doesn't seem to work.
I'm looking for a way to spawn a new instance of the Scatterer widget (along with the child widget Drawer which has a canvas to show it's occupied space) when prompted.
Here's my python code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.scatter import Scatter
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
class Scatterer(Scatter):
pass
class Drawer(FloatLayout):
pass
class MainWindowWidget(FloatLayout):
def addfunction(*args):
root = MainWindowWidget()
s = Scatterer()
d = Drawer()
root.add_widget(s)
s.add_widget(d)
print("button is pressed")
class TestApp(App):
def build(self):
return MainWindowWidget()
if __name__ == '__main__':
TestApp().run()
and my .kv file:
#:kivy 1.0.9
<MainWindowWidget>
Scatterer:
id: scatter2
size: 80, 80
Drawer:
size: scatter2.size
Scatterer:
id: scatter1
size: 80, 80
Drawer:
size: scatter1.size
Button:
text: 'press'
size_hint: None, None
size: 100, 100
pos: 100, 0
on_release: root.addfunction()
<Scatterer>:
do_rotation: False
size_hint: None, None
size: self.size
canvas.after:
Color:
rgba: 1, 1, 0, 0.5
Rectangle:
size: self.size
pos: self.pos
<Drawer>:
size: self.size
canvas:
Color:
rgba: 0, 1, 0, 0.3
Rectangle:
pos: self.pos
size: self.size
root = MainWindowWidget()
Your problem is that you write this, then add the new Scatter to root, but then never do anything with root so it never gets displayed.
You don't want to add the Scatterer to a new MainWindowWidget, but to the current one whose method is running to make the new Scatterer. So replace the declaration of your method with
def addfunction(self, *args):
s = Scatterer()
d = Drawer()
self.add_widget(s)
s.add_widget(d)
print("button is pressed")
Related
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
I trying to create a one screen app with one root rule set. by pressing a button i should see a scrollable text in the grid next to the button. In all the example solutions I see that scrollview is working in similar KV files that I have seen in other questions. Could someone please identify what I have missed in KV file.
my .py file:
import kivy
import string
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
from kivy.properties import StringProperty
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window
class RootContainer(BoxLayout):
instance = ObjectProperty(None)
def __init__(self, **kwargs):
super(RootContainer, self).__init__(**kwargs)
def clickAction1(self, instance):
#identify the button pressed
buttonText = instance.text
self.lbl1.text = instance.text + " some text goes here ... "
myresult = " this is scrolling text.\n " * 30
self.lbl5.text = myresult
class MBApp(App):
def build(self):
return RootContainer()
if __name__ == '__main__':
MBApp().run()
my KV file:
#:kivy 1.0.9
<RootContainer>:
id: theRoot
lbl1: my_labelC
lbl5: my_labelCS
BoxLayout:
orientation: 'vertical'
spacing: 20
padding: 20
canvas:
Color:
rgb: 0, .33, 0
Rectangle:
pos: self.pos
size: self.size
Button:
text: "This is 1st button"
text_size: self.size
size_hint: (.5,1)
on_press: theRoot.clickAction1(self)
Button:
text: "This is 2nd button"
text_size: self.size
size_hint: (.5,1)
on_press: root.clickAction1(self)
GridLayout:
rows: 2
cols: 1
spacing: 10
padding: 10
canvas:
Color:
rgb: .7, .63, 0
Rectangle:
pos: self.pos
size: self.size
Label:
id: my_labelC
canvas.before:
Color:
rgb: 0,0,0
Rectangle:
pos: self.pos
size: self.size
text: "Header text for button clicked ......."
text_size: self.size
ScrollView:
GridLayout:
cols:1
rows:1
height: self.minimum_height
Label:
id: my_labelCS
text: "Scrolling text goes here ....."
I hope this is not a duplicate. Any other suggestions to code are also welcome. Thank you.
You don't set the size of your Label, so the default applies, as any widget, the defaults are
size: 100, 100
size_hint: 1, 1
since size_hintis 1, 1, and the parent is a layout, size itself is overriden by the parent's available space
since you set size: minimum_size in the parent layout, it'll give the minimum size its children require, but the Label doesn't ask for any space, it's size_hint of 1, 1 means that it's happy to take all the available space. Kivy solve this situation by not giving it any space, so the Label size ends up being 0, 0, and the GridLayout's size as well.
So you want to disable size_hint (at least for the height, of the Label, and instead set it to the texture heights
size_hint_y: None
height: self.texture_size[1]
Which is usually sided with setting the texture width to the available width, by setting text_size.
text_size: self.width, None
size_hint_x: 1 # this is the default, so this line is not required, but you want to be sure not to disable this default
Do you really need the GridLayout inside your scrollview? This works for me:
ScrollView:
Label:
id: my_labelCS
size_hint: 1, None
text_size: self.width, None
height: self.texture_size[1]
text: "Scrolling text goes here ....."
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.
I am currently working on a bit of code using Kivy and Python. I am trying to make it so when you click a button, the button's text changes color.
When I click on the button, however, the color doesn't like I want it to.
Any ideas how this can be fixed? I am just learning Kivy and perhaps the answer is easier than I think. The .py file is below
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.button import Button
from kivy.graphics import Color
class TextBubbleSample(Widget):
bubble = ObjectProperty(None)
class TextBubble(Widget):
pass
class Talk(Button):
btn = Button
def button_press(self):
btn.bind(on_state = self.on_event)
def on_event(self):
btn.color = 1,0,0,1
class TextBubbleApp(App):
def build(self):
return TextBubbleSample()
if __name__ == '__main__':
TextBubbleApp().run()
and here is the .kv file
#:kivy 1.0.9
<TextBubbleSample>:
bubble: text_bubble
btn: click_here
TextBubble:
id: text_bubble
x: root.x
center_y: root.center_y
Talk:
id: click_here
x: 10
center_y: 220
text: "Talk to me."
color: 0,0,1,1
<TextBubble>:
canvas:
Color:
rgba: 1,0,0,1
Rectangle:
pos: 10, 10
size: 780, 150
Label:
color: 0,0,1,1
font_size: 35
center_x: 200
top: root.top - 200
text: "I am talking"
You shouldn't/can't use the variable btn like that. Use self.bind(on_state=self.on_event) then self.color = (1, 0, 0, 1).
You should try to reduce the code length, you don't need to use any id in this case.
There could be an easy alternative to inclement's answer.
In your .py file (Talk class) add.
class Talk(Button):
....
def on_release(self):
self.color = 1,0,0,1
and in your .kv file add
Talk:
text: "talk to me"
....
on_release: self.on_release
EDIT:
you can do it this way too
class Singularity(BoxLayout):
def __init__(self,**kwargs):
super(Singularity,self).__init__(**kwargs)
self.b = Button(text = "hello",on_press = self.on_press)
self.add_widget(self.b)
def on_press(self,event):
if event.color == [1,0,0,1]:
event.color = [0,0,1,1]
else:
event.color=[1,0,0,1]
I'm using Kivy, a Python Library, and i'm having trouble getting a scroll view to work, and organizing it into a .kv file.
I have a big system that's broken up into many .py and .kv files.
I am also using kivy's screen manager
ui_manager.py
#ui_manager.py
class UIManager():
_ScreenManager = None
def __init__(self, inScreenManager):
self.ScreenManager = inScreenManager # The main app constructs the manager, sends to me, then returns it as the root widget.
main.py
#main.py
class MyApp(App):
def build(self):
screenManager = ScreenManager()
uiManager = ui_manager.UIManager(screenManager)
return screenManager
Obviously I ommited a little code, but this is almost exactly what happens.
One of the screns needs to scroll. It will have a long graphic and many buttons.
The Kivy documentation, as usual, only tells me how to do it via code, NOT via the kivy language (.kv files)
http://kivy.org/docs/api-kivy.uix.scrollview.html
I want a class, OrangeWidget, to hold the scrolling widget, so I can play with its data later on.
Here is my best attempt at getting an organized scrollview working
orange_widget.py
#orange_widget.py
class OrangeWidget(Screen):
pass
class OrangeGraphic(Widget):
pass
Orange.kv
<OrangeLineWidget>
ScrollView:
size_hint: (None, None)
size: (400, 1200)
OrangeLineGraphic:
pos: root.pos
<OrangeGraphic>
canvas:
Color:
rgba: 1, .5, 0, 1
Rectangle:
pos: self.center_x - 15, 0
size: 30, self.height * 2
Label:
text: "Hello"
I know it's a lot, and it's not looking well organized already, but i'm just trying to figure out how to get the dang thing working properly.
Here is the current result: It won't scroll, and for whatever reason it's been "boxed" to the right, instead of taking up the full screen
as answered on the ML, here you don't define the Label pos, your OrangeGraphic being a simple widget, not a layout, you need to do it:
<OrangeGraphic>
canvas:
Color:
rgba: 1, .5, 0, 1
Rectangle:
pos: self.center_x - 15, 0
size: 30, self.height * 2
Label:
text: "Hello"
pos: root.pos
size: root.size
Looks like the trick, for this particular "sliding" screen, was to use a "Relative Layout"
https://groups.google.com/forum/#!topic/kivy-users/RwuI8QGm3fw
Here is the updated code:
orange_widget.py
class OrangeWidget(Screen):
def __init__(self, **kwargs):
super(OrangeWidget, self).__init__(**kwargs)
scrollView = ScrollView(size_hint=(1, 1))
# add custom widget into that layout
customWidget = OrangeGraphicWidget(height=1200, size_hint_y=None)
#layout.bind(minimum_height=layout.setter('height'))
scrollView.add_widget(customWidget)
self.add_widget(scrollView)
class OrangeGraphicWidget(RelativeLayout):
pass
OrangeWidget.kv
<OrangeWidget>
<OrangeGraphicWidget>
canvas:
Color:
rgba: 1, .5, 0, 1
Rectangle:
pos: self.center_x - 15, 20
size: 30, self.height - (self.height / 10)
Button:
text: "Button 1"
pos: root.pos
size_hint: (None, None)
Button:
text: "Button 2"
pos_hint: {'center_x': .5, 'center_y': .95}
size_hint: (None, None)