I would like to populate MyData with information gathered from dropdown menus that show up in a popup window with "on_touch_up" in AddTouch. That data includes the position of "on_touch_up", in addition to the dropdown data. I am able to print the position within the AddTouch class, but I am having a hard time getting the data further down in my script using (for example: print('from MyMainApp: {}'.format(MyData.pos))).
I am also unable to get "mainbutton" or "dropdown" to show up in a popup window.
Hacking around with this I came up with the following which works, but doesn't do what i need
.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.uix.dropdown import DropDown
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.graphics import Color, Rectangle
class AddTouch(Widget):
def __init__(self, **kwargs):
super(AddTouch, self).__init__(**kwargs)
with self.canvas:
Color(1, 0, 0, 0.5, mode="rgba")
self.rect = Rectangle(pos=(0, 0), size=(10, 10))
def on_touch_down(self, touch):
self.rect.pos = touch.pos
def on_touch_move(self, touch):
self.rect.pos = touch.pos
def on_touch_up(self, touch):
# final position
self.pos = touch.pos
print(self.pos)
class MyPopup(Popup):
def __init__(self, **kwargs):
super(MyPopup, self).__init__(**kwargs)
# create a main button
self.mainbutton = Button(text='Hello', size_hint=(None, None))
# create a dropdown with 10 buttons
self.dropdown = DropDown()
for index in range(10):
btn = Button(text='Value %d' % index, size_hint_y=None, height=44)
btn.bind(on_release=lambda btn: self.dropdown.select(btn.text))
self.dropdown.add_widget(btn)
self.mainbutton.bind(on_release=self.dropdown.open)
self.dropdown.bind(on_select=lambda instance, x: setattr(self.mainbutton, 'text', x))
class MyData:
def __init__(self, **kwargs):
super(MyData, self).__init__(**kwargs)
self.pos=AddTouch.pos
# using kivy screen for consistency
class MainWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("dropd.kv")
class MyMainApp(App):
def build(self):
return kv
print('from MyMainApp: {}'.format(MyData.pos))
if __name__ == "__main__":
MyMainApp().run()
.kv
WindowManager:
MainWindow:
<MainWindow>:
name: "main"
AddTouch:
on_touch_up:
#MyPopup gives 'MyPopup' is not defined, even if I add <MyPopup>: below
#root.MyPopup gives 'MainWindow' object has no attribute 'MyPopup'
I tried adding a simple dynamic Popup class in the .kv file based on this, but again it says 'MyPopup' is not defined:
.kv
AddTouch
on_touch_up:
MyPopup
<MyPopup#Popup>:
auto_dismiss: False
Button:
text: 'Close me!'
on_release: root.dismiss()
What am I missing (other than experience, ability, and general intelligence)?
In addition to adding the line:
self.content = self.mainbutton
to the MyPopup __init__() method, you can trigger the MyPopup creation by modifying your .kv file as:
#:import Factory kivy.factory.Factory
WindowManager:
MainWindow:
<MainWindow>:
name: "main"
AddTouch:
on_touch_up:
Factory.MyPopup().open()
Related
i made a gui with python kivy with a starting screen that has a button in it which generates new screens when pressed, and it also generates new buttons so its easier to pick the screen i need to get in focus but i can't get it working properly cause i can't generate buttons on the newly made screens
when they aren't in focus, the script only generates buttons for the screen in focus it seems
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class ScreenManagement(ScreenManager):
def __init__(self, **kwargs):
super(ScreenManagement, self).__init__(**kwargs)
sm = ScreenManagement(transition=FadeTransition())
class newtab(Screen):
def __init__(self, **kwargs):
super(newtab, self).__init__(**kwargs)
self.bt1=(Button(text="New tab", size_hint =(.1, .1) ,pos_hint ={'center_x':.1, 'center_y':.94}))
self.add_widget(self.bt1)
self.bt1.bind(on_release=self.new_tab)
self.txt1 = TextInput(text='',size_hint =(.1, .1), pos_hint ={'center_x':.2, 'center_y':.75}, multiline=True)
self.add_widget(self.txt1)
def transition(self, instance):
self.manager.current = (instance.text)
for item in sm.screen_names:
self.bt2=(Button(text=item, size_hint =(.1, .1) ,pos_hint ={'center_x':(.1*sm.screen_names.index(item)+.1), 'center_y':.84}))
self.add_widget(self.bt2)
self.bt2.bind(on_release=self.transition)
def new_tab(self, *args):
n = len(self.manager.screen_names) ##number of screens+1
screen = newtab(name="screen {}".format(n)) #make new screen and give it number
self.manager.add_widget(screen)
self.manager.current = "screen 0"
for item in sm.screen_names:
self.bt2=(Button(text=item, size_hint =(.1, .1) ,pos_hint ={'center_x':(.1*sm.screen_names.index(item)+.1), 'center_y':.84}))
self.add_widget(self.bt2)
self.bt2.bind(on_release=self.transition)
sm.add_widget(newtab(name='screen 0'))
class Application(App):
def build(self):
return sm
if __name__ == "__main__":
Application().run()
I suppose that below is what you expected to get. But I agree with #ApuCoder that you may look for TabbedPanel functionality.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class ScreenManagement(ScreenManager):
def __init__(self, **kwargs):
super(ScreenManagement, self).__init__(**kwargs)
sm = ScreenManagement(transition=FadeTransition())
class newtab(Screen):
def __init__(self, **kwargs):
super(newtab, self).__init__(**kwargs)
self.bt1 = (Button(text="New tab", size_hint=(.1, .1), pos_hint={'center_x': .1, 'center_y': .94}))
self.add_widget(self.bt1)
self.bt1.bind(on_release=self.new_tab)
self.txt1 = TextInput(text='', size_hint=(.1, .1), pos_hint={'center_x': .2, 'center_y': .75}, multiline=True)
self.add_widget(self.txt1)
# add all present screen buttons to newly created screen
for i, screen in enumerate(sm.screens):
self.bt2 = (Button(text=screen.name, size_hint=(.1, .1), pos_hint={'center_x': (.1 * i + .1), 'center_y': .84}))
self.add_widget(self.bt2)
self.bt2.bind(on_release=self.transition)
# add this newly created screen button to all screens
for screen in sm.screens + [self]:
screen.bt2 = (Button(text=self.name, size_hint=(.1, .1), pos_hint={'center_x': (.1 * len(sm.screens) + .1), 'center_y': .84}))
screen.add_widget(screen.bt2)
screen.bt2.bind(on_release=screen.transition)
def transition(self, instance):
self.manager.current = instance.text
def new_tab(self, *args):
n = len(self.manager.screen_names) ##number of screens+1
screen = newtab(name="screen {}".format(n)) # make new screen and give it number
self.manager.add_widget(screen)
self.manager.current = "screen 0"
sm.add_widget(newtab(name='screen 0'))
class Application(App):
def build(self):
return sm
if __name__ == "__main__":
Application().run()
I am building an app using Kivy and would like to draw a circle to the middle of a Widget as soon as the app starts up. I found how to run code on start in this question (How do I run a function once the form is loaded Kivy). However, as a comment points out, widths and heights are not initialized yet when calling on_start(). Does anyone know how I could do this?
I have the following code. Using this, the circle is drawn at position 50, 50, while I would like it in the middle of the Field widget.
main.py:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Color, Ellipse
from kivy.lang import Builder
from kivy.properties import ObjectProperty
class Field(Widget):
def __init__(self, **kwargs):
super(Field, self).__init__(**kwargs)
def init_circle(self):
with self.canvas:
Color(1,1,1,1, mode='rgba')
r = 20
self.circle = Ellipse(pos=(self.width//2 - r, self.height//2 - r), size=(2*r, 2*r))
print(self.circle)
def move_circle(self):
cx, cy = self.circle.pos
self.circle.pos = (cx + 10, cy)
class RootClass(Widget):
field = ObjectProperty(None)
def __init__(self, **kwargs):
super(RootClass, self).__init__(**kwargs)
class MyMainApp(App):
def build(self):
self.r = RootClass()
return self.r
def on_start(self, **kwargs):
self.r.field.init_circle()
if __name__ == '__main__':
Builder.load_file("my.kv")
MyMainApp().run()
my.kv:
<RootClass>
field: field_id
BoxLayout:
size: root.size
orientation: "vertical"
Field:
id: field_id
size_hint: 1, 0.9
Button:
size_hint: 1, 0.1
text: "Move"
on_press:
root.field.move_circle()
Method 1:
Using bind by binding to a callback method,
class Field(Widget):
def __init__(self, **kwargs):
super(Field, self).__init__(**kwargs)
# Bind a callback method, say here 'init_circle' to the prop. 'size' and 'pos'
# so that whenever those prop. change the method will be called.
# To prevent redrawing you may use method 'clear' or some other strategy.
self.bind(size = self.init_circle, pos = self.init_circle)
def init_circle(self, *args):
with self.canvas:
...
Method 2:
Using Clock by scheduling the process,
def on_start(self, **kwargs):
Clock.schedule_once(self.r.field.init_circle)
How do I access a widget from screen1 and make changes to it in screen2 and also return screen1 back to it initial state(like I just run the code).
I have commented out some code that is not working.
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class ScreenManagement(ScreenManager):
def __init__(self, **kwargs):
super(ScreenManagement, self).__init__(**kwargs)
class Screen2(Screen):
def __init__(self, **kwargs):
super(Screen2, self).__init__(**kwargs)
self.retry = Button(text='retry', font_size=15, size_hint=(.26, .26),
pos_hint={'center_x': .5, 'center_y': .32}, on_press=self.retrying,
background_color=(0, 0, 1, 1))
self.add_widget(self.retry)
def retrying(self, *args):
self.manager.current = 'screen1'
# it should change the text in screen1 to "i am back to screen1, thanks you"
#self.welc.text=" i am back to screen1, thank you"
# it should change the button color back to it normal state
#self.goto.background_color='normal state'
class Screen1(Screen):
def __init__(self, **kwargs):
super(Screen1, self).__init__(**kwargs)
self.welc = Label(text='hi there welcome to my first screen', font_size=15, size_hint=(.26, .26),
pos_hint={'center_x': .5, 'center_y': .7})
self.add_widget(self.welc)
self.goto = Button(text='next screen', font_size=15, size_hint=(.2, .2),
pos_hint={'center_x': .5, 'center_y': .32}, on_press=self.going, background_color=(0, 0, 1, 1))
self.add_widget(self.goto)
def going(self, *args):
self.goto.background_color=(1,0,0,1)
self.manager.current = 'screen2'
class Application(App):
def build(self):
sm = ScreenManagement(transition=FadeTransition())
sm.add_widget(Screen1(name='screen1'))
sm.add_widget(Screen2(name='screen2'))
return sm
if __name__ == "__main__":
Application().run()
My question is this:
how do i change the text in screen1 when the retry button is pressed.
How do i return screen1 back to it initial state after the retry button is pressed so that the "next screen" button color changes back to blue
Once you have changed the current screen to screen1, then you can access that Screen as self.manager.current_screen, so your retrying() method can be:
def retrying(self, *args):
self.manager.current = 'screen1'
self.manager.current_screen.welc.text = "i am back to screen1, thanks you"
self.manager.current_screen.goto.background_color = background_color=(0, 0, 1, 1)
To set the screen1 back to its original state, you could write another method that just sets all the values back to the original value one by one. Or you could recreate screen1 by doing something like this in a method of your Application.:
def reset_screen1(self):
sm = self.root
scr1 = sm.get_screen('screen1`)
sm.remove_widget(scr1)
sm.add_widget(Screen1(name='screen1'))
I want to change some button labels of a class in a popup and retain the new label after reopening the popup. If you run my app, you can see that after pressing "Press me" button, a popup appears with a button with a label "Default", after pressing on it, it changes the label to "New". I want to be able to close the popup, press the "Press me" button and see the button in a popup with a label "New".
My .py file
from kivy.uix.floatlayout import FloatLayout
from kivy.core.window import Window
from kivy.uix.popup import Popup
from kivy.app import App
from kivy.uix.widget import Widget
import time
Window.clearcolor = (1, 1, 1, 1)
Window.size = (800, 480)
class MyGrid(Widget):
def btn(self):
show_popup(T, "Window")
class T(FloatLayout):
pass
def show_popup(tab, name):
show = tab()
popupWindow = Popup(title = name, content = show, size_hint = (None,None), size = (800,384), auto_dismiss = True)
popupWindow.open()
return popupWindow
class TimeApp(App):
def build(self):
return MyGrid()
if __name__ == "__main__":
TimeApp().run()
my .kv file
<MyGrid>
Button:
text: "Press me"
on_press: root.btn()
<T>:
Button:
pos_hint: {"center_x": 0.5, "center_y": 0.5}
text: "Default"
on_press: self.text = "New"
One way that you can do it, is by keeping a reference to the popup.
The py side:
Window.clearcolor = (1, 1, 1, 1)
Window.size = (800, 480)
class MyGrid(Widget):
def __init__(self, **kwargs):
super(MyGrid, self).__init__(**kwargs)
self.popupWindow = Popup(content=T(), size_hint=(None, None),
size=(800, 384), auto_dismiss=True)
def btn(self):
self.show_popup("Window")
def show_popup(self, name):
self.popupWindow.title = name
self.popupWindow.open()
class T(FloatLayout):
pass
class TimeApp(App):
def build(self):
return MyGrid()
if __name__ == "__main__":
TimeApp().run()
While I run this script rippleexample2.py with rippleexample2.kv, the buttons should have ripple effects upon pressing, but it doesn't work.
I know the RippleButton class is working fine in ctmbtn.py, when a button is pressed here, there is ripple effect. I don't know what is wrong here. Perhaps binding function?
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)
class Login(Screen):
pass
class MainScreen(Screen):
pass
class ScreenManager(ScreenManager):
pass
Login = Builder.load_file("rippleexample2.kv")
class SimpleKivy4(App):
def build(self):
return Login
if __name__ == "__main__":
SimpleKivy4().run()
rippleexample2.kv
ScreenManager:
Login:
MainScreen:
<Login>:
name:"login"
RippleButton:
text:'Login'
font_size: 24
size_hint: (.4,.25)
on_release: app.root.current = "main"
<MainScreen>:
name: "main"
Button:
text: 'back'
on_release: app.root.current = "login"
ctmbtn.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import (StringProperty, NumericProperty, ObjectProperty,
ListProperty, DictProperty, BooleanProperty)
from kivy.uix.touchripple import TouchRippleBehavior
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)
class RootWidget(BoxLayout):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
self.add_widget(RippleButton(text='btn 1'))
cb = CustomBtn()
cb.bind(pressed=self.btn_pressed)
self.add_widget(cb)
self.add_widget(RippleButton(text='btn 2'))
def btn_pressed(self, instance, pos):
print ('pos: printed from root widget: {pos}'.format(pos=pos))
class CustomBtn(Widget):
pressed = ListProperty([0, 0])
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.pressed = touch.pos
# we consumed the touch. return False here to propagate
# the touch further to the children.
return True
return super(CustomBtn, self).on_touch_down(touch)
def on_pressed(self, instance, pos):
print ('pressed at {pos}'.format(pos=pos))
class TestApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
TestApp().run()
The Touch Ripple code is still experimental, and it is not visible when using it in kv language for Button widget.
Actually, the Button touch ripple animation on interaction works when using kv language. It is just not visible because the background_color (r, g, b, a) of Button widget defaults to [1, 1, 1, 1] or grey color with no transparency. Whereby 'a' (alpha compositing or transparency) is 1 i.e. not transparent.
The ripple_color defaults to [1., 1., 1., .5] or grey color with half transparency.
Solution
The temporary work around is to change the alpha compositing or transparency of Button widget to 0.5.
Please refer to my example for Kivy RippleButton