Pass values between screens

I'm completely new to kivy. The app has two screens - CalculateScreen and ResultScreen. ScreenManager provides switching between screens. CalculateScreen takes five values from TextInput: start_distance, finish_distance, start_gasoline, added_fuel, normal_consumption. Method counting returns three values - day_distance, daily_consumption, gasoline_left, which must be passed to appopriate text Labels of the ResultScreen on pressing "Calculate" button: day_distance_result, daily_consumption_result, gasoline_after_result. I tryed to do this by calling set_values method on changing screens but it doesn't work in appropriate way:

Traceback (most recent call last):
   File "mainlogic.py", line 105, in <module>
     MyApp().run()
   File "C:\Users\Anna\project\lib\site-packages\kivy\app.py", line 950, in run
     runTouchApp()
   File "C:\Users\Anna\project\lib\site-packages\kivy\base.py", line 582, in runTouchApp
     EventLoop.mainloop()
   File "C:\Users\Anna\project\lib\site-packages\kivy\base.py", line 347, in mainloop
     self.idle()
   File "C:\Users\Anna\project\lib\site-packages\kivy\base.py", line 391, in idle
     self.dispatch_input()
   File "C:\Users\Anna\project\lib\site-packages\kivy\base.py", line 342, in dispatch_input
     post_dispatch_input(*pop(0))
   File "C:\Users\Anna\project\lib\site-packages\kivy\base.py", line 248, in post_dispatch_input
     listener.dispatch('on_motion', etype, me)
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\Anna\project\lib\site-packages\kivy\core\window\__init__.py", line 1412, in on_motion
     self.dispatch('on_touch_down', me)
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\Anna\project\lib\site-packages\kivy\core\window\__init__.py", line 1428, in on_touch_down
     if w.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\Anna\project\lib\site-packages\kivy\uix\screenmanager.py", line 1198, in on_touch_down
     return super(ScreenManager, self).on_touch_down(touch)
   File "C:\Users\Anna\project\lib\site-packages\kivy\uix\widget.py", line 545, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\Anna\project\lib\site-packages\kivy\uix\relativelayout.py", line 297, in on_touch_down
     ret = super(RelativeLayout, self).on_touch_down(touch)
   File "C:\Users\Anna\project\lib\site-packages\kivy\uix\widget.py", line 545, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\Anna\project\lib\site-packages\kivy\uix\widget.py", line 545, in on_touch_down
     if child.dispatch('on_touch_down', touch):
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\Anna\project\lib\site-packages\kivy\uix\behaviors\button.py", line 151, in on_touch_down
     self.dispatch('on_press')
   File "kivy\_event.pyx", line 705, in kivy._event.EventDispatcher.dispatch
   File "kivy\_event.pyx", line 1248, in kivy._event.EventObservers.dispatch
   File "kivy\_event.pyx", line 1132, in kivy._event.EventObservers._dispatch
   File "C:\Users\Anna\project\lib\site-packages\kivy\lang\builder.py", line 57, in custom_callback
     exec(__kvlang__.co_value, idmap)
   File "C:\Users\Anna\project\kivy-project\my.kv", line 72, in <module>
     on_press: root.set_values()
   File "mainlogic.py", line 55, in set_values
     result_screen.ids.day_distance_result.text = self.day_distance
 AttributeError: 'CalculateScreen' object has no attribute 'day_distance'

Here is my main.py file.

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty, StringProperty


class CalculateScreen(Screen):
    start_distance = ObjectProperty()
    finish_distance = ObjectProperty()
    start_gasoline = ObjectProperty()
    added_fuel = ObjectProperty()
    normal_consumption = ObjectProperty()
    
    def counting(self, start_distance, finish_distance, start_gasoline, added_fuel, normal_consumption):
        if self.added_fuel == 0:
            day_distance = str(finish_distance-start_distance)
            daily_consumption = str((int(day_distance)*normal_consumption)/100)
            gasoline_left = str(start_gasoline-float(daily_consumption))
        else:
            day_distance = str(finish_distance-start_distance) 
            daily_consumption = str((float(day_distance)*normal_consumption)/100)
            gasoline_left = str((start_gasoline+added_fuel)-float(daily_consumption))
        return day_distance, daily_consumption, gasoline_left
        
    def calculations(self):
        try:
            start_distance = float(self.start_distance.text)
            finish_distance = float(self.finish_distance.text)
            start_gasoline = float(self.start_gasoline.text)
            added_fuel = float(self.added_fuel.text)
            normal_consumption = float(self.normal_consumption.text)
        except:
            start_distance = 0
            finish_distance = 0
            start_gasoline = 0
            added_fuel = 0
            normal_consumption = 0
        
            self.counting(start_distance, finish_distance, start_gasoline, added_fuel, normal_consumption)
    
    def set_values(self):
        result_screen = self.manager.get_screen("result")
        result_screen.ids.day_distance_result.text = self.day_distance
        result_screen.daily_consumption_result.text = self.daily_consumption
        result_screen.gasoline_after_result.text = self.gasoline_left
        
        
class ResultScreen(Screen):
    day_distance_result = ObjectProperty()
    daily_consumption_result = ObjectProperty()
    gasoline_after_result = ObjectProperty()
    

class Manager(ScreenManager):
    screen_one = ObjectProperty(None)
    screen_two = ObjectProperty(None)
    

class MyApp(App):
    def build(self):
        m = Manager()
        return m

if __name__=='__main__':
    MyApp().run()
    

Here is my my.kv file.

<Manager>:
    id: screen_manager
    screen_one: screen_one
    screen_two: screen_two
    
    CalculateScreen:
        id: screen_one
        name: "calculator"
        manager: screen_manager
    
    ResultScreen:
        id: screen_two
        name: "result"
        manager: screen_manager
        
        
<CalculateScreen>
    name: "calculator"

    start_distance: start_distance
    finish_distance: finish_distance
    start_gasoline: start_gasoline
    added_fuel: added_fuel
    normal_consumption: normal_consumption
    
    BoxLayout:
        orientation: 'vertical'
        padding: 20
        spacing: 10
        
        BoxLayout:
            orientation: 'vertical'
            Label:
                text: "Start distance"
            TextInput:
                id: start_distance
        
        BoxLayout:
            orientation: 'vertical'
            Label:
                text: "Final distance"
            TextInput:
                id: finish_distance
        
        BoxLayout:
            orientation: 'vertical'
            Label:
                text: "Fuel before leaving"
            TextInput:
                id: start_gasoline
        
        BoxLayout:
            orientation: 'vertical'
            Label:
                text: "Refuelling"
            TextInput:
                id: added_fuel
        
        BoxLayout:
            orientation: 'vertical'
            Label:
                text: "Normal consumption"
            TextInput:
                id: normal_consumption
    
        Button:
            text:'Calculate'
            on_press: root.calculations()
            on_press: root.set_values()
            on_release: root.manager.current = "result"
        
                            
<ResultScreen>
    name: "result"
    
    on_enter: root.manager.get_screen("calculator").set_values()
    
    day_distance_result: day_distance_result
    daily_consumption_result: daily_consumption_result
    gasoline_after_result: gasoline_after_result
    
    
    BoxLayout:
        orientation: 'vertical'
        padding: 20
        spacing: 10
    
        BoxLayout:
            Label
                text: "Daily distance result"
            Label:
                text: "0"
                id: day_distance_result
        
        BoxLayout: 
            Label
                text: "Daily consumption result"
            Label:
                text: "0"
                id: daily_consumption_result
    
        BoxLayout:
            Label
                text: "Gasoline after"
            Label:
                text: "0"
                id: gasoline_after_result
        
        
        BoxLayout:
            orientation: 'horizontal'
            spacing: 10
        
            Button:
                text: 'Exit'
            Button:
                text: 'Recalculate'
                on_release: root.manager.current = "calculator"
        

Upvotes: 1

Views: 49

Answers (1)

John Anderson
John Anderson

Reputation: 39152

Since your set_values() method is referencing attributes of the CalculateScreen instance (like self.day_distance, self.daily_consumption, etc), you must provide those attributes. You can do this by modifying your counting() method to set those attributes:

def counting(self, start_distance, finish_distance, start_gasoline, added_fuel, normal_consumption):
    if self.added_fuel == 0:
        self.day_distance = str(finish_distance - start_distance)
        self.daily_consumption = str((int(self.day_distance) * normal_consumption) / 100)
        self.gasoline_left = str(start_gasoline - float(self.daily_consumption))
    else:
        self.day_distance = str(finish_distance - start_distance)
        self.daily_consumption = str((float(self.day_distance) * normal_consumption) / 100)
        self.gasoline_left = str((start_gasoline + added_fuel) - float(self.daily_consumption))
    # return self.day_distance, self.daily_consumption, self.gasoline_left

Note that since the return value is unused, that line can be eliminated.

And your calculations() method does not call the counting() method unless there is an exception. Probably just a typo. The call to counting() should be unindented:

def calculations(self):
    try:
        start_distance = float(self.start_distance.text)
        finish_distance = float(self.finish_distance.text)
        start_gasoline = float(self.start_gasoline.text)
        added_fuel = float(self.added_fuel.text)
        normal_consumption = float(self.normal_consumption.text)
    except:
        start_distance = 0
        finish_distance = 0
        start_gasoline = 0
        added_fuel = 0
        normal_consumption = 0

    self.counting(start_distance, finish_distance, start_gasoline, added_fuel, normal_consumption)

One other issue is the on_press attributes of the Calculate Button in your kv file. Exactly the opposite of what anyone would expect, the construction:

    Button:
        text:'Calculate'
        on_press: root.calculations()
        on_press: root.set_values()
        on_release: root.manager.current = "result"

results in root.set_values() called first, and then root.calculations() called second. A fix is to use just one on_press attribute:

        text:'Calculate'
        on_press:
            root.calculations()
            root.set_values()
        on_release: root.manager.current = "result"

Upvotes: 1

Related Questions