I'm trying to learn Kivy using their examples, however I'm having an issue. I'm using their button doc example:
from kivy.uix.button import Button
def callback(instance):
print('The button <%s> is being pressed' % instance.text)
btn1 = Button(text='Hello world 1')
btn1.bind(on_press=callback)
btn2 = Button(text='Hello world 2')
btn2.bind(on_press=callback)
However, the program runs and immediately closes. I assumed maybe its tkinter, where the program runs on a constant loop and you need to add something at the end so it doesn't close, but I couldn't find anything on their docs about that.
To reiterate, I don't get any errors, the file just runs, I get a very brief pop up, and then it ends. I don't get an interface.
Firstly, kivy need to loop for control all own functions. So we need a App class and have to return our layouts directly or layouts under Screen Manager. In Kivy-Button documentation, Kivy shows you only related part. So there is a no any App class or loop for control.So program runs and closes immediately because app class doesn't loop window.
If you're beginner and trying to learn kivy from documentation, you need to figure how Kivy actually works and how documentation explain things. I'm sharing this code below for you, you need to understand add-remove widgets ,set layouts,... in kivy from documentations or search for full-code examples not part.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
class TestLayout(BoxLayout):
def __init__(self, **kwargs):
super(TestLayout, self).__init__(**kwargs)
self.orientation = 'vertical'
but1 = Button(text='Button1')
self.add_widget(but1)
but2 = Button(text='Button2')
self.add_widget(but2)
class MyApp(App):
def build(self):
return TestLayout()
if __name__ == '__main__':
MyApp().run()
When you understand how it works, you should start to use Screen Manager for easily create pages, send-get values (and many things) for your applications.I hope these helps you at the beginning. Good luck.
Related
I'm trying to make an app with KivyMD/Kivy, and I'd like to change a label's text multiple times with a few seconds of interval between the changes. I initially tried to do this with time.sleep(), but this froze up the GUI completely, which made the label changes and such not work.
I've seen that wxPython has the wx.CallLater() function which (if I understand correctly) will call a certain function in some amount of time without freezing up the GUI. In this thread, people were talking about threading, but it seemed to rise another problem without fixing the initial problem, so I'm really not sure if this would work in my case.
So is threading the way to go, is there an equivalent of wx.CallLater() in Kivy, or is there another better solution to my problem?
Working test code:
from kivymd.app import MDApp
from kivy.lang import Builder
import time
KV = '''
MDScreen:
MDFillRoundFlatIconButton:
id: button
icon: 'git'
on_release: app.some_func()
'''
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def some_func(self):
for i in range(3):
self.root.ids.button.text = str(3 - i)
time.sleep(3)
self.root.ids.button.text = 'Go'
Test().run()
As #John Anderson suggested, the Clock object from kivy.clock has methods that achieve the same thing as wx.CallLater().
from kivy.clock import Clock
# to schedule an event once:
Clock.schedule_once(lambda _: some_function(), in_x_seconds)
# to schedule an event repeatedly:
Clock.schedule_interval(lambda _: some_function(), every_x_seconds)
I am writing a program in Kivy, and whenever I am trying to bind a function to a Button or any other widget I get the following problem from pylint: "Instance of 'Button' has no 'bind' member" and the lines turn red. I am completely new to Kivy and this really bugs me.
The program works perfect when I execute it, and the compiler does not seem to have any problem with my bindings. What have I done wrong? Am I missing an import or anything, or is there something wrong with my environment?
Attached you find a code snippet I wrote as an example.
from kivy.app import App
from kivy.uix.button import Button
class MainApp(App):
def build(self):
button = Button(text='Hello from Kivy',
size_hint=(.5, .5),
pos_hint={'center_x': .5, 'center_y': .5})
button.bind(on_press=self.on_press_button)
return button
def on_press_button(self, instance):
print('You pressed the button!')
if __name__ == '__main__':
app = MainApp()
app.run()
It's likely that whatever linter you are using is not able to find the bind method because it comes from cython code. You need to configure it differently (if possible), or use a different linter.
This question already has answers here:
Lambda in a loop [duplicate]
(4 answers)
Closed 3 years ago.
I have spend the last few days making an app that I want to implement on a raspberry pi coupled to a 10 inch touchscreen. I am making this app for a student housing association here in Germany.
So the idea is to have the raspberry with the touchscreen installed on/next to/on top of (not sure yet), the common fridge. The fridge only holds drinks in .5L bottles format which cost 0.80€ to 1.10€ each. We don't pay immediately, we just write down our consummations on a list.
What I am trying to do is an app with the kivy library in which I can just click on my name, get a drink and the app would save my consumptions, what I already paid and save these infos in a CSV file. In order to do that I have to generate a number of buttons dynamically (the names of the poeple on the list) with kivy, which I did. But now it seems that I cannot assign them any function when they get pressed. More precisely, the index of each button doesn't seem to have any effect. I don't really know how to explain it so let's look at some code:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
class grid(App):
button = [0 for i in range(25)]
def build(self):
main = GridLayout(cols = 5)
for i in range(25):
self.button[i] = Button(text = str(i))
self.button[i].bind(on_release =lambda x: grid.printString(str(i)))
main.add_widget(self.button[i])
return main
def printString(string):
print(string)
app = grid()
app.run()
So this isn't my actual code, it's an example that illustrates the problem. The output I get is 24 for each button I click. What I want is that each button prints it's own index in the button[] list. I have been trying to make this work for hours and I haven't found anything that worked. If anyone knows how to assign these button behaviors correctly, I would be really grateful for your input.
Declare a class CustomButton with inheritance of Button widget
Use on_touch_down event and check for collision
Assign id when creating CustomButton
Example
main.py
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
class CustomButton(Button):
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
print("Button.id=", self.id)
return True
return False
class grid(App):
def build(self):
main = GridLayout(cols=5)
for i in range(25):
button = CustomButton(id=str(i), text=str(i))
main.add_widget(button)
return main
app = grid()
app.run()
I'm new with kivy, but I have decent experience with Python and Tkinter. I'm trying to control a carousel in kivy programmatically. Essentially, I have an external python program which I want to use to automatically switch images in the carousel. To make an example, I have some code in another file:
import time
while True:
time.sleep(1)
#do something to control the carousel
and then I have my kivy app:
import kivy
from kivy.app import App
from kivy.uix.carousel import Carousel
from kivy.uix.image import AsyncImage
class CarouselApp(App):
self.srcs = ["/a/bunch.png", "/of/paths.jpg", "/to/images.png."]
def build(self):
self.carousel = Carousel(direction="right")
for i in range(0, len(self.srcs)):
src = self.srcs[i]
image = AsyncImage(source=src, allow_stretch=True)
self.carousel.add_widget(image)
return self.carousel
if __name__ == "__main__":
CarouselApp().run()
I would like to be able to control which slide is displayed in the carousel using the top code, but I'm not sure how I would go about doing that, since I can't execute anything after App.run()
I have investigated kivy's Clock module, but I'm not sure that would work for me since I want to switch slides when certain conditions are satisfied rather than on a time basis. The time example I gave is simply an example of my line of thinking.
Any help would be greatly appreciated!
I strongly suggest using a Kivy file to handle this situation. Second, AsyncImage is used when you want to use an image that will be downloaded from the Internet, and as I can see you have the images you are going to use are locally stored, so use Image instead.
I think you should implement the method on_start in the class which is extending App (CarouselApp in this case) and schedule a function using Clock as,
def on_start(self):
Clock.schedule_interval(self.my_callback, 1)
Then in the same class (CarouselApp) you should define my_callback:
def my_callback(self, nap):
if condition_is_satisfied: # this is supposed to be your condition
# control carousel
I'm using Kivy and I'm trying to setup a ScreenManager, but I don't want that ScreenManager to be the root widget in my window. Here's a test code snippet that demonstrates what I'm trying to do. (This code demonstrates my problem.)
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
class MyRootWidget(Widget):
def __init__(self, **kwargs):
super(MyRootWidget, self).__init__(**kwargs)
self.screen_manager = ScreenManager()
self.add_widget(self.screen_manager)
# self.add_widget(Label(text='label direct'))
class MyApp(App):
def build(self):
root = MyRootWidget()
new_screen = Screen(name='foo')
new_screen.add_widget(Label(text='foo screen'))
root.screen_manager.add_widget(new_screen)
root.screen_manager.current = 'foo'
for x in root.walk():
print x
return root
if __name__ == '__main__':
MyApp().run()
When I run this code, the window is blank, though I would expect that it would show the text "foo screen"?
The print of the walk() command shows that the root widget contains the screenmanager, my screen, and the label, like this:
<__main__.MyRootWidget object at 0x109d59c80>
<kivy.uix.screenmanager.ScreenManager object at 0x109eb4ae0>
<Screen name='foo'>
<kivy.uix.label.Label object at 0x109ecd390>
So that's working as I would expect.
If I uncomment the line which adds the label widget directly to the root widget, that label shows up as expected.
Also if I change the MyApp.build() method so that it returns new_screen instead of returning root, it also works in that I see the label "foo screen" on the display.
BTW, the reason I want to not have the screen manager be the root widget is because I want to be able to print messages (almost like popups) on the screen in front of whichever screen is active (even if screens are in the process of transitioning), so I was thinking I would need the screen manager not to be root in order to do that?
Also my ultimate goal is to have multiple "quadrants" in the window, each with its own screen manager, so I was thinking I needed to make sure I can show screen managers that are not the root widget in order to do this.
So if there's a better way for me to do this, please let me know. Also I do not want to use .kv files for this as I want to set this entire environment up programmatically based on other config options (which I've left out of this example.)
Overall though I wonder if anyone knows why this code doesn't work?
Thanks!
Brian
The problem is you are sticking your ScreenManager in a generic Widget. If you put it in a Layout, it will display properly, ie:
class MyRootWidget(BoxLayout):
There are several layouts available: http://kivy.org/docs/gettingstarted/layouts.html