Kivy: Multiple items in a popup wont work - python

I am trying to use multiple items in a popup with kivy in python. I would
like to find out how to make this work. I am not sure if it has to do with the fact
that the script is on my phone and not made for computer.
Here is a little example script that I am asking you to help make work for future reference.
import kivy
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
popup = Popup(title='Test popup',
content=Label(text='Hello world'),
TextInput(text='Hi'), #Here is what I am trying to make work
size_hint=(None, None), size=(400, 400))
So you can see it is two objects in one content of the popup. I am sure this is possible
because I've seen it on kivy apps in the appstore, but not sure how to do it myself.

The content of a Popup can only be a single widget. You cannot add two widgets like you're trying to do.
To accomplish what you're trying to do you'll have to add the label and text input to e.g. a boxlayout and then add the boxlayout to content. Here's an example that should work:
from kivy.uix.popup import Popup
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
box = BoxLayout()
box.add_widget(Label(text='Hello world'))
box.add_widget(TextInput(text='Hi'))
popup = Popup(title='Test popup', content=box, size_hint=(None, None), size=(400, 400))

You can achieve this using kv file.
:
orientation:'vertical'
Label:
text: 'Hello World'
Button:
text: 'Press Me'
In python file:
def openPop(self):
self.pop = Popup(title='Test',content=Content(),auto_dismiss=True)
self.pop.open()

you need to do this:
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.super_box = BoxLayout(orientation = "vertical")
self.pn = TextInput(text = "projectname") # TODO: Project name input
self.super_box.add_widget(self.pn)
"""Create button: Creates a new project repo"""
cb = Button(text="Create")
cb.bind(on_release = lambda x: print("a new project repo gets created"))
self.super_box.add_widget(cb)

Related

Kivy: Change popup title label proprieties

I'm writing an interface with Kivy.
I've added a change in my .kv file that remove size_hint from Label
<Label>
size_hint: None, None
This change reflects itself obviously on all labels, included the labels of all buttons and, my problem, the label of the popup title.
In fact, label of the popup title remains little and text goes to newline after few letters (you can see it easily by creating a popup with a long title and set the Label with size_hint: None, None)
I could write a custom class like MyLabel#Label but this will not reflects on all buttons, and I should rewrite all buttons, and all place where labels are used but not explicitly declared by the code.
That's why I'm looking for another way to accomplish this.
There's a way to tell to kivy, in .kv file or in my python main.py, to use a different label class for my popup title? or to set the size_hint property of the label used in the popup title?
Thanks for Your Attention,
Best Regards.
[EDIT]
Screenshot of the running example problem
Example:
my.kv
<Label>
size_hint: None, None
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.core.window import Window
def ForceLimit():
pop = Popup(title='Force limit reached!',content=Label(text='Force limit reached! Decremental movement inhibited.'),size_hint=(None, None), size=(400, 400))
pop.open()
kv = Builder.load_file("my.kv")
class MyMainApp(App):
def build(self):
#Window.borderless = True
Window.size = (1024,600)
return ForceLimit()
if __name__ == "__main__":
MyMainApp().run()

Kivy Python - Button to increment variable, without using NumericProperty

I'm a complete novice to both python and kivy having learnt python from codeacademy about a week ago and kivy from youtube tutorials.
Could someone please explain to me why the code below does not result in a screen with a label displaying n, which is incremented by the button?
Python file
import kivy
from kivy.app import App
from kivy.uix.button import Button, Label
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty
class Example(BoxLayout):
n = 0
def n_plus(self):
self.n += 1
class ExampleApp(App):
def build(self):
return Example()
example = ExampleApp()
example.run()
KV file
<Example>:
BoxLayout:
Label:
text: str(root.n)
Button:
text: "+1"
on_press: root.n_plus()
Then could you explain why making n = NumericProperty(0) makes this work?
I'd like to run some functions on n which don't seem to work on numeric properties.
Because when you use NumericProperty()
As the official document said:
It produces events such that when an attribute of your object changes,
all properties that reference that attribute are automatically
updated.
So, in short, it creates a binding relationship between your UI(.kv) and attribute of its class(.py)
But, actually, you can modify the UI by yourself without the help from the kivy framework. I changed your example as the following:
Add an id attribute to your widget
Access the id attribute by using self.ids.your_id_in_kv_file
But it's obviously not good, since now you need to update your UIby yourself everytime you want to update your UI. But with that XXXXProperty from kivy, you just need to change the value of that attribute, you don't need to worry about the UI at all.
Another disadvantage of the solution is that, when you need to change the UI, you need to change tons of code if you modify them all by yourself...
Here is the example:
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.lang.builder import Builder
from kivy.uix.boxlayout import BoxLayout
Builder.load_string("""
<Example>:
BoxLayout:
Label:
id: lbl
text: "0"
Button:
text: "+1"
on_press: root.n_plus()
""")
class Example(BoxLayout):
def n_plus(self):
value = self.ids.lbl.text
self.ids.lbl.text = str(int(value) + 1)
class ExampleApp(App):
def build(self):
return Example()
if __name__ == '__main__':
ExampleApp().run()

Adding a new Button on release of another button in Kivy

I'm trying to have a button where if it is clicked it will insert a new button. I cant get the bind to return the new button.
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.scatter import Scatter
from kivy.uix.button import Button
class app(App):
def build(self):
layout = FloatLayout()
button1 =Button(text="test",pos=(385,450),size_hint=(.1,.1))
button1.bind(on_release=self.btn2)
layout.add_widget(button1)
return layout
def btn2(self, event):
print "worked"
layout = FloatLayout()
btn3 = Button(text="worked",size=(.1,.1),pos=(380,400))
layout.add_widget(btn3)
return layout
app().run()
You are creating another instance of FloatLayout in the btn2 event with layout = FloatLayout(), however that instance isn't anywhere else, but in the btn2 method - i.e. you added a Button to FloatLayout, but that layout isn't visible and after the function ends it's highly possible that even doesn't exist, because it's garbage-collected by Python.
You can either use partial to pass the already existing instance of FloatLayout (where your previous Button is) like this if you need only one layout:
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
from functools import partial
class app(App):
def build(self):
layout = FloatLayout()
button1 =Button(text="test",pos=(385,450),size_hint=(.1,.1))
button1.bind(on_release=partial(self.btn2, layout))
layout.add_widget(button1)
return layout
def btn2(self, layout, *args):
print "worked"
btn3 = Button(text="worked",size=(.1,.1),pos=(380,400))
layout.add_widget(btn3)
app().run()
Or you can use the instance that's passed to method arguments from the event (the instance of a widget that dispatched the event):
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
class app(App):
def build(self):
layout = FloatLayout()
button1 =Button(text="test",pos=(385,450),size_hint=(.1,.1))
button1.bind(on_release=self.btn2)
layout.add_widget(button1)
return layout
def btn2(self, button):
layout = button.parent # parent of the button is the "layout" from build()
btn3 = Button(text="worked",size=(.1,.1),pos=(380,400))
layout.add_widget(btn3)
app().run()
In both cases you only need to find the right instance to work with, not create another one that's not even used. Also return layout isn't necessary if you don't expect on_release to work with the layout variable (which it won't).

Extending the Kivy ScrollView contents down

I have a FloatLayout as a child of a ScrollView with size_hint_y set to None. I want to be able to extend it as I add more and more content. The problem is that since Kivy's coordinate system starts at the bottom-left, when I add to the FloatLayout height, all the content stays at the bottom. Can I somehow make it extend down? Because I don't think that moving all widgets up is efficient, especially if there's a lot of them and I need to handle the position of all children as well.
Here is a snippet that explains the problematic behaviour:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
class TestApp(App):
def extend_h(self, *args):
global msg_float
msg_float.height += 50
def build(self):
global msg_float
msg_float = FloatLayout(size_hint_y = None)
bt1_main = Button(on_press = self.extend_h)
bl = BoxLayout()
sc = ScrollView()
sc.add_widget(msg_float)
bl.add_widget(sc)
bl.add_widget(bt1_main)
lb = Label(text = "Test",
size=(100,200),
size_hint = (None, None))
msg_float.add_widget(lb)
return bl
TestApp().run()
With a press of a button, the view extends and the "Test" label stays at the bottom, but I'd want it to stay on top.
You could use a relative layout instead of a float layout to fix the coords, but instead you should just omit using any of these, and add labels to a grid layout. Check examples at kivy repo:
https://github.com/kivy/kivy/blob/master/examples/widgets/scrollview.py
https://github.com/kivy/kivy/blob/master/examples/widgets/scrollview.kv

Loading one line of text from file to Kivy label

I want to make a simple program that is just showing definitions that are stored in text file.One label and button to show next definition. I try to do it with documentation but i cannot find how to load text into label. Can someone show me to some good resources or code samples ?
My code for now (i want to build in on top of example from kivy website):
import kivy
kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.label import Label
class MyApp(App):
def build(self):
return Label(text = 'Hello world')
if __name__ == '__main__':
MyApp().run()
The easiest way to update widgets in the UI are by binding to their properties. This can be done in code, but the real power of kivy in my opinion comes from using it's declarative UI language. Using kv, you get automatic binding.
Here is a quick example of what you might do:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty
kv = '''
BoxLayout:
orientation: 'vertical'
Label:
text: app.text
Button:
text: 'click me'
on_press: app.clicked()
'''
class MyApp(App):
text = StringProperty("hello world")
def build(self):
return Builder.load_string(kv)
def clicked(self):
self.text = "clicked!"
if __name__ == '__main__':
MyApp().run()
In the kv description of the UI, you tell kivy that you want the text on the Label to be bound to a StringProperty on the app which you defined on the class. The auto-binding means that anytime you set a value to that property (like in the clicked function), the UI will update with the new value automatically.

Categories