KIVY - bind function via class property? - python

If you run this code:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.lang import Builder
Builder.load_string('''
#:kivy 1.11.0
<MyGrid>:
Button:
size_hint: 0.3, 0.2
text: 'open popup'
on_release: root.openPopup()
<TestClass>
size_hint: 0.7, 0.7
Button:
id: thisButton
size_hint: 0.5, 0.5
text: root.message
on_release: root.thisOne()
on_release: root.closeme()
''')
class TestClass(Popup):
message = StringProperty('')
buttonFunction = ObjectProperty(None)
def closeme(self):
self.dismiss()
def thisOne(self):
print('success')
class MyGrid(FloatLayout):
def openPopup(self):
y = TestClass(message = 'test text')
y.open()
class DropApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
DropApp().run()
You will see I can send text to the Button via class property(TestClass.message).
But if you run the code below.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.properties import StringProperty
from kivy.properties import ObjectProperty
from kivy.lang import Builder
Builder.load_string('''
#:kivy 1.11.0
<MyGrid>:
Button:
size_hint: 0.3, 0.2
pos_hint_x: {'center_x': 0.5}
text: 'open popup'
on_release: root.openPopup()
<TestClass>
# thisButton: thisButton
size_hint: 0.7, 0.7
Button:
id: thisButton
size_hint: 0.5, 0.5
text: root.message
# text: 'OK'
on_release: root.buttonFunction()
''')
class TestClass(Popup):
message = StringProperty('')
buttonFunction = ObjectProperty(None)
def closeme(self):
self.dismiss()
def thisOne(self):
print('success')
class MyGrid(FloatLayout):
def openPopup(self):
y = TestClass(message = 'test text', buttonFunction = TestClass.closeme())
y.open()
class DropApp(App):
def build(self):
return MyGrid()
if __name__ == '__main__':
DropApp().run()
I don't know if I can specify button function to the button via class property. The error is that 'TypeError: closeme() missing 1 required positional argument: 'self''
Hope someone can help!

You need to pass the closeme() method of the instance of TestClass that you are creating. So, you can change your openPopup() method slightly:
def openPopup(self):
y = TestClass(message = 'test text')
y.buttonFunction = y.closeme
y.open()
Now, the buttonFunction refers to the closeme() method of the TestClass instance.
Another way is to take advantage of the ObjectProperty in kv. If you change the TestClass to set the buttonfunction in an __init__() method:
class TestClass(Popup):
message = StringProperty('')
buttonFunction = ObjectProperty()
def __init__(self, **kwargs):
super(TestClass, self).__init__(**kwargs)
# set the buttonfunction
self.buttonFunction = self.closeme
Then, your openPopup() method can just be:
def openPopup(self):
y = TestClass(message = 'test text')
y.open()

Related

How would I switch screens from a .py file while the screens are managed in a main .kv file?

I have three codes, I am confused with how I would navigate to the next screen in my Organize.kv from my login.py, I have the main kv file that has all the screens saved and the login python and kv file that manages the login screen, the login kv file balances two different tabs, so far I have the create tab that sends you to the create class of the login python file which is connected to mysql, how would I switch screens to the Main Menu which is in the main Organize.kv file from the login python file? Sorry if this doesn't make sense, I'm super warn out from trying to figure this out.
Anything helps, thank you
The main Organize.kv that has all the screens
#:import organize organize
#:import navigation_screen_manager navigation_screen_manager
#:import anchorlayout_with_action_bar anchorlayout_with_action_bar
#:import sharpen_skills sharpen_skills
#:import login login
<MainMenu#BoxLayout>:
orientation: "vertical"
padding: "40dp"
spacing: "20dp"
size_hint: .7, .9
pos_hint: {"center_x": .5}
Button:
text: "Organize"
on_press: app.manager.push("screen2")
Button:
text: "Journal"
on_press: app.manager.push("screen3")
Button:
text: "Sharpen Skills"
on_press: app.manager.push("SkillsExamplesTabs")
Button:
text: "Exit"
on_press: app.stop()
<MyScreenManager>:
Screen:
name: "Login"
title: "login"
LoginTabs:
Screen:
name: "MainMenu"
MainMenu:
Screen:
name: "screen2"
AnchorLayoutWithActionBar:
title: "Organize"
Screen:
name: "screen3"
AnchorLayoutWithActionBar:
title: "Journal"
Screen:
name: "SkillsExamplesTabs"
AnchorLayoutWithActionBar:
title: "Sharpen Skills"
SkillsExamplesTabs:
My login.kv file
<LoginTabs#TabbedPanel>:
do_default_tab: False
TabbedPanelItem:
text: "Create Account"
Create:
TabbedPanelItem:
text: "Login"
Login:
and finally my login.py file
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager
from kivy.uix.stacklayout import StackLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
from kivy.graphics import Color, Rectangle
import mysql.connector
import navigation_screen_manager
from kivy.uix.screenmanager import ScreenManager, Screen
from navigation_screen_manager import NavigationScreenManager
Builder.load_file("login.kv")
Builder.load_file("Organize.kv")
sm = ScreenManager()
class MyScreenManager(NavigationScreenManager):
pass
class Create(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
self.build()
def build(self):
self.warning = Label(text="", pos_hint={'center_x': .5, 'center_y': .5})
user_label = Label(text="Username", size_hint=(.5, .5),
pos_hint={'center_x': .5, 'center_y': .5})
self.user_textinput = TextInput(text='Enter Username')
pas_label = Label(text="Password", size_hint=(.5, .5),
pos_hint={'center_x': .5, 'center_y': .5})
self.pas_textinput = TextInput(text="enter Password")
enter_button = Button(text="Create")
with user_label.canvas:
Color(0, .25, 1, .25)
enter_button.bind(on_press=self.Add)
self.add_widget(self.warning)
self.add_widget(user_label)
self.add_widget(self.user_textinput)
self.add_widget(pas_label)
self.add_widget(self.pas_textinput)
self.add_widget(enter_button)
def Add(self, btn):
print("Working")
t1 = self.user_textinput.text
t2 = self.pas_textinput.text
print(f"T1 = {t1} and {t2}")
conn = mysql.connector.connect(
user='root',
password='*******',
host='127.0.0.1',
database='Usersdb')
mycursor = conn.cursor()
try:
sql = "INSERT INTO user_info (user_name, user_pass) VALUES (%s, %s)"
val = (t1,t2)
mycursor.execute(sql,val)
conn.commit()
except:
self.warning = "Username Already Taken"
class Login(StackLayout):
pass
And finally my App class
from kivy.app import App
from kivy.uix.label import Label
from kivy.metrics import dp
from kivy.properties import StringProperty, BooleanProperty, ObjectProperty
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.stacklayout import StackLayout
from kivy.uix.widget import Widget
from navigation_screen_manager import NavigationScreenManager
class MyScreenManager(NavigationScreenManager):
pass
class Organizer(App):
manager = ObjectProperty(None)
def build(self):
self.manager = MyScreenManager()
return self.manager
Organizer().run()
And my Navigation Screen Manager
from kivy.uix.screenmanager import ScreenManager
class NavigationScreenManager(ScreenManager):
screen_stack = []
def push(self, screen_name):
self.screen_stack.append(self.current)
self.transition.direction = "left"
self.current = screen_name
def pop(self):
if len(self.screen_stack) > 0:
screen_name = self.screen_stack[-1]
del self.screen_stack[-1]
self.transition.direction = "right"
self.current = screen_name
Good day. You must access your screen manager in order to set the current screen which is your active screen. In your App class, I recommend saving your screen manager as an attribute via the on_start function.
class MyApp(App):
def on_start(self):
screen_manager = self.root
From there, you could set your current screen using callback methods in kivy or python.
#in kv
on_release: setattr(app.screen_manager, 'current', 'the next screen name')
#in python
def set_screen_callback(self, instance *args):
app.screen_manager.current = "the next screen name"
Your post does not include your App class, so a specific answer is impossible. The fact that you have multiple kv files is irrelevant once those kv files are loaded. In general, you just set the current property of the ScreenManager to the name of the Screen that you want displayed.
Again, the fact that you have multiple kv files is irrelevant once those kv files are loaded. So in any of the kv files, I believe that you should be able to use:
app.manager.push("MainMenu")
or:
app.manager.current = "MainMenu"
to get to the MainMenu Screen.

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/kivy : AttributeError: 'int' object has no attribute 'replace' in python

I have 2 file test.py and test.kv. When I run test.py and pass numeric value in self.abc.text=10 then it gives error
File "/usr/lib/python2.7/dist-packages/kivy/uix/textinput.py", line 2930, in _set_text
text = text.replace(u'\r\n', u'\n')
AttributeError: 'int' object has no attribute 'replace'
If I pass string value then it's working. I think text for string value but I don't what is for numeric value?
test.py
import kivy
kivy.require('1.9.0') # replace with your current kivy version !
import sqlite3 as lite
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import BooleanProperty, ListProperty, StringProperty, ObjectProperty, NumericProperty
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.core.window import Window
from kivy.uix.label import Label
#Window.maximize()
from kivy.clock import Clock
from kivy.uix.treeview import TreeView, TreeViewLabel, TreeViewNode
Window.size = (500, 530)
class GroupScreen(Screen):
groupName = ObjectProperty(None)
popup = ObjectProperty(None)
abc = ObjectProperty(None)
def display_groups(self, instance):
self.abc.text=10
class Group(App):
def build(self):
self.root = Builder.load_file('test.kv')
return self.root
if __name__ == '__main__':
Group().run()
test.kv
#:kivy 1.10.0
<CustomLabel#Label>:
text_size: self.size
valign: "middle"
padding_x: 5
<SingleLineTextInput#TextInput>:
multiline: False
<GreenButton#Button>:
background_color: 1, 1, 1, 1
size_hint_y: None
height: self.parent.height * 0.150
GroupScreen:
groupName: groupName
abc:abc
GridLayout:
cols: 2
padding : 30,30
spacing: 10, 10
row_default_height: '40dp'
CustomLabel:
text: 'Number'
SingleLineTextInput:
id: abc
CustomLabel:
text: 'Test'
SingleLineTextInput:
id: groupName
on_text: root.display_groups(self)
GreenButton:
text: 'Ok'
GreenButton:
text: 'Cancel'
Use NumericProperty and then str(root.abc) in kv.
Try this example:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty
class MyBoxLayout(BoxLayout):
abc = NumericProperty(0)
def set_text(self):
self.abc = 42
KV = """
MyBoxLayout:
Button:
text: str(root.abc)
on_release:
root.set_text()
"""
class Testapp(App):
def build(self):
root = Builder.load_string(KV)
return root
Testapp().run()
You need to type self.abc.text = str(rows[1]) in order for it to be passed as the correct type.
Hope this helps!

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

AsyncImage refresh in kivy/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:

Categories