kv Language id linked with another class Object as ObjectProperty kivy - python

I am trying to update a label text from another class using its update method in Clock but I couldn't understand why its not updating the label properly .I have a sample code below :
gui_v9 = '''
#:import Clock kivy.clock.Clock
<Level_1>:
on_enter: self.EnterLevel_1()
<ScoreBar>:
time_Label: timelabel
GridLayout:
rows: 4
cols: 1
size: root.size
#Space away from border
padding: 2
spacing: 10
canvas:
Color:
rgba: 204/255.0, 204/255.0, 0/255.0, 1
Rectangle:
# self here refers to the widget i.e FloatLayout
pos: self.pos
size: self.size
Button:
text: 'Score'
size_hint: .5, .5
Label:
text: "Level 1"
Label:
text: "Time :"
id: timelabel
Button:
text: 'Mute'
'''
class ScoreBar(Widget):
time_Label = ObjectProperty(None)
def __init__(self):
super(ScoreBar, self).__init__()
class Level_1(Screen,Widget):
def __init__(self, **kwargs):
super(Level_1, self).__init__(**kwargs)
self.layout = GridLayout(cols=2,spacing=(10),padding=10)
def EnterLevel_1(self):
print "Hi This is EnterLevel_1 . Level One Gui work area "
scoreBar = ScoreBar()
Field = tama(speed=3)
self.layout.add_widget(Field)
self.layout.add_widget(scoreBar)
self.add_widget(self.layout)
Clock.schedule_interval(Field.update, 10.0/100)
#Field
class tama(Widget):
def __init__(self, speed=1 ):
super(tama, self).__init__()
self.speed = speed
self.id = "Field"
self.size = (800,600)
self.Extra = 200
print ScoreBar().time_Label.text
def update(self,dt):
print ScoreBar().time_Label.text
ScoreBar().time_Label.text ="cdfdfd"
# Create the screen manager
Builder.load_string(gui_v9)
sm = ScreenManager()
sm.add_widget(Level_1(name='level_1'))
class MyJB(App):
def build(self):
return sm
if __name__ == '__main__':
MyJB().run()

The issue is that you have lines like
print ScoreBar().time_Label.text
This doesn't tell you anything about the existing ScoreBar, it makes a new one and returns information about that.
From the tama, you could refer to self.parent.children[1] to access the one you actually originally added, or devise another way to access the reference.

Related

How to Periodically Update Parent (Screen) Class UI from Child (BoxLayout) Class (Python with Kivy)

Goal:
Periodic update of parent (screen) class / UI from child (boxlayout) class. Theconf2.dat is occasionally updated (from various other screens), and I want the UI to update every 5 seconds or so by re-running this class.
Latest code update:
In the __init__ function, I have Clock.schedule_interval(self.create_button, 1), which should cause the create_button function to rerun every second.
At the top of the create_button function, I have self.box_share.clear_widgets(), which should clear all the widgets so they can be repopulated (per the instructions outlined further down the create_button function).
Action:
I run the code
I navigate to NoTags screen by clicking the button with text title 'updating sequence'
I make changes to buttons that were dynamically created under scrollview by clicking on them. They successfully change color. This information is written to the conf2.dat file.
I navigate to SequenceScreen screen by first clicking 'home' button, then clicking 'sequence display' button. This SequenceScreen screen is the screen I wish to have updated to reflect the changes made to conf2.dat file.
Result:
UI associated withSequenceScreen(Screen) class still does not update per changes made from UI associated with NoTags(Screen) class.
However, when I restart the app altogether, I find the SequenceScreen UI successfully updated.
Suspicion:
I'm just one line of code away from getting this UI to update properly.
Python Code:
from kivy.app import App
# kivy.require("1.10.0")
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.textinput import TextInput
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.properties import NumericProperty
from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.uix.scrollview import ScrollView
#from kivy.base import runTouchApp
from kivy.properties import StringProperty, ObjectProperty, NumericProperty
from kivy.storage.dictstore import DictStore
import pickle
import datetime, threading
import time
from kivy.clock import mainthread
class BackHomeWidget(Widget):
pass
class SequenceBoxLayout_NoEdits(BoxLayout):
box_share = ObjectProperty()
config_file = DictStore('conf2.dat')
def __init__(self, **kwargs):
super(SequenceBoxLayout_NoEdits, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_interval(self.create_button, 1)
def create_button(self, *args):
self.box_share.clear_widgets()
top_button_share = 1.1
color = (.4, .4, .4, 1)
for i in range(25):
top_button_share -= .4
id_ = "part" + str(i + 1)
if self.config_file.exists(id_):
btn_color = self.config_file[id_]["background_color"]
else:
self.config_file.put(id_, background_color=color)
btn_color = color
button_share = Button(background_normal='',
background_color=btn_color,
id=id_,
pos_hint={"x": 0, "top": top_button_share},
size_hint_y=None,
height=60,
font_size = 30,
text= str(i+1)
self.box_share.add_widget(button_share)
#Clock.schedule_interval(self.parent.ids.updatedisplay.create_button(self, *args) , 1)
#self.parent.ids.updatedisplay.create_button(self, *args)
class SequenceBoxLayout_NoTag(BoxLayout):
box_share = ObjectProperty()
config_file = DictStore('conf2.dat')
def __init__(self, **kwargs):
super(SequenceBoxLayout_NoTag, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_once(self.create_button)
def create_button(self, *args):
df = pd.read_excel("Test.xlsx","Sheet1")
parts = df['parts'].values.tolist()
top_button_share = 1.1
color = (.4, .4, .4, 1)
for i in range(len(parts)):
top_button_share -= .4
id_ = "part" + str(i + 1)
if self.config_file.exists(id_):
btn_color = self.config_file[id_]["background_color"]
else:
self.config_file.put(id_, background_color=color)
btn_color = color
button_share = Button(background_normal='',
background_color=btn_color,
id=id_,
pos_hint={"x": 0, "top": top_button_share},
size_hint_y=None,
height=60,
font_size = 30,
text= str(i+1)+ ". " + str(parts[i]))
if self.parent.name == 'notags':
button_share.bind(on_press=self.update_buttons_notag)
self.box_share.add_widget(button_share)
def update_buttons_notag(self, button):
button.background_color = 0.86,0.54,0.04,1
self.config_file.put(button.id, background_color=(0.86,0.54,0.04,1))
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class SequenceScreen(Screen):
pass
class NoTags(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("updatelistexample.kv")
class MainApp(App):
def build(self):
return presentation
if __name__ == "__main__":
MainApp().run()
KV Code:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManagement:
transition: FadeTransition()
MainScreen:
AnotherScreen:
NoTags:
SequenceScreen:
<SmallNavButton#Button>:
font_size: 32
size: 125, 50
color: 0,1,0,1
<BigButton#Button>:
font_size: 40
size_hint: 0.5, 0.15
color: 0,1,0,1
<BackHomeWidget>:
SmallNavButton:
on_release: app.root.current = "main"
text: "Home"
pos: root.x, root.top - self.height
<MainScreen>:
name: "main"
FloatLayout:
BigButton:
on_release: app.root.current = "notags"
text: "updating sequence"
pos_hint: {"x":0.25, "top": 0.4}
BigButton:
on_release: app.root.current = "sequence"
text: "sequence display"
pos_hint: {"x":0.25, "top": 0.7}
<AnotherScreen>:
name: "newgarage"
<NoTags>:
name: "notags"
SequenceBoxLayout_NoTag:
BackHomeWidget:
FloatLayout:
BigButton:
text: "Select Parts w/o Tags"
pos_hint: {"x":0.5, "top": 0.6}
background_normal: ''
background_color: (0.4,0.4,0.4,1)
<SequenceBoxLayout_NoEdits>:
box_share: box_share
ScrollView:
GridLayout:
id: box_share
cols: 1
size_hint_y: None
size_hint_x: 0.5
spacing: 5
padding: 130
height: self.minimum_height
canvas:
Color:
rgb: 0, 0, 0
Rectangle:
pos: self.pos
size: self.size
<SequenceBoxLayout_NoTag>:
box_share: box_share
ScrollView:
GridLayout:
id: box_share
cols: 1
size_hint_y: None
size_hint_x: 0.5
spacing: 5
padding: 130
height: self.minimum_height
canvas:
Color:
rgb: 0, 0, 0
Rectangle:
pos: self.pos
size: self.size
<SequenceScreen>:
name: "sequence"
SequenceBoxLayout_NoEdits:
id: updatedisplay
BackHomeWidget:
Credit:
Based on advice provided by #Tshirtman in the comments thread of the posted question...
Summary:
The problem with the code had to do with the fact that I had two different DictStore pointing to the same file, which was tripping up the communication between both classes.
The solution was to instead use only one DictStore and define that variable in the App class, then reference that particular variable in the child classes [using App.get_running_app()], like so:
Define config_file in App class:
class MainApp(App):
config_file = DictStore('conf2.dat')
def build(self):
return presentation
Reference App variable in child classes:
class SequenceBoxLayout_NoEdits(BoxLayout):
...
def __init__(self, **kwargs):
super(SequenceBoxLayout_NoEdits, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_interval(self.create_button, 1)
def create_button(self, *args):
self.box_share.clear_widgets()
app = App.get_running_app()
...
for i in range(len(parts)):
...
if app.config_file.exists(id_):
btn_color = app.config_file[id_]["background_color"]
else:
app.config_file.put(id_, background_color=color)
...
...
class SequenceBoxLayout_NoTag(BoxLayout):
...
def __init__(self, **kwargs):
super(SequenceBoxLayout_NoTag, self).__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_once(self.create_button)
def create_button(self, *args):
...
app = App.get_running_app()
...
for i in range(len(parts)):
...
if app.config_file.exists(id_):
btn_color = app.config_file[id_]["background_color"]
else:
app.config_file.put(id_, background_color=color)
...
...
def update_buttons_notag(self, button):
app = App.get_running_app()
...
app.config_file.put(button.id, background_color=(0.86,0.54,0.04,1))
Clock has a schedule_interval method, which works much like the schedule_once one, but will be called every n seconds instead of just once after n seconds.
So you could certainly just change that call in __init__ and start the create_button by calling self.box_share.clear_widgets() to remove widgets from the previous call before re-creating them.
This might be a bit wasteful, if you find yourself recreating a lot of widgets even if nothing changed, so you could add some logic to first check if the data didn't change, or even if it did, just reuse the old buttons if possible.
box = self.box_share
old_buttons = box.children[:]
box.clear_widgets()
# [your logic about computing the list of parts]
for i, p in enumerate(parts): # this is much better than doing range(len(parts))
# [the logic to get the content of the button]
if old_buttons: # check there are still buttons in the list of old buttons
btn = old_buttons.pop()
else:
btn = Button(
background_normal='',
background_color=btn_color,
pos_hint={"x": 0, "top": top_button_share},
# etc, all the things that are common to all your buttons
# but really, hardcoding them like this is a bit painful,
# you should use a subclass of button so you can style it
# in a kv rule so it would apply to all of them directly
)
btn.id=id_
btn.text = "{}. {}".format(i+1, p)
btn.pos_hint["top"] = top_button_share
# re-apply any other property that might have changed for this part
box.add_widget(btn)
But this logic is quite a common one, actually, and there are other things you can do to improve things in even more situations, though that's quite some work.
Thankfully, you are not the first one to need such thing, and we have been blessed with the wonderful RecycleView, which automates all of this and more, you just need to feed it a data directory, and it'll create the necessary widgets to fill the visible part of the scrollview if there is enough widgets to warrant scrolling, and automatically update when data changes, and when you scroll to see different parts of the list. I encourage you to check it, yourself. but the end result would certainly be something like.
<PartButton#Button>:
id_: None
part: ''
text: '{}. {}'.format(self.id, self.part)
<SequencedBoxLayout#Recycleview>:
parts: self.get_parts()
viewclass: 'PartButton'
data:
[
{
id_: i,
part: part
} for i, p in enumerate(self.parts or [])
]
RecycleBoxLayout:

update a kivy label text in another class

This is a follow up to a previous question I've asked on how to change the properties of a kivy widget (update a kivy label text in another class). I've been trying to figure out why the temperature reading on the Menuscreen updates but within the Mashscreen, the text doesn't update. It looks like the values are being passed to eh temperature1def method but the screen widget doesn't update.
Also, is it better to send the value using
Mashscreen().temperature1def(self.test_temp)
or is it better practice to use
self.stuff_p.text = str(self.test_temp) + u'\u00B0F'
within the MenuScreen to update the label within the Mashscreen? Thanks in advance.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.properties import ObjectProperty
from kivy.clock import Clock
sm = """
ScreenManager:
#Handling the gesture event.
id:manager
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 0.5
Rectangle:
pos: 0,0
size: 800, 480
MenuScreen:
id:MenuScreen
name:'MenuScreen'
manager: manager
Mashscreen:
id:Mashscreen
name: 'Mashscreen'
manager: manager
<MenuScreen>:
stuff_r: mainel1temp
Button:
text: "Go to mashscreen"
on_release:
root.manager.current = "Mashscreen"
Label:
id: mainel1temp
text:'0'
size_hint: None, None
size: 75,50
pos: 295,308
font_size:'22sp'
text_size: self.size
halign: 'left'
valign: 'middle'
<Mashscreen>:
stuff_p: temperature1
FloatLayout:
Label:
id: temperature1
text:'100'
size_hint: None, None
size: 75,50
pos: 50,275
font_size:'22sp'
text_size: self.size
halign: 'left'
valign: 'middle'
"""
class MenuScreen(Screen):
test_temp = 99
stuff_r = ObjectProperty(None)
def __init__(self,**kwargs):
super(MenuScreen,self).__init__(**kwargs)
Clock.schedule_interval((self.read_temp), 1)
#self.read_temp(1)
def read_temp(self, dt):
self.test_temp += 1
self.stuff_r.text = str(self.test_temp) + u'\u00B0F'
Mashscreen().temperature1def(self.test_temp)
#self.parent.ids.Mashscreen.stuff_p.text = str(self.test_temp) + u'\u00B0F'
class Mashscreen(Screen):
stuff_p = ObjectProperty(None)
def __init__(self, **kwargs):
super(Mashscreen, self).__init__(**kwargs)
def temperature1def(self, temp1):
print(temp1)
self.stuff_p.text = str(temp1)
class TestApp(App):
def build(self):
return Builder.load_string(sm)
if __name__ == '__main__':
TestApp().run()
First…
Mashscreen().temperature1def(self.test_temp)
This doesn't call the temperature1def method on your Mashscreen instance in the UI, instead, it creates a new Mashscreen instance, calls the method on it, and then let this object be garbage collected by python. If you want to update your UI, you need to get a reference to the widget you want to update.
You define your Mashscreen in the root rule of your application, so you can get it by its id in this object.
App.get_running_app() will return a reference to your currently running app, which has a root attribute, which is your root widget, any widget at the root of a rule can use its ids attribute to get a reference to any id defined in its scope, so.
App.get_running_app().root.ids.Mashscreen.temperature1def(self.test_temp)
will certainly be more like what you actually want to do.
Now, regarding the question about how to do it best in python kivy, i find that it's cleaner to do something like.
App.get_running_app().root.ids.Mashscreen.temperature = self.test_temp
and then to change your Mashscreen class to have a temperature NumericProperty, and to change your kv rule to use this value in the Label.
<Mashscreen>:
stuff_p: temperature1
FloatLayout:
Label:
id: temperature1
text: '%s' % root.temperature
size_hint: None, None
size: 75,50
pos: 50,275
font_size:'22sp'
text_size: self.size
halign: 'left'
valign: 'middle'

Kivy - Binding a Label's Text to a Variable in Another Class

I am trying to get the label’s text in “EventWindow” to update when the variable “current_text” in the Event class in “scripts/events.py”. I realize that the answer may lie in binding “current_text” to “ct”, but that just results in “AttributeError: class Event has no attribute ‘bind’”. If I am barking up the wrong tree with this solution, I would be very receptive of another method.
Below are the relevant code snippets, but the full project is available at: https://github.com/DinkWerks
main.py
from kivy.properties import ObjectProperty, StringProperty
from kivy.app import App
# Utility Imports
import yaml
# Game Imports
from scripts.events import Event
# File Loads
loc_file = file('data/location.yml')
loc_dat = yaml.load(loc_file)
# Bind Classes
event = Event()
class EventWindow(BoxLayout):
ct = StringProperty('')
def __init__(self, **kwargs):
super(EventWindow, self).__init__(**kwargs)
self.ct = event.current_text
# Error occurs below. Comment out too see semi-functional app.
Event.bind(current_text=self.setter('ct'))
scripts/events.py
from kivy.properties import StringProperty
...
from player import Player
from enemy import Enemy
class Event(Player):
open_events = file('data/event.yml', 'r')
event_file = yaml.load(open_events)
current_text = StringProperty('1234')
def __init__(self):
Player.__init__(self)
self.events = Event.event_file
self.selection = ''
self.current_text = '1234'
def event_name(self):
...
def event_selector(self, eid):
...
def parse(self):
driver = 1
variables = ('Name', 'Is_Person', 'Level', 'Gold')
poss_commands = ("[Next Slide]", "[Query]", "[Terminate]", "[Combat]")
while driver >= 0:
text = self.events[self.selection][driver]
lexer = shlex(text)
lexer.quotes = '/'
output = ''
command = ''
for token in lexer:
if token in variables:
output += str(eval('Player.' + token))
elif token.replace('/', '') in poss_commands:
command += token.replace('/', '')
else:
output += token.replace('/', '')
self.current_text = output
driver += self.controller(command)
self.modifier()
def modifier(self):
...
def controller(self, cmd):
...
text.kv
<EventWindow>:
BoxLayout:
pos: 100,100
size_hint: .4,.7
orientation: 'vertical'
Image:
source: 'maps/map.jpg'
pos: self.pos
size: self.size
ScrollView:
canvas.before:
Color:
rgba: [.2,.2,.2,.8]
Rectangle:
size: self.size
pos: self.pos
Label:
id: text_area
text: root.ct
padding: 15,10
text_size: self.width, None
size_hint_y: None
height: self.texture_size[1]
<Foo>:
id: bl
popup: popup.__self__
header: header
BoxLayout:
orientation: 'vertical'
BoxLayout:
...
FloatLayout:
id: mapspace
canvas:
...
EventWindow:
id: event
Popup:
...
Thanks!
In your code, Event is derived from Player, which itself is an old style class. Importantly, neither implement methods necessary for event management (e.g. fbind; for a stub class defining an interface see Observable). Most likely it is enough to make Event or Player a subclass of EventDispatcher to be able to create and bind to events kivy-style.

Kivy: Modifying a child widget of another separate class

im currently looking into kivy to start with crossplatform development. i have a bit of python experience (but basic) and now wanted to code a little game in kivy to get into. i probably wont finish this but i like learning stuff while doing it with something im intrested in.
Anyway my "App" is supposed to be seperated in two seperate "screens" the top one is only used for displaying stuff and the all interactive stuff is controlled from the bottom "screen".
Now i want to display some text in old school way by getting it written letter by letter to the screen.
This is working fine but for some reason the Label widget is only updated on screen if i call the "print_something" function from the top screen, if i call it from the bottom screen the function is indeed called but the Label widget wont change on screen.
Am i doing something wrong?
Here is a stripped version of the code:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.clock import Clock
Builder.load_string('''
<MainUI>:
orientation: 'vertical'
# both these variables can be the same name and this doesn't lead to
# an issue with uniqueness as the id is only accessible in kv.
<Screen1>:
print_txt: print_txt
layout: layout
RelativeLayout:
id: layout
pos: 0, 400
size: 480, 400
Button:
pos: 0, 200
size_hint: (1, 0.2)
text: "Test Print"
on_press: root.print_something('TEST PRINT FROM SCREEN1')
AnchorLayout:
anchor_x: 'center'
anchor_y: 'bottom'
Label:
id: print_txt
padding_x: 10
markup: True
text_size: self.size
halign: 'left'
valign: 'top'
size_hint: (1, 0.2)
text: ""
<Screen2>:
btn1: btn1
RelativeLayout:
pos: 0, 0
size: 480, 400
Button:
id: btn1
pos_hint: {'x': .15, 'center_y': .5}
size_hint: (0.7, 0.5)
text: "Test Print"
on_press: root.print_text()
''')
class Screen1(Widget):
print_txt = ObjectProperty(None)
layout = ObjectProperty(None)
def print_something(self, string):
print 'Function called...'
self.print_txt.text = ''
counter = [0]
string_len = len(string)
def print_step(dt):
if counter[0] == string_len:
return False
else:
self.print_txt.text += string[counter[0]]
counter[0] = counter[0] + 1
Clock.schedule_interval(print_step, 2.0/60.0)
print 'Function End..'
class Screen2(Widget):
btn1 = ObjectProperty(None)
def __init__(self):
super(Screen2, self).__init__()
def print_text(self):
print 'Trying to print Text from Screen2 to Screen1'
target = Screen1()
target.print_something('TEST PRINT FROM SCREEN2')
class MainUI(Widget):
def __init__(self):
super(MainUI, self).__init__()
self.screen1 = Screen1()
self.add_widget(self.screen1)
self.add_widget(Screen2())
class MainApp(App):
def build(self):
Window.size = (480, 800)
return MainUI()
if __name__ == '__main__':
MainApp().run()
Your Screen2 print_text method creates a new Screen1 instance, which is modified but not displayed anywhere so you don't see anything change.
You could change the call to instead something like
on_press: root.parent.screen1.print_text()
...to access the print_text function of the Screen1 instance that you actually want to update.

Why are widgets not created at __init__ function?

I'm new to kivy so I'm trying to do an App, useful to me, to learn this framework.
My need is to manage my shift work schedule so I started inserting calendar days into a GridLayout. After several troubles now I'm blocked because I've got this error:
File "turno.py", line 53, in load_content
self.gridMonthView.add_widget(DaysInfo(day=wid))
AttributeError: 'NoneType' object has no attribute 'add_widget'
The load_content is called from inside the __init__ function, the error happens when I try to add DaysInfo widget (one for each day of the current month).
The strange (for me) behaviour is that if I comment line n.47 (that calls the load_content method) the program goes running and then I'm able to use the load_content from inside functions prevMonth and nextMonth without having any error.
So the question is:
Why does it seem that inside the __init__ method I can't use add_widget for gridMonthView reference/object but it's possible from the other methods of the same class?
Maybe that is not possible to add widget before the __init__ function ends something that I don't know/understand?
So, here's the code:
A little module to handle dates: calendario.py
from calendar import Calendar
from datetime import date
IT_months = ['Gennaio', 'Febbraio', 'Marzo', 'Aprile',\
'Maggio', 'Giugno', 'Luglio', 'Agosto',\
'Settembre', 'Ottobre', 'Novembre', 'Dicembre']
IT_days = ['Lunedi',
'Martedi',
'Mercoledi',
'Giovedi',
'Venerdi',
'Sabato',
'Domenica']
class Calendario():
def __init__ (self):
self.currDay = date.today()
self.calen = Calendar(0)
# def __init__ (self):
def getMonth(self):
return IT_months[self.currDay.month-1]
# def getMonth(self):
def getYear(self):
return str(self.currDay.year)
# def getYear(self):
def getDaysOfMonth(self, listOfDays):
listOfDays = []
for td in self.calen.itermonthdates(self.currDay.year, self.currDay.month):
listOfDays.append(td.day)
# for td in ...
return listOfDays
# def getDaysOfMonth(self, listOfDays):
def setNextMonth(self):
if self.currDay.month == 12:
self.currDay = self.currDay.replace(month=1, year=(self.currDay.year+1))
else:
self.currDay = self.currDay.replace(month=(self.currDay.month+1))
# def setNextMonth(self):
def setPrevMonth(self):
if self.currDay.month == 1:
self.currDay = self.currDay.replace(month=12, year=(self.currDay.year-1))
else:
self.currDay = self.currDay.replace(month=(self.currDay.month-1))
# def setNextMonth(self):
the .kv file: turno.kv
#:kivy 1.9.0
#
# menu bar
#
<MenuBar>:
orientation: 'horizontal'
padding: 1
spacing: 1
size_hint_y: 0.15
Button:
text: "Icon"
size_hint_x: 0.3
on_press: root.menu()
Button:
text: "Title"
#size_hint: 1, 0.5
#
# day's info
#
<DaysInfo>:
orientation: 'vertical'
padding: 1
spacing: 1
Label:
color: 1,0,0,1
background_color: 1,1,1,1
id: f1
text: " "
Label:
id: f2
text: " "
Label:
id: f3
text: " "
#
# month view
#
<MonthView>:
gridMonthView: gridMonthView
#
orientation: "vertical"
#
# month selection
#
BoxLayout:
id: box1
orientation: 'horizontal'
padding: 1
spacing: 1
size_hint_y: 0.15
Button:
backgroud_color: 0,1,0,1
text: " << "
size_hint_x: 0.1
on_press: root.prevMonth()
Button:
id: idSelMonth
text: root.curMonth
size_hint_x: 0.5
Button:
id: isSelYear
text: root.curYear
size_hint_x: 0.3
Button:
text: " >> "
size_hint_x: 0.1
on_press: root.nextMonth()
#
# week's days
#
BoxLayout:
id: box2
orientation: 'horizontal'
padding: 1
spacing: 1
color: 0., 0.5, 0.5, 1
size_hint_y: 0.1
Label:
text: "Lu"
Label:
text: "Ma"
Label:
text: "Me"
Label:
text: "Gi"
Label:
text: "Ve"
Label:
text: "Sa"
Label:
text: "Do"
#
# Month's days
#
GridLayout:
id: gridMonthView
cols: 7
rows: 6
padding: 1
spacing: 1
size_hint_y: 0.6
#
# Turno Main Form
#
<TurnoMainForm>:
orientation: 'vertical'
padding: 20
spacing: 10
#width: 400
#height: 800
# menu bar
MenuBar:
# month view
MonthView:
id: id1
the app's source code: turno.py
# -*- coding: utf-8 -*-
"""
Created on Mon Oct 06 12:25:04 2015
#author: a809077
"""
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty, StringProperty
from kivy.config import Config
from calendario import Calendario
# impostazione della grandezza della finestra
Config.set('graphics', 'width', '480')
Config.set('graphics', 'height', '800')
class MenuBar(BoxLayout):
def __init__(self, **kwargs):
super(MenuBar, self).__init__(**kwargs)
print ("------- MenuBar -------")
for child in self.children:
print(child)
def menu (self):
print (" ====== click sul menu ======")
# end of class: MenuBar(BoxLayout)
##
## visualizza i dati mensili
##
class MonthView(BoxLayout):
gridMonthView = ObjectProperty(None)
curMonth = StringProperty()
curYear = StringProperty()
def __init__(self, **kwargs):
print ("------- Month View -------")
self.curMonth = TurnoApp.currCal.getMonth()
self.curYear = TurnoApp.currCal.getYear()
super(MonthView, self).__init__(**kwargs)
self.load_content(True)
self.printInfo()
def load_content(self, clearWidget = False):
if (clearWidget):
print "---- Clear Widgets ----"
self.gridMonthView.clear_widgets()
lod = list ()
lod = TurnoApp.currCal.getDaysOfMonth(lod)
for wid in lod:
self.gridMonthView.add_widget(DaysInfo(day=wid))
def prevMonth (self):
print (" ====== click sul mese precedente 1 ======")
TurnoApp.currCal.setPrevMonth()
self.curMonth = TurnoApp.currCal.getMonth()
self.curYear = TurnoApp.currCal.getYear()
#self.printInfo()
self.load_content(True)
def nextMonth (self):
print (" ====== click sul mese successivo ======")
TurnoApp.currCal.setNextMonth()
self.curMonth = TurnoApp.currCal.getMonth()
self.curYear = TurnoApp.currCal.getYear()
#self.printInfo()
self.load_content(True)
def printInfo (self):
print " ____ items ____"
for key, val in self.ids.items():
print("key={0}, val={1}".format(key, val))
print " ____ childs ____"
for child in self.children:
print("{} -> {}".format(child, child.id))
print " ____ walk ____"
for obj in self.walk():
print obj
# end of class: MonthView(GridLayout):
class DaysInfo(BoxLayout):
def __init__(self, **kwargs):
super(DaysInfo, self).__init__()
#print ("-- Days Info - {:d} ------".format(kwargs["day"]))
self.ids["f1"].text =str(kwargs["day"])
# end of class: DaysInfo(BoxLayout):
class TurnoMainForm(BoxLayout):
def __init__(self, **kwargs):
super(TurnoMainForm, self).__init__(**kwargs)
print ("-------TurnoMainForm-------")
for child in self.children:
print(child)
# end of class: TurnoMainForm(BoxLayout):
class TurnoApp (App):
# icon = 'mia_icona.png'
title = 'Turno Terna'
currCal = Calendario()
def build (self):
return TurnoMainForm()
#return MonthView()
# end of class: TurnoApp (App):
TurnoApp().run()
I don't try to reduce the code to an example, instead I post everything because it may be better to understand where the problem is and to give me some tip to improve the code.
Why does it seem that inside the init method I can't use add_widget for gridMonthView reference/object but it's possible from the other methods of the same class?
Because self.gridMonthView has not yet been set yet (i.e. modified from the default value of None) within the __init__. This is a technical limitation - imagine two widgets with kv rules that reference one another, in this case at least one of them can't reference the other in its __init__ because it will be the first one to actually have been instantiated. This is the kind of effect you see here, the widgets must all be instantiated before references between them can be set.
It works in other methods because you call them only later, after all the instantiation is complete.
You can use something like Clock.schedule_once(self.post_init, 0) and put your widget addition in the post_init method; scheduling with an interval of 0 makes sure it will happen before the next frame, but after everything currently in progress has completed.

Categories

Resources