Is it possible to schedule Kivy StringProperty set() method using Clock? - string

I would like to delay Kivy screen manager transition, which I can do for example like this:
#!/usr/bin/env python
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty
from kivy.uix.button import Button
from kivy.clock import Clock
Builder.load_string('''
#: kivy 1.9.2
#: import sm kivy.uix.screenmanager
#: set base_font_size 25
<MainWidget>:
manager: screen_manager
ScreenManager:
id: screen_manager
transition: sm.NoTransition()
Screen:
name: 'screen_one'
Button:
text: 'Screen 1, press to switch to Screen 2'
font_size: base_font_size
on_press:
root.manager.current = 'screen_two'
root.statusmsg = 'Screen 2, press to launch Armageddon'
Screen:
name: 'screen_two'
Button:
text: root.statusmsg
font_size: base_font_size
disabled: True if root.statusmsg == 'Busy launching Armageddon' else False # prevent multiple activation
on_press:
root.deliver_payload()
''')
class MainWidget(FloatLayout):
statusmsg = StringProperty()
def __init__(self, **kwargs):
super(MainWidget, self).__init__(**kwargs)
def deliver_payload(self, dt=0):
if not dt: # called via on_press
self.statusmsg = 'Busy launching Armageddon'
# launch Armageddon here
Clock.schedule_once(self.deliver_payload, 2)
else: # scheduled via Clock
self.manager.current = 'screen_one'
class MyApp(App):
def build(self):
mw=MainWidget()
return mw
if __name__ == '__main__':
MyApp().run()
However, manager.current is a Kivy StringProperty and I should be able to assign a new value to it with the set() method and perhaps schedule this with Clock e.g. Clock.schedule_once(lambda dt: self.manager.property('current').set(??, 'screen_one'), 2).
I wasn't able to find documentation for Kivy StringProperty set() method. It seems the method takes two args and the first argument needs to be of type kivy._event.EventDispatcher - according to traceback info that I get when I try some random guesses. Can anyone point to some documentation, or examples of use for the set() method?
EDIT: I realise I did not make it clear what I want to do ultimately - I am asking if it is possible to schedule the Kivy property set() method using Kivy Clock. For example, I am able to toggle a Kivy BooleanProperty like this:
if self.property('myboolean').get(self):
self.property('myboolean').set(self, False)
else:
self.property('myboolean').set(self, True)
But I was not able to schedule the method using Clock. The following line generated no errors but did not have any effect either:
Clock.schedule_once(lambda dt: self.property('myboolean').set(self, False),0)
In the case of Kivy StringProperty set() method, I was not even able to figure out what the 1st argument needed to be, so did not get as far as trying to schedule it with Clock.
FURTHER EDIT:
Although screen manager 'current' is a StringProperty according to documentation, it appears to behave somewhat differently. With a StringProperty I am able to do the following:
self.property('statusmsg').set(self, 'Armageddon now')
However, attempting something like this with manager.current does not succeed:
self.manager.property('current').set(self.manager, 'aux_screen')
The error message is:
self.manager.property('current').set(self.manager, 'screen_one')
TypeError: Argument 'obj' has incorrect type (expected kivy._event.EventDispatcher, got kivy.weakproxy.WeakProxy)
Confusing.

I stumbled across this by chance Kivy: Changing screens in screen manager with an on_press event (thank you, Tshirtman) and I now have one answer. The following change gives me what I want - the ability to schedule screen change directly (i.e. without an intermediate method to call):
def deliver_payload(self):
self.statusmsg = 'Busy launching Armageddon'
Clock.schedule_once(lambda dt: self.manager.setter('current')(self.manager,'screen_one'), 2)
So it appears that for assigning a new value to screen manager's 'current' property, the correct method to use is the 'setter' method (not the 'set' method that I was attempting to use).

You can set a new value for a StringProperty by simply doing a self.manager.current = 'some string', as you do in your code.

Related

Error Cannot create instance of abstract (non-instantiable) type `GtkBox' python3

I have error when run this code:
from gi.repository import Gtk
class MainWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="")
# box
self.box = Gtk.Box(spacing=10)
self.add(self.box)
# bacon button
self.bacon_button = Gtk.Button(label="Bacon")
self.bacon_button.connect("clicked", self.bacon_clicked)
self.box.pack_start(self.bacon_button, True, True,0)
# tuna button
self.tuna_button = Gtk.Button(label="Tuna")
self.tuna_button.connect("clicked", self.tuna_clicked)
self.box.pack_start(self.tuna_button, True, True,0)
def bacon_clicked(self, widget):
print("You clicked Bacon")
def tuna_clicked(self, widget):
print("You clicked Tuna")
window = MainWindow()
window.connect("delete-event", Gtk.main_quit)
window.show_all()
Gtk.main()
And error at output:
File "/Users/*********/Documents/Program/Tutorial/venv/lib/python3.11/site-packages/gi/overrides/__init__.py", line 319, in new_init
return super_init_func(self, **new_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: cannot create instance of abstract (non-instantiable) type `GtkBox'
Process finished with exit code 1
I currently using python3 and already install PyGObject package in PyCharm
Your code is loading GTK2, instead of GTK3; in GTK 2.x, GtkBox is indeed an abstract type.
If you want to use GTK 3.x, you will need to add:
import gi
gi.require_version('Gtk', '3.0')
at the top, before importing the Gtk namespace.
Don't use Box directly. If you want a Box that grows horizontally with pack_start, use Gtk.HBox (documented here).
If you want it to grow vertically, use Gtk.VBox (documented here).
That being said, the more modern (GTK4) way of doing things is to use a Gtk.Grid for everything.

Get the status of a checkbox from a Custom List in Kivy/KivyMD

I'm trying a custom List item based on the KivyMD's documentation examples, but I'm having a problem identifying which of my chechkboxes has been activated.
With * args I can access its state and which object it is but it prints like this:
{<__ main __. RightCheckbox object at 0x000001B1A62E6970>, False}.
The problem is that "0x000001B1A62E6970" is not a constant value (this value may change with the execution of the code) that represents the checkbox as its id does.
My minimal code KV:
KV = '''
MDCard:
orientation : 'vertical'
size_hint : (0.8,0.3)
pos_hint : {"center_x":.5,"center_y":.5}
elevation : 15
padding : 20
spacing : 30
id: box
MDList:
id: scroll
<ListItemWithCheckbox>:
IconLeftWidget:
icon: root.icon
RightCheckbox:
'''
And the MainApp and Custom Class definitions:
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivymd.app import MDApp
from kivymd.uix.list import IRightBodyTouch, OneLineAvatarIconListItem
from kivymd.uix.selectioncontrol import MDCheckbox
from kivymd.icon_definitions import md_icons
class ListItemWithCheckbox(OneLineAvatarIconListItem):
'''Custom list item.'''
icon = StringProperty("android")
def on_press(self):
print(self.text)
class RightCheckbox(IRightBodyTouch, MDCheckbox):
'''Custom right container.'''
def on_active(self, *args):
print(args)
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
icons = list(md_icons.keys())
for i in range(5):
self.root.ids.scroll.add_widget(
ListItemWithCheckbox(text=f"Item {i}", icon=icons[i])
)
MainApp().run()
What I have tried:
I have tried to give a default id for the RightCheckbox to later change it to a unique one when putting the widgets in the list and thus access their states in something like "root.ids.checkboxid" but I don't know how to put it when doing self.root.ids.scroll.add_widget (ListItemWithCheckbox (text = f "Item {i}", icon = icons [i]))
Also in the on_active method (when any checkbox is selected) of the RightCheckbox class I have tried to print various attributes such as MDCheckBox.active .icon .ids. .text but none of them prints anything to help me identify which specific checkbox has been selected
I would really appreciate if anyone can help Thanks
One way to do this is to create a reference to the ListItemWithCheckbox within the RightCheckbox like this:
<ListItemWithCheckbox>:
IconLeftWidget:
icon: root.icon
RightCheckbox:
listItem: root
Then your on_active() method can use that reference:
class RightCheckbox(IRightBodyTouch, MDCheckbox):
'''Custom right container.'''
def on_active(self, rcb, value):
print(rcb.listItem.text, 'is', value)

How to dynamically update label texts in kivy that is imported from excel file?

I am creating a questionnaire form in kivy. I have added few label widgets in my GUI. I don't want to define label texts statically in my code, instead my objective is to dynamically update label texts that is fetched from an excel file.
For example: my excel file has 2 questions:
Name of the company?
Department?
I have 2 label widgets in my GUI, and the text of widgets should be:
Name of the company?
Department?
respectively and has to be dynamically fetched from the excel file.
I encountered an error when i tried to run my code.
Questionnaire.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
import pandas as pd
class FetchData():
file = pd.read_excel("Questionnaire.xlsx")
Quest = file['QUESTIONS']
class Questions(Widget):
Data = FetchData().Quest
qvars =[]
company = ObjectProperty(None)
department = ObjectProperty(None)
qvars.append(company)
qvars.append(department)
def validate(self):
for i in range(len(self.qvars)):
self.qvars[i].text = self.Data[i]
class QuestionnaireApp(App):
def build(self):
return Questions()
if __name__=="__main__":
QuestionnaireApp().run()
Questionnaire.kv
<Questions>:
company:company
department:department
GridLayout:
cols:1
size:root.width, root.height
GridLayout:
cols:1
Label:
id:company
TextInput:
Label:
id:department
TextInput:
Button:
text:"process"
on_release: root.validate()
I am getting the following error:
File "C:/Users/pavan m sunder/virtual environments/android/Questionnaire.py", line 23, in validate
self.qvars[i].text = self.Data[i]
AttributeError: 'kivy.properties.ObjectProperty' object has no attribute 'text'
I referred to similar questions that had the same error but none matches specifically to my problem.
Using your qvars list messes things up because it's a list of the property objects, which doesn't have the right behaviour - Kivy Properties are descriptors, they only work at class level.
Instead, just access self.company or self.department in your methods.

Kivy progressbar value won't update

I'm trying to program a progress bar which counts down from 60 to 0 seconds as soon as the screen it is on opens, however I can't work out why the value of the bar isn't updating.
I believed self.ids.pb.value was a valid way to do this, which leads me to think I'm making a mistake elsewhere.
Thanks in advance.
Error
KeyError: 'pb'
AttributeError: 'super' object has no attribute '__getattr__'
.py file
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen, CardTransition
from kivy.uix.progressbar import ProgressBar
class CountDown(ProgressBar):
def count(self):
self.ids.pb.value = 60
seconds = 60
def count_it(seconds):
if seconds == 0:
return
seconds -= 1
self.ids.pb.value = seconds
Clock.schedule_once( lambda dt: count_it(seconds), 1)
Clock.schedule_once( lambda dt: count_it(60), 1)
class EasyMode(Screen):
pass
class TutorialEasy(Screen):
pass
class GIFapp(App):
countdown = CountDown()
def build(self):
return Builder.load_file("testkivy.kv")
Kv file
<TutorialEasy>:
Button:
on_release: app.countdown.count()
on_release: app.root.current = "EasyMode"
<EasyMode>:
CountDown:
id: pb
max:60
Update
Value wasn't updating once errors were fixed because I missed declaring the value in Kv lang ie.
<EasyMode>:
CountDown:
id: pb
max:60
value: app.countdown.value
The value is already in the CountDown class.
So you can set the value with self.value = seconds
If you are in the EasyMode class, it would work with self.ids.pb.value = seconds

How to read a Kivy.properties.NumericProperty value in my code at a specific point

So basically I have a kivy project in which,I have a layout that I use in many classes,as such..I have made in custom and put it in a separate file so I can just reference it from different parts of my code.
Snippet:
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string('''
<CustLayout>:
#code here
''')
class CustLayout(BoxLayout):
t_length = NumericProperty(0)
my_len = 0
print(my_len)
def __init__(self, **kwargs):
super(Silvertable,self).__init__(**kwargs)
self.bind(t_length=self.on_t_length)
#This statement is executed after all other prints
print(self.t_length)
def on_t_length(self,instance,length):
#I'd like to get kv file value before the next line
self.my_len = length
print(my_len)
My kiv file:
#:import Silvertable silvertables.Silvertable
#chunk of code
BoxLayout:
Silvertable:
t_length: 5
Here,I DO get the value but unfortunately too late.That is,I get the value after the program has finished.my_len Doesn't change it's still 0
Bind Kivy Properties
The value of my_len is 0 (zero) because the print() functions were executed first before the run() method.
The value of my_len did change from 0 to 5 as per binding on property of t_length.
Please refer to the example and trace for details.
Example
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty
from kivy.lang import Builder
Builder.load_string('''
<CustLayout>:
t_length: 5
''')
class CustLayout(BoxLayout):
t_length = NumericProperty(0)
my_len = 0
print("\tCustLayout.class level-start: my_len=", my_len)
def __init__(self, **kwargs):
super(CustLayout,self).__init__(**kwargs)
print("\nCustLayout.__init__:")
self.bind(t_length=self.on_t_length)
#This statement is executed after all other prints
print("\tself.t_length=", self.t_length)
print("\tself.my_len=", self.my_len)
def on_t_length(self, instance, length):
print("\nCustLayout.on_t_length:")
#I'd like to get kv file value before the next line
self.my_len = length
print("\tself.my_len=", self.my_len)
print("\tCustLayout.class level-end: my_len=", my_len)
class TestBindProperty(App):
def build(self):
print("\nTestBindProperty.build:")
return CustLayout()
def on_stop(self):
print("\tTestBindProperty.on_stop: my_len=", self.root.my_len)
if __name__ == "__main__":
TestBindProperty().run()
Output

Resources