I`m trying to make a simple GUI with Kivy(1.9) using a popup to change some options from a list and save it to a db, for example. When i call popup(), Python(3.4.5) crash..
main.py:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.properties import ListProperty
from kivy.lang import Builder
Builder.load_string('''
<PopView>:
title: 'Popup'
size_hint: (.8, .8)
Button:
text: 'Save'
''')
class MainApp(App):
def build(self):
b = Button(text='click to open popup')
b.bind(on_click=self.view_popup())
return b
def view_popup(self):
a=PopView()
a.data=[1,2,3,4] #e.g.
a.open()
class PopView(Popup):
def __init__(self):
self.data = ListProperty()
def save_data(self):
#db.query(self.data)
pass
if __name__ in ('__main__', '__android__'):
MainApp().run()
Here are a couple of things.
First, if you are going to overite __init__ remember to call super
But in this simple case you dont need __init__
Then, there is no on_click event on Button. Use on_press or on_release
And last but not least: You dont need to call the method in the bind function. Only pass it (without ())
So now your example looks like this.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.properties import ListProperty
from kivy.lang import Builder
Builder.load_string('''
<PopView>:
title: 'Popup'
size_hint: (.8, .8)
Button:
text: 'Save'
''')
class MainApp(App):
def build(self):
b = Button(text='click to open popup')
b.bind(on_release=self.view_popup)
return b
def view_popup(self,*args):
a = PopView()
a.data=[1,2,3,4] #e.g.
a.open()
class PopView(Popup):
data = ListProperty()
def save_data(self):
#db.query(self.data)
pass
if __name__ in ('__main__', '__android__'):
MainApp().run()
Related
To change an attribute of a class, say font_size of the Label class, I can do add this in the kv file:
<Label>
font_size: "15sp"
How do I access the font_size attribute via code?
Your question is too vague... in what context do you want to access the attribute? If it is at the instantiation then you could do this ...
from kivy.app import App
from kivy.uix.label import Label
class TestApp(App):
def build(self):
return Label(text='hello world',
font_size= '70sp')
if __name__ == '__main__':
TestApp().run()
But if you want to access the font size of a label that is created in kV... Probably best to assign the label a NumericProperty as font size in the KV file and then change the NumericProperty as needed...
from kivy.app import App
from kivy.uix.label import Label
from kivy.properties import NumericProperty
class TestApp(App):
FontSize = NumericProperty(50)
def ChangeFont(self):
self.FontSize +=10
if __name__ == '__main__':
TestApp().run()
KV...
BoxLayout:
Button:
text: 'press me'
on_press: app.ChangeFont()
Label:
text: 'Hello'
font_size: app.FontSize
And if you want to access a specific label widget you can make the label a class attribute and then modify it's text directly, if you had many labels you could add each to a list, assign the list as a class attribute and loop over the list changing each label.font_size, the example here only has one label...
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class MyButton(Button):
pass
class TestApp(App):
L = Label(text = 'hello', font_size = 20)
def ChangeFont(self):
self.L.font_size +=10
def build(self):
B = BoxLayout()
B.add_widget(MyButton())
B.add_widget(self.L)
return B
if __name__ == '__main__':
TestApp().run()
KV...
<MyButton>:
text: 'press me'
on_press: app.ChangeFont()
As John said in the comment, use ids to identify your label.
kv file
<MyBoxLayout>
Label:
id: mylabel
font_size: '15dp'
Button:
on_release: root.change_font_size()
python file
class MyBoxLayout(BoxLayout):
def change_font_size(self):
self.ids.mylabel.font_size = '12dp'
Obviously you can do more with it than just change the size once, but thats the basic idea.
The double tap works on the first code that uses a .kv file however I don't know how to get it to work on a code that doesn't use a .kv file. The second code below is generating MDCards as posts with a title, image, and subtitle. I want to add double-tap to that image only. How can I achieve this?
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.behaviors import TouchBehavior
from kivymd.uix.button import MDRaisedButton
KV = '''
Screen:
MyButton:
text: "PRESS ME"
pos_hint: {"center_x": .5, "center_y": .5}
'''
class MyButton(MDRaisedButton, TouchBehavior):
def on_double_tap(self, *args):
print("<on_double_tap> event")
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
MainApp().run()
How can I add a double-tap to the image within the MDCard?
from kivy.core.window import Window
from kivy.uix.scrollview import ScrollView
from kivymd.uix.card import MDCard
from kivymd.uix.gridlayout import MDGridLayout
from kivymd.app import MDApp
from kivymd.uix.behaviors import TouchBehavior
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.label import MDLabel
from KivaMD.kivymd.utils.fitimage import FitImage
Window.size = (440, 760)
class MyButton(MDRaisedButton, TouchBehavior):
def on_double_tap(self, *args):
print("<on_double_tap> event")
class MainApp(MDApp):
def build(self):
blog = MDGridLayout(cols=1, spacing=40, size_hint_y=None, padding=[20,], md_bg_color=[0,0,0.1,.2])
blog.bind(minimum_height=blog.setter('height'))
for i in range(10):
post = MDCard(size_hint=(.9, None), size=(300, 300), radius=10)
container = MDGridLayout(cols=1)
post.add_widget(container)
container.add_widget(MDLabel(text="Title", halign='center'))
#ADD DOUBLE TAP TO THIS IMAGE
container.add_widget(FitImage(source="data/assets/img/placeholder.jpg", size_hint_y=None, height=200))
container.add_widget(MDLabel(text="Subtitle", halign='center'))
blog.add_widget(post)
scroll = ScrollView(size_hint=(1, None), size=(Window.width, Window.height))
scroll.add_widget(blog)
return scroll
MainApp().run()
Do it just as your show for the MyButton class:
class MyFitImage(FitImage, TouchBehavior):
def on_double_tap(self, *args):
print("MyFitImage: <on_double_tap> event")
Then use MyFitImage in place of FitImage in your code.
In Kivy Text Input, cursor moves to the text end every time when changing font_size:
from kivy.app import App
from kivy.lang import Builder
KV = """
TextInput
on_touch_down: self.font_size+=1
"""
class MyApp(App):
def build(self):
self.root = Builder.load_string(KV)
MyApp().run()
Is there a way how to fix or workaround this behavior of TextInput?
Declare a class attribute, prev_cursor in class MyApp
Implement a method reset_cursor() to restore prev_cursor to TextInput's cursor
on_touch_down event, save current cursor position
on_touch_up event, use Kivy Clock.schedule_once() to invoke method reset_cursor()
Example
main.py
from kivy.app import App
from kivy.lang import Builder
KV = """
#:import Clock kivy.clock.Clock
TextInput:
on_touch_down:
app.prev_cursor = self.cursor
self.font_size += 1
on_touch_up:
Clock.schedule_once(lambda dt: app.reset_cursor(), 0.1)
"""
class MyApp(App):
prev_cursor = ()
def build(self):
self.root = Builder.load_string(KV)
def reset_cursor(self):
self.root.cursor = self.prev_cursor
MyApp().run()
I am trying to make an APP with 2 screen:
First screen is a Button
Second screen shows a graph
When the Button of the first screen is pressed, the second screen appears with the graph.
I was able to plot the graph with 1 screen only, using matplotlib.
Here is my code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use("module://kivy.garden.matplotlib.backend_kivy")
from kivy.garden.matplotlib import FigureCanvasKivyAgg
from kivy.uix.widget import Widget
class Sensores(Screen):
pass
class Grafico(Screen):
def build(self):
box = BoxLayout()
box.add_widget(FigureCanvasKivyAgg(plt.gcf()))
return box
class Menu(ScreenManager):
pass
presentation = Builder.load_file('sensor.kv')
class sensor(App):
def build(self):
return presentation
if __name__ == "__main__":
sensor().run()
KIVY
Menu:
Sensores:
Grafico:
<Sensores>
name: 'sensores'
BoxLayout:
Button:
text: "Sensor 01"
on_release:
root.Grafico()
<Grafico>
name: 'grafico'
I expect to have the graph in the second screen.
I see two problems with your code. First, in your kv file the Button action is incorrect:
Button:
text: "Sensor 01"
on_release:
root.Grafico()
If the Button is intended to switch to the other screen, it should be:
Button:
text: "Sensor 01"
on_release:
root.manager.current='grafico'
Second, in your Grafico class you have a build() method that is never called. If you change that from:
class Grafico(Screen):
def build(self):
box = BoxLayout()
box.add_widget(FigureCanvasKivyAgg(plt.gcf()))
return box
to:
class Grafico(Screen):
def on_enter(self, *args):
box = BoxLayout()
box.add_widget(FigureCanvasKivyAgg(plt.gcf()))
self.add_widget(box)
I think you will get the desired result. The key is that the on_enter() method is called when the Grafico Screen is displayed. The method is your code, but with a self.add_widget(box) added in order to add the box to the screen. See the Screen Documentation for more info.
Thank you very much!
It works now! Follow the code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use("module://kivy.garden.matplotlib.backend_kivy")
from kivy.garden.matplotlib import FigureCanvasKivyAgg
from kivy.uix.widget import Widget
plt.plot([1,23,2,4])
plt.ylabel("alguns numeros legais")
class Sensores(Screen):
pass
class Grafico(Screen):
def on_enter(self, *args):
box = BoxLayout()
box.add_widget(FigureCanvasKivyAgg(plt.gcf()))
self.add_widget(box)
class Menu(ScreenManager):
pass
presentation = Builder.load_file('sensor.kv')
class sensor(App):
def build(self):
return presentation
if __name__ == "__main__":
sensor().run()
KV LANG
#:kivy 1.9.1
Menu:
Sensores:
Grafico:
name: 'grafico'
<Sensores>
name: 'sensores'
BoxLayout:
Button:
text: "Sensor 01"
on_release:
root.manager.current = 'grafico'
<Grafico>
name: 'grafico'
I would like to get the text of my TextInput via a StringProperty, but it does not work. I get an empty string. In the second example, I am declaring the whole TextInput as an ObjectProperty and then it does work. What is wrong with my first example?
How can a StringProperty be used to define the text inside a TextInput?
First example does not print text of TextInput
example1.py
from kivy.app import App
from kivy.base import Builder
from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
Builder.load_string("""
<rootwi>:
orientation: 'vertical'
Button:
on_press: root.print_txt()
TextInput:
text: root.textinputtext
""")
class rootwi(BoxLayout):
textinputtext = StringProperty()
def print_txt(self):
print(self.textinputtext)
class MyApp(App):
def build(self):
return rootwi()
if __name__ == '__main__':
MyApp().run()
Second example does print text of TextInput, but uses a ObjectProperty not StringProperty example2.py
from kivy.app import App
from kivy.base import Builder
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
Builder.load_string("""
<rootwi>:
txt: txt
orientation: 'vertical'
Button:
on_press: root.print_txt()
TextInput:
id: txt
""")
class rootwi(BoxLayout):
txt = ObjectProperty()
def print_txt(self):
print(self.txt.text)
class MyApp(App):
def build(self):
return rootwi()
if __name__ == '__main__':
MyApp().run()
If I set the text to sth specific, it shows up in the TextInput. (But still, cannot be printed)
from kivy.app import App
from kivy.base import Builder
from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
Builder.load_string("""
<rootwi>:
orientation: 'vertical'
Button:
on_press: root.print_txt()
TextInput:
text: root.textinputtext
""")
class rootwi(BoxLayout):
textinputtext = StringProperty()
def __init__(self, **kwargs):
self.textinputtext = 'palim'
super(rootwi, self).__init__(**kwargs)
def print_txt(self):
print(self.textinputtext)
class MyApp(App):
def build(self):
return rootwi()
if __name__ == '__main__':
MyApp().run()
If you want set and get the text using the StringProperty then you should create a bidirectional bind:
from kivy.app import App
from kivy.base import Builder
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.boxlayout import BoxLayout
Builder.load_string("""
<rootwi>:
orientation: 'vertical'
textinputtext: txt.text
Button:
on_press: root.print_txt()
TextInput:
id: txt
text: root.textinputtext
""")
class rootwi(BoxLayout):
textinputtext = StringProperty()
def __init__(self, **kwargs):
super(rootwi, self).__init__(**kwargs)
self.textinputtext = 'palim'
def print_txt(self):
print(self.textinputtext)
class MyApp(App):
def build(self):
return rootwi()
if __name__ == '__main__':
MyApp().run()
Output: