AsyncImage refresh in kivy/python - python

In python2.7 + kivy1.9, I use AsyncImage like:
class Foo(BoxLayout):
..def bar(self):
....file_name=StringProperty()
..
..
....self.file_name="/../../image.png"
..
and in kivy,
BoxLayout:
..AsyncImage:
....source: root.file_name
Before the second call of function bar I change the content of image.png.
But the image displayed doesn't change.
I tried "nocache: True" after source command in kivy but it doesn't work.
How can I get the correct display everytime I call the func bar.

The problem is that you declare file_name property in each bar call. The file_name property must be a class attribute:
main.py:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
class RootWidget(BoxLayout):
image_path = StringProperty('image1.png') # <<<<<<<<
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
def change_image(self, path):
self.image_path = path
class TestApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
TestApp().run()
test.kv:
<RootWidget>:
orientation: "vertical"
AsyncImage:
id: image
source: root.image_path
BoxLayout:
size_hint_y: 0.1
Button:
text: 'Image 1'
on_press: root.change_image("image1.png")
Button:
text: 'Image 2'
on_press: root.change_image("image2.png")
EDIT
If you would like to use only one file name and change the content of the image, you need call reload method (with nocache property True):
main.py:
import os
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
class AsyncTest(BoxLayout):
img = ObjectProperty()
def show_icon(self):
self.img.source = "/sdcard/archive/icon_0.png"
def switch(self):
os.system("mv /sdcard/archive/icon_1.png /sdcard/archive/icon_0.png")
self.img.reload()
class TestApp(App):
def build(self):
return AsyncTest()
if __name__ == '__main__':
TestApp().run()
test.ḱv:
<AsyncTest>:
orientation: "vertical"
img: asyn_image
AsyncImage:
id: asyn_image
nocache: True
BoxLayout:
size_hint_y: 0.1
Button:
text: 'Show'
on_press: root.show_icon()
Button:
text: 'Switch'
on_press: root.switch()
Output:

Related

How to use variables in kivy, that are defined in another .py module (not main.py)?

I want to have all the variables for an app defined in a .py module separate from the main.py file. Then I want to use those variables inside a kivy file.
I have tried the code below with several variations and there are always errors. The module works fine for defining for example different Fonts but, when it comes to properties(ObjectProperty) it does not work.
Project structure:
KivyFolder
modules(folder)
variables.py
screens(folder)
screen1.kv
screen2.kv
main.py
main.kv
Code:
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from os import listdir
from kivy.lang import Builder
kv_screens = './screens/'
for kv in listdir(kv_screens):
Builder.load_file(kv_screens+kv)
class Screen1(Screen):
pass
class Screen2(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file('main.kv')
class MainApp(App):
import modules.variables
def build(self):
return presentation
if __name__ == '__main__':
MainApp().run()
main.kv
#:kivy 1.10.1
ScreenManagement:
Screen1:
Screen2:
screen1.kv
#:kivy 1.10.1
<Screen1>:
name: 's1'
BoxLayout:
orientation: 'vertical'
Label:
text: 'Screen 1'
font_size: app.font_size_one
Button:
text: 'Go To Screen 2'
on_press:
root.manager.current = 's2'
screen2.kv
#:kivy 1.10.1
<Screen2>:
name: 's2'
BoxLayout:
orientation: 'vertical'
Label:
text: 'Screen 2'
font_size: '20dp'
Button:
text: 'Go To Screen 1'
on_press:
root.manager.current = 's1'
variables.py
from kivy.properties import ObjectProperty
font_size_one = ObjectProperty('40dp')
font_size_two = ObjectProperty('60dp')
Now doing it like this produces this error:
AttributeError: 'NoneType' object has no attribute 'bind'
I would expect to be able to call a variable since it is imported at MainApp(App), so 'app.variable name should read it.
I have also tried:
font_size: app.variables.font_size_one
and:
font_size: app.modules.variables.font_size_one
Then I tried importing the module before everything else with the same result.
I also tried putting it into a class and calling that class in .kv like this:
variables.py
from kivy.properties import ObjectProperty
class FVArs():
font_size_one = ObjectProperty('40dp')
font_size_two = ObjectProperty('60dp')
screen1.kv
#:kivy 1.10.1
<Screen2>:
name: 's2'
BoxLayout:
orientation: 'vertical'
Label:
text: 'Screen 2'
font_size: FVars().font_size_two
Button:
text: 'Go To Screen 1'
on_press:
root.manager.current = 's1'
Error:
NameError: name 'FVars' is not defined
Thank you for your answers.
Bye
EDIT: Since the answer below works on the example code but not in my actual project I am adding the relevant project code:
Project structure:
KivyFolder
modules(folder)
global_variables.py
screens(folder)
Intro.kv
(and other screens as separate .kv files
main.py
main.kv
main.py
from kivy.app import App
from modules.global_variables import globalVariables
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.label import Label
from kivy.properties import StringProperty, NumericProperty, ObjectProperty, Property
from kivy.core.window import Window
background_color = (0.1, 0.1, 0.1, 1)
Window.clearcolor = background_color
from os import listdir
from kivy.lang import Builder
kv_screens = './screens/'
for kv in listdir(kv_screens):
Builder.load_file(kv_screens+kv)
kv_elements = './elements/'
for kv in listdir(kv_elements):
Builder.load_file(kv_elements+kv)
class IntroScreen(Screen):
user_name = StringProperty("Troll")
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("main.kv")
class MainApp(App):
title_size = globalVariables.title_size
def build(self):
self.title = 'Kivy Omnia!'
return presentation
if __name__ == "__main__":
app = MainApp()
app.run()
global_variables.py
from kivy.properties import ObjectProperty
class globalVariables():
title_size = ObjectProperty('101sp')
main.kv
#:kivy 1.10.1
ScreenManagement:
IntroScreen:
intro.kv
#:kivy 1.10.1
<IntroScreen>:
name: "intro"
BoxLayout:
orientation: 'vertical'
Label:
text: "Hello " + root.user_name + "!"
font_size: app.title_size
Error:
kivy.lang.builder.BuilderException: Parser: File
"C:\Users\Ghost\CodeProjects\phyton\kivyomnia\screens\Intro.kv", line
10: ...
8: Label:
9: text: "Hello " + root.user_name + "!"
10: font_size: app.title_size
11: font_name: "Exo-B"
12: AnchorLayout: ... BuilderException: Parser: File "C:\Users\Ghost\CodeProjects\phyton\kivyomnia\screens\Intro.kv", line
10: ...
8: Label:
9: text: "Hello " + root.user_name + "!"
10: font_size: app.title_size
11: font_name: "Exo-B"
12: AnchorLayout: ... AttributeError: 'NoneType' object has no attribute 'bind' File
"C:\Python37-32\lib\site-packages\kivy\lang\builder.py", line 249, in
create_handler
return eval(value, idmap), bound_list File "C:\Users\Ghost\CodeProjects\phyton\kivyomnia\screens\Intro.kv", line
10, in
font_size: app.title_size File "C:\Python37-32\lib\site-packages\kivy\lang\parser.py", line 75, in
getattribute
object.getattribute(self, '_ensure_app')() File "C:\Python37-32\lib\site-packages\kivy\lang\parser.py", line 70, in
_ensure_app
app.bind(on_stop=lambda instance:
File "C:\Python37-32\lib\site-packages\kivy\lang\builder.py", line
615, in _apply_rule
rctx['ids']) File "C:\Python37-32\lib\site-packages\kivy\lang\builder.py", line 254, in
create_handler
cause=tb)
So if just reposition the:
title_size = globalVariables.title_size
... inside the IntroScreen(Screen) and then inside Intro.kv:
font_size: root.title_size
... it works. But the point is to have all the global variables available across all screens.
Another question:
if app.something gets a what is inside main app class
and root.something gets what is in the root class of that file then how do you call something from another class that is not app or root. If that makes sense.
You can import your FVars class with this line:
from modules.variables import FVars
Then inside your MainApp class, you can create a class variable with the data of FVars's class variable.
class MainApp(App):
f1 = FVars.font_size_one
You can then access it on your kv files:
font_size: app.f1
Here is some basic example:
main.py
from kivy.app import App
from kivy.lang import Builder
from modules.variables import FVars
class Test(App):
f1 = FVars.font_size_one
def build(self):
return Builder.load_file("test.kv")
Test().run()
test.kv
BoxLayout:
Label:
text: "Hello"
font_size: app.f1
modules/variables.py
from kivy.properties import ObjectProperty
class FVars():
font_size_one = ObjectProperty('40dp')
font_size_two = ObjectProperty('60dp')
UPDATE
To prevent the error, you can load the kv files inside the build method.
def build(self):
self.title = 'Kivy Omnia!'
kv_screens = './screens/'
for kv in listdir(kv_screens):
Builder.load_file(kv_screens+kv)
kv_elements = './elements/'
for kv in listdir(kv_elements):
Builder.load_file(kv_elements+kv)
presentation = Builder.load_file("main.kv")
return presentation
There's a plenty of ways on how to access a variable inside a class which is neither an app nor root, you can just set the id property of that widget.
Here's a basic example:
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
class SecondClass(BoxLayout):
someNumber = 123 # Let's say we wanted to print the value of this variable when we press the button.
class FirstClass(BoxLayout):
pass
class TestApp(App):
def build(self):
return FirstClass()
TestApp().run()
test.kv
<SecondClass>:
<FirstClass>:
Button:
text: "Print the Numbah"
on_press: print(app.root.ids.secondclass.someNumber) # We can do something like this
SecondClass:
id: secondclass
We can also do something like this:
<SecondClass>:
<FirstClass>:
Button:
text: "Print the Numbah"
on_press: print(root.ids.secondclass.someNumber)
SecondClass:
id: secondclass
Or this:
<SecondClass>:
<FirstClass>:
Button:
text: "Print the Numbah"
on_press: print(secondclass.someNumber)
SecondClass:
id: secondclass
Or even this:
<SecondClass>:
<FirstClass>:
second: secondclass
Button:
text: "Print the Numbah"
on_press: print(app.root.second.someNumber)
SecondClass:
id: secondclass
And this:
<SecondClass>:
<FirstClass>:
second: secondclass
Button:
text: "Print the Numbah"
on_press: print(root.second.someNumber)
SecondClass:
id: secondclass

Scrollview to display different videos in kivy lags after a while

I'm a beginner in kivy and python. I've been trying to create a scrollview in a page that displays selected video on the screen. But after a while when i selected 5-6 videos to display even though i delete the video widget it starts to lag. And i'm open to any suggestions to better way to handle this kind of application.
from kivy.config import Config
Config.set('graphics', 'width', '1920')
Config.set('graphics', 'height', '1080')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.properties import StringProperty
from kivy.uix.scrollview import ScrollView
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.uix.button import Button
import os
from kivy.uix.video import Video
from kivy.uix.videoplayer import VideoPlayer
from kivy.clock import mainthread
from functools import partial
from kivy.core.window import Window
Window.clearcolor = (0, 0, 0, 0)
isThereVideo=False
picture_path="/home/linux/kivyFiles/kivyLogin/assets"
video_path="/home/linux/kivyFiles/kivyLogin/videoAssets/"
class Image(Image):
pass
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class Selfie(Screen):
pass
class RootWidget(Widget):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
Clock.schedule_once(self.createMultipleButton)
#mainthread
def createMultipleButton(self, dt):
self.root = Widget()
size_y=150;
size_x=150;
for i in range(1):
folderList = os.listdir(picture_path)
if len(folderList)==0:
time.sleep(1)
break
fileList = os.listdir(picture_path)
print fileList
for file in fileList:
x = (picture_path+"/"+file)
button = Button(id=str(file),text="" + str(file),size_hint=(None, None),height=size_y,width=size_x, pos_hint={'x': 0, 'y': 1},background_normal=x)
button.bind(on_release=partial(self.VideoContainer, file))
print file
self.scrollview.content_layout.add_widget(button)
print button.id
print("Parent of ScreenTwo: {}".format(self.parent))
#print(button.pos)
def VideoContainer(self,name,btn):
global isThereVideo
if isThereVideo==True:
#self.videocontainer.video_layout.unload()
self.videocontainer.clear_widgets()
mylist=name.split('.')
emailButton = Button(id='email')
video = Video(source="/home/linux/kivyFiles/kivyLogin/videoAssets/"+mylist[0]+".mp4", state='play',options={'eos': 'loop'})
video.size=(self.parent.width,self.parent.height)
video_pos=(self.parent.x,self.parent.y)
#video.pos_hint={'x': self.parent.width /2, 'y': self.parent.height/2}
video.play=True
#video.pos=(self.parent.width /2 , self.parent.height/2)
#self.videocontainer.video_layout.add_widget(emailButton)
self.videocontainer.add_widget(emailButton)
emailButton.add_widget(video)
isThereVideo=True
print("Parent of ScreenTwo: {}".format(self.parent))
return 0
class ScreenManagement(ScreenManager):
pass
class VideoContain(Widget):
pass
class ScrollableContainer(ScrollView):
pass
presentation = Builder.load_file("login.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == '__main__':
Window.fullscreen = True
app=MainApp()
app.run()
And my Kv file
ScreenManagement:
MainScreen:
Selfie:
<MainScreen>:
name: 'main'
Button:
on_release: app.root.current = 'selfie'
text: 'Another Screen'
font_size: 50
<Selfie>:
name: 'selfie'
Button:
on_release: app.root.current = 'login'
text: 'Selfie Screen'
font_size: 10
pos_hint: {"right": 1, 'top':1}
size_hint: (0.1, 0.1)
RootWidget:
<RootWidget>
size_hint: (0.1, None)
scrollview: scrollview
videocontainer:videocontainer
size:(self.parent.width, self.parent.height)
VideoContain:
id:videocontainer
##size:(self.parent.width, self.parent.height)
size:(root.width, root.height)
ScrollableContainer:
id: scrollview
size: root.size
pos: root.pos
<VideoContain>
video_layout:video_layout
FloatLayout:
cols:1
id: video_layout
<ScrollableContainer>:
scroll_timeout: 75
scroll_distance: 10
app: app
content_layout: content_layout
GridLayout:
id: content_layout
cols: 1
size_hint: (0.1, None)
pos: root.pos
height: self.minimum_height
padding: 25
spacing: 25
I posted all my code since i don't know which part may causing the problem i'm facing.
Solved it.
def VideoContainer(self,name,btn):
global isThereVideo
if isThereVideo:
# self.videocontainer.video_layout.unload()
self.videocontainer.clear_widgets()
In this part i'm clearing the widgets but i also need to unload the video somehow.
self.video.unload() solved my problem

python: function call from within a class

This is a sample program to display "Hello" or "Good-bye" when you push the button 1 or 2, simplified from my real program.
What I want to do is to execute function btn1() and display "Hello" when the program starts, without pushing the button 1.
What lines of code and where should I add?
When I put "Greetings().btn1()" in class Greetings, error: "name Greetings is not defined" occured. Then I put the same command after def build(self) in class GreetingApp, but nothing happened.
In main.py,
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
class Greetings(BoxLayout):
greeting=StringProperty()
def btn1(self):
self.greeting='Hello.'
def btn2(self):
self.greeting='Good-bye.'
class GreetingApp(App):
def build(self):
return Greetings()
GreetingApp().run()
And in greeting.kv,
#: import main main
Greetings:
<Greetings>:
orientation: "vertical"
Label:
text: root.greeting
BoxLayout:
orientation: "horizontal"
Button:
text: '1'
on_press: root.btn1()
Button:
text: '2'
on_press: root.btn2()
Just call btn1 before returning root widget:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.lang import Builder
Builder.load_string('''
<Greetings>:
orientation: "vertical"
Label:
text: root.greeting
BoxLayout:
orientation: "horizontal"
Button:
text: '1'
on_press: root.btn1()
Button:
text: '2'
on_press: root.btn2()
''')
class Greetings(BoxLayout):
greeting = StringProperty()
def btn1(self):
self.greeting = 'Hello.'
def btn2(self):
self.greeting = 'Good-bye.'
class GreetingApp(App):
def build(self):
root = Greetings()
root.btn1()
return root
GreetingApp().run()
The __init__ method runs every time you create an instance of a class.
So what ever you want to do in creation time of the object, you can put in __init__. Initiate the object.
Put this in your Greetings class.
def __init__(self, **kwargs):
super(Greetings, self).__init__(**kwargs)
self.btn1()
Then you call it when you create the object.

How to get Python variables to Kivy?

I want to get the random values generated in 'randgen' displayed as text in the button (right now the button displays string). How can I get rand_val into the .kv file?
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.clock import Clock
import random
root = Builder.load_string('''
<Demo>:
cols: 1
BoxLayout:
orientation: 'vertical'
Button:
text: 'rand_val_here'
size_hint: .2, .2
pos_hint: {'x':0, 'center_y': .1}
''')
class Demo(BoxLayout):
pass
class MainApp(App):
def build(self):
Clock.schedule_interval(self.randgen, 0.01)
return Demo()
def randgen(dt, self):
rand_val = random.randint(0, 10)
print(rand_val)
if __name__ == '__main__':
MainApp().run()
The arguments and names of some functions are not correct, I think they are caused because you have tried to copy and remove the secondary part but remember that the order is interesting in python.
If you want to assign a property to a Widget you must first obtain it and for them an id must be placed, this id will be the name of the variable that will be created and assigned, we can access it through ids as I show below:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.clock import Clock
import random
root = Builder.load_string('''
<Demo>:
cols: 1
BoxLayout:
orientation: 'vertical'
Button:
id: btn
size_hint: .2, .2
pos_hint: {'x':0, 'center_y': .1}
''')
class Demo(BoxLayout):
def __init__(self, *args, **kwargs):
BoxLayout.__init__(self, *args, **kwargs)
Clock.schedule_interval(self.randgen, 0.01)
def randgen(self, dt):
rand_val = random.randint(0, 10)
self.ids.btn.text = str(rand_val)
print(rand_val)
class MainApp(App):
def build(self):
return Demo()
if __name__ == '__main__':
MainApp().run()
There are two methods of solving this. The method 1 is using ObjectProperty, and the method 2 is using StringProperty.
Method 1 - Using ObjectProperty
In this example, an ObjectProperty is used to hook up to the button because an id is a weakref to the widget. Using ObjectProperty creates a direct reference, provides faster access and is more explicit.
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.clock import Clock
import random
root = Builder.load_string('''
<Demo>:
btn: btn
orientation: 'vertical'
Button:
id: btn
text: 'rand_val_here'
size_hint: .2, .2
pos_hint: {'x':0, 'center_y': .1}
''')
class Demo(BoxLayout):
btn = ObjectProperty(None)
def __init__(self, **kwargs):
super(Demo, self).__init__(**kwargs)
Clock.schedule_interval(self.randgen, 0.01)
def randgen(self, dt):
self.btn.text = str(random.randint(0, 10))
class MainApp(App):
title = "Updating Button's Text - Using ObjectProperty"
def build(self):
return Demo()
if __name__ == '__main__':
MainApp().run()
Output
Method 2 - Using StringProperty
Without changing much of your original app, the solution is as follow:
Since your root widget class, Demo is a BoxLayout, therefore the attribute cols: 1 which is only applicable to GridLayout is not required in the kv file
Declare rand_val of type StringProperty
Populate the button's text using app.rand_val
Note:
Your app has nested BoxLayout.
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.lang import Builder
from kivy.clock import Clock
import random
root = Builder.load_string('''
<Demo>:
BoxLayout:
orientation: 'vertical'
Button:
text: app.rand_val
size_hint: .2, .2
pos_hint: {'x':0, 'center_y': .1}
''')
class Demo(BoxLayout):
pass
class MainApp(App):
rand_val = StringProperty("")
def build(self):
Clock.schedule_interval(self.randgen, 0.01)
return Demo()
def randgen(self, dt):
self.rand_val = str(random.randint(0, 10))
print(self.rand_val)
if __name__ == '__main__':
MainApp().run()
Output

How do I initialise CheckBox state in kivy

I want to initialise the value of a CheckBox in kivy from the python code
I have tried (see example) but it doesn't work. Can anyone please help?
import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty
from kivy.properties import StringProperty
class MainScreen(BoxLayout):
BlueText = StringProperty()
Blue = BooleanProperty()
Red = BooleanProperty()
UseColours = BooleanProperty()
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.BlueText='Blue'
self.UseColours=True
self.Blue=False
self.Red=True
def doBlue(self,*args):
pass
def doRed(self,*args):
pass
def doUseColours(self,*args):
pass
class BasicApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
BasicApp().run()
My kv file tries to pick up whether or not the boxes are checked by setting 'value'. Is this right?
MainScreen:
<MainScreen>:
orientation: "vertical"
GridLayout:
cols: 2
Label:
text: root.BlueText
CheckBox:
group: 'colours'
value: root.Blue
on_active: root.doBlue(*args)
Label:
text: "Red"
CheckBox:
group: 'colours'
value: root.Red
on_active: root.doRed(*args)
Label:
text: "Use colours"
CheckBox:
value: root.UseColours
on_active: root.doUseColours(*args)
Use active property:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string('''
<MyWidget>:
CheckBox:
active: False
CheckBox:
active: True
''')
class MyWidget(BoxLayout):
pass
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()
From Python code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty
from kivy.lang import Builder
Builder.load_string('''
<MyWidget>:
CheckBox:
active: root.is_active
CheckBox:
active: not root.is_active
Button:
text: 'toggle'
on_press: root.toggle()
''')
class MyWidget(BoxLayout):
is_active = BooleanProperty(False)
def toggle(self):
self.is_active = not self.is_active
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()

Categories