Kivy - Why Label does not update? - python

I want to change a text of a label but I can't do it, I can see it changing on the shell but not on the UI. I even directly change the text of the label by referencing its id but still its not updating. Anyone knows how to do this?
class MainApp(Screen, EventDispatcher):
title = "Top 10 Plays of 2015"
def __init__(self,*args,**kwargs):
super(MainApp, self).__init__(*args, **kwargs)
def change_curr_title(self, title, *args):
self.title = title
self.ids.lblTitle.text = self.title
print(self.ids.lblTitle.text)
pass
class OtherVideos(BoxLayout, EventDispatcher):
def __init__(self, *args, **kwargs):
super(OtherVideos,self).__init__(*args, **kwargs)
self.loadVideos()
def loadVideos(self):
self.clear_widgets()
con = MongoClient()
db = con.nba
vids = db.videos.find()
vidnum = 1
for filename in vids:
myid = "vid" + str(vidnum)
getfilename = filename['filename']
button = Button(id=myid,
text=getfilename,
color=[0,0.7,1,1],
bold=1)
button.bind(on_release=partial(self.change_Title, getfilename))
self.add_widget(button)
vidnum += 1
def change_Title(self, title, *args):
main = MainApp()
main.change_curr_title(title)
This is the construction of my kivy:
<MainApp>:
....
BoxLayout:
....
BoxLayout:
....some widgets
BoxLayout:
OtherVideos:
...this is where the buttons are generated...
BoxLayout:
Label:
id: lblTitle
text: root.title
Is there anyway to upload my whole code on this? like the file itself, so you guys can look at it.
EDIT: I can easily update the label when I'm making a new method like this without a parameter and binding it to a button through kivy
def update_label(self):
self.ids.lblTitle.text = "New Title"
I don't know why buttons with events created dynamically doesn't work.

Here:
def change_Title(self, title, *args):
main = MainApp() # !
main.change_curr_title(title)
you are creating a new object of screen (MainApp), which isn't connected to anything. To make it work, main should link to the existing instance of MainApp screen.
The OtherVideos box layout needs to have a reference to it, preferably in kv file.
Edit
In order to create a link from MainApp to OtherVideos, create an ObjectProperty:
class OtherVideos(BoxLayout):
main = ObjectProperty()
def __init__(self, *args, **kwargs):
super(OtherVideos,self).__init__(*args, **kwargs)
self.loadVideos()
...
which will be populated in kv file:
OtherVideos:
main: root
...this is where the buttons are generated...
Then, in the change_Title function, use this reference:
def change_Title(self, title, *args):
self.main.change_curr_title(title)

Related

Stuck with Class Function in Kivy

So I 've been creating this Kivy App, but this happened
class MyScreenManager(ScreenManager):
def __init__(self, **kwargs):
super(MyScreenManager, self).__init__(**kwargs)
'''conn = sqlite3.connect('first_db.db')
c = conn.cursor()
c.execute("SELECT name FROM sqlite_master WHERE type='table';")
screens = c.fetchall()
for i in range(len(screens)):
self.add_note()
c.close()'''
self.add_note()
def add_note(self, name = ""):
self.add_widget(NoteScreen(name = name))
print(self.parent)
my .kv file
MDNavigationLayout:
x: toolbar.height
size_hint_y: 1.0 - toolbar.height/root.height
MyScreenManager:
id: screen_manager
NoteScreen:
name: "Home"
When I run the app, instead of printing out the parent of MyScreenManager which is MDNavigationLayout, it printed out "None". I don't know how to fix it. Can anybody help me?
self.parent isn't available at the time of the __init__ method because the class is first instantiated, then added to its parent.
Per documentation:
The parent of a widget is set when the widget is added to another widget
The base Widget.__init_() doesn't set self.parent.
Since Kivy uses an observable property for parent the action can be added in on_parent
def on_parent(self, instance, value):
super(MyScreenManager, self).on_parent(instance, value)
self.add_note()

Kivy: How do I add a context menu to button

I have created a custom Button class in Kivy with some callback methods, when I right click on the button I want to be able to choose from a couple of different actions:
from kivy.config import Congfig
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
from kivy.app import App
from kivy.uix import Button
class CustomButton(Button):
"""This is just for demonstration"""
def __init__(self, name='', **kwargs):
super(Service, self).__init__(**kwargs)
self.name = name
self.on_touch_down = self.mouse_click
def mouse_click(self, touch):
if self.collide_point(touch.x, touch.y):
if touch.button == 'right':
# Open context menu
pass
def callback_1(self):
# This will be called from one of the context options
pass
def callback_2(self):
# Or this will be called from a different option
pass
The code is just for demonstration purposes to express what I'm trying to achieve. Is this possible?
Thanks in advance.
In your mouse_click() method (where you have # Open context menu) you can call a custom popup menu like:
self.popup = PopMenu(touch)
self.popup.open()
With a PopMenu class that creates a ModalView, something like:
class PopMenu(object):
def __init__(self, touch):
myContent = BoxLayout(orientation='vertical')
button = Button(text='button1')
myContent.add_widget(button)
button = Button(text='button2')
myContent.add_widget(button)
button = Button(text='button3')
myContent.add_widget(button)
self.popup = ModalView(size_hint=(None, None), height=myContent.height, pos_hint={'x' : touch.spos[0], 'top' : touch.spos[1]})
self.popup.add_widget(myContent)
def open(self, *args):
self.popup.open()
And you can add your callbacks to the buttons in the PopMenu.

Kivy - Add widget(Splitter) to a GridLayout

I am trying to put a given pictures in a gridlayout that can scroll, and when i select the picture the color of the image change, here is my code:
CONTAINER_PNG = os.path.join(AllImage_ROOT, 'images')
IMAGES_NAMES = [c[:-4] for c in os.listdir(CONTAINER_PNG)]
LIST_IM = os.listdir(CONTAINER_PNG)
class ImageButton(ButtonBehavior, Image):
pass
class AllImage(BoxLayout):
# screen_manager = ObjectProperty()
def __init__(self, **kwargs):
BoxLayout.__init__(self, **kwargs)
self.orientation='vertical'
splitter = Splitter(sizable_from = 'bottom')
root = ScrollView()
layout = GridLayout(cols=4, spacing=10)
layout2 = GridLayout(cols=4, spacing=10)
button = ImageButton(source = 'mix.png')
layout2.add_widget(button)
self.add_widget(layout2)
for im in IMAGES_NAMES:
if IMAGES_NAMES != None :
btn = ImageButton(source = im+'.png')
btn.bind(on_press= lambda a:layout.add_widget( ToggleButton(text = 'work') ))
btn.bind(on_press= lambda b:self.background_color(1,1,1))
layout.add_widget(btn)
layout2.add_widget(splitter)
root.add_widget(layout)
self.add_widget(root)
class TryApp(App):
def build(self):
return AllImage()
def on_pause(self):
return True
if __name__ == "__main__":
TryApp().run()
I know i am doing things wrong, so i have several questions :
1- Why when i add a Splitter between my 2 Grids it don't work(the splitter is not visible)
2- How can i change the color of my ImageButton ?
3- The scrollview isn't working on my GridLayout, how can i custom my Grid that can be bigger than my window.
Thank you for your time :)
kivy tries to make things simple by separating the UI from the logic..from the kivy docs,it says You must deactivate at least one of the size_hint instructions (x or y) of the child to enable scrolling..
<AllImage>:
orientation:'vertical'
ScrollView:
do_scroll_x:False
GridLayout:
cols:4
spacing:10
size_hint_y:None
height: 8*dp(80)
for clarity sake,try to implement the UI stuffs in a kv file to make things easier to read.
btn.bind(on_release= lambda a:layout.add_widget( ToggleButton(text = 'work') ))
btn.bind(on_press= lambda b:self.background_color(1,1,1))
i dont think the on_press can handle two methods at the sametime,so try this

Inheritance in Variable Value - Python

Well, I'm having trouble in a specific part of my code. It uses kivy, however I'm pretty sure there is a python solution. Here is the thing: I'll have a button, when pressed will take me to another screen, when it's pressed, calls method vai, that changes, or should change the string variable value that was created in init method. Afterwards, when another screen shows up, its button receives that CHANGED variable at text parameter. But the real issue is that by the time second screen appears, the button text does not change, remaining the value I set up on init once, not the changed value on vai method.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
class Principal(App):
def build(self):
return SM()
class SM(ScreenManager):
def __init__(self):
super(SM, self).__init__()
self.add_widget(Um())
self.current = 'TelaUm'
class Um(Screen):
def __init__(self):
super(Um, self).__init__()
self.name = 'TelaUm'
self.add_widget(UmC())
class UmC(FloatLayout):
def __init__(self):
super(UmC, self).__init__()
self.btnSelecionado = 'qualquer_merda'
self.btn = Button(id = 'Bosta!', text = 'Bosta!', pos_hint = { 'center_x' : .5, 'center_y' : .5 }, size_hint = (None, None))
self.btn.bind(on_press = self.vai)
self.add_widget(self.btn)
def vai(self, instance):
self.parent.parent.add_widget(Dois())
self.parent.parent.current = 'TelaDois'
self.btnSelecionado = instance.id
class Dois(Screen):
def __init__(self):
super(Dois, self).__init__()
self.name = 'TelaDois'
self.add_widget(DoisC())
class DoisC(UmC, FloatLayout):
def __init__(self):
super(DoisC, self).__init__()
self.btn2 = Button(text = self.btnSelecionado, pos_hint = { 'center_x' : .5, 'center_y' : .5 }, size_hint = (None, None) )
self.add_widget(self.btn2)
Principal().run()
I don't completely understand what you are trying to do. But it looks to me like the second screen is a new instance of UmC and therefore has its own value of btnSelecionado. So inevitably this new instance has the value from init since it's only the old instance that has been changed.
def vai(self, instance):
self.parent.parent.add_widget(Dois())
self.parent.parent.current = 'TelaDois'
self.btnSelecionado = instance.id
Line 2 creates a new instance and line 4 sets the value in the old instance.

Kivy: Self-updating label text

Let's say I have 3 classes: a "woking class" where stuff takes place, a label class and a class to contain them.
For example the label class could be a status bar showing the status of something going on the working class. I wish I could find a way to make the label self-update the value to show, since this value is a value of the working class being changed inside the latter.
Here I have an example code
Builder.load_string('''
<CustomLabel>
text: 'Value is {}'.format(root.value)
<WorkingClass>:
orientation: 'vertical'
Button:
text: 'Update'
on_release: root.update()
<MainLayout>
orientation: 'vertical'
''')
class CustomLabel(Label):
value = NumericProperty()
class WorkingClass(BoxLayout):
def __init__(self, *args, **kwargs):
super(WorkingClass, self).__init__(*args, **kwargs)
self.a = 5
def update(self):
self.a += 1
print(self.a)
class MainLayout(BoxLayout):
def __init__(self, *args, **kwargs):
super(MainLayout, self).__init__(*args, **kwargs)
self.workingClass = WorkingClass()
self.customLabel = CustomLabel(value=self.workingClass.a)
self.add_widget(self.customLabel)
self.add_widget(self.workingClass)
class MyApp(App):
def build(self):
return MainLayout()
if __name__ == "__main__":
MyApp().run()
Is there a way of doing it with properties or whatever? Becouse I don't want to need to manually update (sommehow) the label each time I change the value. Anyway to achieve this?
You're updating a property on WorkingClass, but that doesn't update the value on CustomLabel since you did a direct assignment instead of binding it. But yes, you can use Propertys to make everything work automatically.
In WorkingClass:
class WorkingClass(BoxLayout):
a = NumericProperty()
def __init__(self, **kwargs): ...
This makes a into a Property which you can bind to.
Then in MainLayout's constructor:
self.workingClass = WorkingClass()
self.customLabel = CustomLabel(value=self.workingClass.a)
self.workingClass.bind(a=self.customLabel.setter('value'))
The last line says: "when the value of property a on self.workingClass changes, set the value property of self.customLabel to the same value"
Alternatively, you could just add the Property to WorkingClass above, then get rid of MainLayout's constructor and use kv instead:
<MainLayout>:
orientation: 'vertical'
WorkingClass:
id: working_class
CustomLabel:
value: working_class.a # assigning one property to another in kv automatically binds

Categories