How to reference other class methods in kivy - python

I'm new to stack overflow but I've been programming in python for a couple years. One thing I havent done much of is object oriented programming. I just started learning kivy and I think I might have screwed up the root of my program by organizing it wrong. I used classes to define each label, button, layout...etc. now I'm wondering how I can reference the attributes of each text input to use what's filled in for other methods I want to define.
Example: have a button when pressed gets the input from two different text inputs and adds them together and a label displays it.
You can see what I've tried in my code but I'm having a hell of a time figuring out how to reference all these things from different classes.
I hope my code is understandable...sorry its lengthy but I want you to see the whole scope of what I did. I'm really hoping I can get a solution to where I can keep my organization and have certain things only show up under their own files but I understand if I have to change a lot of things...
Main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.label import Label
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.uix.gridlayout import GridLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from os import listdir
from kivy.core.window import Window
#load all files with 'kv' in folder
kv_path = './kv/'
for kv in listdir(kv_path):
Builder.load_file(kv_path+kv)
#move the keyboard below text input
Window.softinput_mode = 'below_target'
#classes for savings and loan screens
class SaveButton(Button):
pass
class LoanButton(Button):
pass
class Calculate(Button):
def add(self):
total = SaveDepositInput.inideposit +GoalAmountInput.amount
return total
pass
class TotalsLabel(Label):
pass
#layout classes
class MainBoxLayout(BoxLayout):
pass
class InsideAnchorLayout(AnchorLayout):
pass
class OneColGridlayout(GridLayout):
pass
class AColGridLayout(GridLayout):
pass
class TwoColGridLayout(GridLayout):
pass
class WidgetRelativeLayout(RelativeLayout):
pass
#Toggle Buttons
class DailyToggle(ToggleButton):
pass
class WeeklyToggle(ToggleButton):
pass
class BiWeeklyToggle(ToggleButton):
pass
class MonthlyToggle(ToggleButton):
pass
class YearlyToggle(ToggleButton):
pass
class NoneToggle(ToggleButton):
pass
class Monthly2Toggle(ToggleButton):
pass
class Yearly2Toggle(ToggleButton):
pass
#classes for screen change
class SaveScreen(Screen):
pass
class LoanScreen(Screen):
pass
class SaveLoanTabs(TabbedPanel):
pass
#classes for savings screen
class OutputLabel(Label):
pass
class GoalOutputLabel(Label):
pass
class NoReinvestLabel(Label):
pass
class SaveInstructLabel(Label):
pass
class SaveDepositLabel(Label):
pass
class SaveDepositInput(TextInput):
def inideposit(self):
initial = root.TextInput.text
deposit = int(initial)
return deposit
pass
class SaveYearsLabel(Label):
pass
class SaveYearsInput(TextInput):
pass
class SaveMonthsInput(TextInput):
pass
class ChooseCompoundLabel(Label):
pass
class SaveInterestLabel(Label):
pass
class SaveInterestInput(TextInput):
pass
class RepeatDepositLabel(Label):
pass
class RepeatDeposit2Label(Label):
pass
class RepeatDepositInput(TextInput):
pass
class YearsLabel(Label):
pass
class MonthsLabel(Label):
pass
class GoalAmount(Label):
pass
class GoalAmountInput(TextInput):
def amount(self):
initial = root.TextInput.text
goal = int(initial)
return goal
pass
#classes for loan screen
class LoanOutputLabel(Label):
pass
class LoanInstructLabel(Label):
pass
class LoanAmountLabel(Label):
pass
class LoanAmountInput(TextInput):
pass
# Create the screen manager
sm = ScreenManager()
sm.add_widget(SaveScreen(name='save'))
sm.add_widget(LoanScreen(name='loan'))
#class to run app
class InterestApp(App):
def build(self):
self.title = "Compound Interest and Loan Application"
return sm
#run app
if __name__ == "__main__":
InterestApp().run()
Main.kv file(mostly used for layout formatting)
<SaveScreen>:
MainBoxLayout:
GridLayout:
cols: 2
SaveButton:
on_press: root.manager.current = 'save'
LoanButton:
on_press: root.manager.current = 'loan'
InsideAnchorLayout:
GridLayout:
cols: 1
canvas:
Color:
rgba: .7,1,.7,1
Rectangle:
size: self.size
pos: self.pos
SaveInstructLabel:
InsideAnchorLayout:
TwoColGridLayout:
SaveDepositLabel:
SaveDepositInput:
InsideAnchorLayout:
AColGridLayout:
ChooseCompoundLabel:
InsideAnchorLayout:
AColGridLayout:
cols: 3
DailyToggle:
MonthlyToggle:
YearlyToggle:
InsideAnchorLayout:
TwoColGridLayout:
SaveInterestLabel:
SaveInterestInput:
InsideAnchorLayout:
AColGridLayout:
RepeatDepositLabel:
InsideAnchorLayout:
AColGridLayout:
cols: 5
NoneToggle:
WeeklyToggle:
BiWeeklyToggle:
Monthly2Toggle:
Yearly2Toggle:
InsideAnchorLayout:
TwoColGridLayout:
RepeatDeposit2Label:
RepeatDepositInput:
InsideAnchorLayout:
TwoColGridLayout:
size_hint_y: None
height: dp(50)
SaveYearsLabel:
TwoColGridLayout:
YearsLabel:
SaveYearsInput:
MonthsLabel:
SaveMonthsInput:
InsideAnchorLayout:
TwoColGridLayout:
GoalAmount:
GoalAmountInput:
InsideAnchorLayout:
GridLayout:
cols: 1
canvas:
Color:
rgba: .5,1,1,1
Rectangle:
size: self.size
pos: self.pos
size_hint: None, None
height: dp(80)
width: self.parent.width - dp(15)
TotalsLabel:
OutputLabel:
GoalOutputLabel:
NoReinvestLabel:
InsideAnchorLayout:
AColGridLayout:
Calculate:
<LoanScreen>:
MainBoxLayout:
TwoColGridLayout:
SaveButton:
on_press: root.manager.current = 'save'
LoanButton:
on_press: root.manager.current = 'loan'
Buttons.kv
<SaveButton>:
id: 'save'
text: "[u]Save[/u]"
color: 1, .9, 0, 1
background_normal: ''
background_color: (.5, .5, .5, 1) if self.state == 'normal' else (0, .75, 1, 1)
group: 'menu'
markup: True
<LoanButton>:
text: "[u]Loan[/u]"
markup: True
color: 1, .9, 0, 1
background_normal: ''
background_color: (.5, .5, .5, 1) if self.state == 'normal' else (0, .75, 1, 1)
group: 'menu'
<DailyToggle>:
text: 'Daily'
group: 'compound'
<MonthlyToggle>:
text: 'Monthly'
group: 'compound'
<YearlyToggle>:
text: 'Yearly'
group: 'compound'
<WeeklyToggle>:
text: 'Weekly'
group: 'repeat'
<Yearly2Toggle>:
text: 'Yearly'
group: 'repeat'
<Monthly2Toggle>:
text: 'Monthly'
group: 'repeat'
<BiWeeklyToggle>:
text: 'Bi-Weekly'
group: 'repeat'
<NoneToggle>:
text: 'None'
group: 'repeat'
<Calculate>:
text: '[u][b]Calculate[/b][/u]'
markup: True
on_release: self.add
Labels.kv
<SaveInstructLabel>:
text: "[b]This is the Savings screen. It will calculate compounded interest over a period of time and tell you the result. Follow each prompt to calculate.[/b]"
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'center'
color: 0,0,0,1
markup: True
<SaveDepositLabel>:
text: "Enter initial deposit amount:"
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'center'
color: 0,0,0,1
<ChooseCompoundLabel>:
text: "Choose frequency of compounding interest:"
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'center'
color: 0,0,0,1
<SaveInterestLabel>:
text: 'Enter the interest APY:'
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'center'
color: 0,0,0,1
<RepeatDepositLabel>:
text: 'How often will you make a deposit:'
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'center'
color: 0,0,0,1
<SaveYearsLabel>:
text: 'Enter the amount of years and months you will have this account build:'
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'center'
color: 0,0,0,1
font_size: dp(15)
<YearsLabel>:
text: 'Years:'
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'right'
color: 0,0,0,1
<MonthsLabel>:
text: 'Months:'
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'right'
color: 0,0,0,1
<GoalAmount>:
text: '(Optional)Enter an amount you would like to reach:'
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'center'
color: 0,0,0,1
<OutputLabel>:
text: app.Calculate.add
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'center'
color: 0,0,0,1
<GoalOutputLabel>:
text: 'total years to reach goal'
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'center'
color: 0,0,0,1
<NoReinvestLabel>:
text: 'if you didnt reinvest'
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'center'
color: 0,0,0,1
<TotalsLabel>:
text: '[u][b]TOTALS:[/b][/u]'
markup: True
valign: 'middle'
halign: 'center'
color: 0,0,0,1
<RepeatDeposit2Label>:
text: 'Enter recurring deposit amount:'
text_size: root.width, None
size: self.texture_size
valign: 'middle'
halign: 'center'
color: 0,0,0,1
Textboxes.kv
<SaveDepositInput>:
multiline: False
hint_text: '$0.00'
input_filter: 'float'
size_hint_y: None
height: dp(25)
<SaveInterestInput>:
multiline: False
hint_text: '0.0'
input_filter: 'float'
size_hint_y: None
height: dp(25)
<SaveYearsInput>:
multiline: False
hint_text: '0'
input_filter: 'int'
size_hint_y: None
height: dp(25)
<SaveMonthsInput>:
multiline: False
hint_text: '0'
input_filter: 'int'
size_hint_y: None
height: dp(25)
<GoalAmountInput>:
multiline: False
hint_text: '$0.00'
input_filter: 'float'
size_hint_y: None
height: dp(25)
<RepeatDepositInput>:
multiline: False
hint_text: '$0.00'
input_filter: 'float'
size_hint_y: None
height: dp(25)
And Layouts.kv
<MainBoxLayout>:
spacing: dp(2)
orientation: 'vertical'
canvas.before:
Color:
rgba: .7,1,.7,1
Rectangle:
size: self.size
pos: self.pos
<TwoColGridLayout>:
cols: 2
size_hint: .95, .70
canvas:
Color:
rgba: .5,1,1,1
Rectangle:
size: self.size
pos: self.pos
<OneColGridLayout>:
cols: 1
<AColGridLayout>:
cols: 1
size_hint: .95,.70
canvas:
Color:
rgba: .5,1,1,1
Rectangle:
size: self.size
pos: self.pos
<WidgetRelativeLayout>:
<InsideAnchorLayout>:
anchor_x: 'center'

In your Calculate Button, the add method is trying to call static methods, but the methods you name are actually instance methods. So you need to call them as instance methods. That means that you must have the instances of SaveDepositInput and GoalAmountInput. Since all of these things are in your SaveScreen, you can easily reference them using ids. To do this you cn add an id to SaveDepositInput in your SaveScreen:
InsideAnchorLayout:
TwoColGridLayout:
SaveDepositLabel:
SaveDepositInput:
id: save_deposit_input
and for the GoalInputAmount:
InsideAnchorLayout:
TwoColGridLayout:
GoalAmount:
GoalAmountInput:
id: goal_amount_input
then, in order to access these from the Calculate Button:
InsideAnchorLayout:
AColGridLayout:
Calculate:
gai: goal_amount_input
sdi: save_deposit_input
Then, the CalculateButton class becomes:
class Calculate(Button):
def add(self):
total = self.sdi.inideposit() + self.gai.amount()
return total
A couple other issues. The CalculateButton in your Buttons.kv should be:
<Calculate>:
text: '[u][b]Calculate[/b][/u]'
markup: True
on_release: self.add()
( you were missing the ())
And you have a similar class vs instance issue with your OutputLabel in your Labels.kv.

Related

How to get a kivy variable added to Screen from add widget()

So im trying to get a variable added to screen and the id fro that is in a widget Im going to add,
so if for example I have this:
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import StringProperty
kv = """
Screen:
cal:cal
side: side
GridLayout:
rows: 1
cols:2
spacing:0
GridLayout:
rows: 5
cols:1
Button:
text: "Cube"
# on_press: app.mode = self.text
on_press: app.setMode(self)
Button:
text: "Cuboid"
on_press: app.setMode(self)
Button:
text: "Cylinder"
on_press: app.setMode(self)
Button:
text: "Cone"
on_press: app.setMode(self)
Button:
text: "Sphere"
on_press: app.setMode(self)
FloatLayout:
Label:
text: "The Volume and surface area of a {}:".format(app.mode)
pos_hint: {"x":0.1, "y":0.8}
text_size: self.size
FloatLayout:
id:cal
Label:
text:"Side:"
pos_hint: {"x":1.1, "y":0.7}
text_size: self.size
Label:
text:"Volume:"
pos_hint: {"x":1.1, "y":0.65}
text_size: self.size
Label:
text:"Surface Area:"
pos_hint: {"x":1.1, "y":0.6}
text_size: self.size
TextInput:
size_hint: (.4, None)
height: 26
multiline: False
pos_hint: {"x":1.24, "y":0.7}
id: side
text: app.sideText
on_text_validate: app.Cube_on_side_change(self)
Label:
text: app.volume
pos_hint: {"x":1.27, "y":0.65}
text_size: self.size
Label:
text: app.surface_area
pos_hint: {"x":1.355, "y":0.6}
text_size: self.size
"""
cube = """
FloatLayout:
Label:
text:"Side:"
pos_hint: {"x":1.1, "y":0.7}
text_size: self.size
Label:
text:"Volume:"
pos_hint: {"x":1.1, "y":0.65}
text_size: self.size
Label:
text:"Surface Area:"
pos_hint: {"x":1.1, "y":0.6}
text_size: self.size
TextInput:
size_hint: (.4, None)
height: 26
multiline: False
pos_hint: {"x":1.24, "y":0.7}
id: side
text: app.sideText
on_text_validate: app.Cube_on_side_change(self)
Label:
text: app.volume
pos_hint: {"x":1.27, "y":0.65}
text_size: self.size
Label:
text: app.surface_area
pos_hint: {"x":1.355, "y":0.6}
text_size: self.size"""
cuboid = """
FloatLayout:
id:main
Label:
text:"Length:"
pos_hint: {"x":1.1, "y":0.7}
text_size: self.size
Label:
text:"Breadth:"
pos_hint: {"x":1.1, "y":0.65}
text_size: self.size
Label:
text:"Height:"
pos_hint: {"x":1.1, "y":0.6}
text_size: self.size
TextInput:
size_hint: (.4, None)
height: 26
multiline: False
pos_hint: {"x":1.24, "y":0.7}
id: length
text: app.sideText
on_text_validate: app.Cuboid_on_side_change(self)
TextInput:
size_hint: (.4, None)
height: 26
multiline: False
pos_hint: {"x":1.24, "y":0.65}
id: breadth
text: app.sideText
on_text_validate: app.Cube_on_side_change(self)
TextInput:
size_hint: (.4, None)
height: 26
multiline: False
pos_hint: {"x":1.24, "y":0.6}
id: height
text: app.sideText
on_text_validate: app.Cuboid_on_side_change()
Label:
text: "Volume:"
pos_hint: {"x":1.1, "y":0.55}
text_size: self.size
Label:
text: "Surface Area:"
pos_hint: {"x":1.1, "y":0.5}
text_size: self.size
Label:
text: app.volume
pos_hint: {"x":1.27, "y":0.55}
text_size: self.size
Label:
text: app.surface_area
pos_hint: {"x":1.355, "y":0.5}
text_size: self.size"""
cone = """
FloatLayout:
Label:
text:"Radius:"
pos_hint: {"x":1.1, "y":0.7}
text_size: self.size
Label:
text:"Height:"
pos_hint: {"x":1.1, "y":0.65}
text_size: self.size
TextInput:
size_hint: (.4, None)
height: 26
multiline: False
pos_hint: {"x":1.24, "y":0.7}
id: Radius
text: app.sideText
on_text_validate: app.Cuboid_on_side_change(self)
TextInput:
size_hint: (.4, None)
height: 26
multiline: False
pos_hint: {"x":1.24, "y":0.65}
id: height
text: app.sideText
on_text_validate: app.Cube_on_side_change(self)
Label:
text: "Volume:"
pos_hint: {"x":1.1, "y":0.6}
text_size: self.size
Label:
text: "Surface Area:"
pos_hint: {"x":1.1, "y":0.55}
text_size: self.size
Label:
text: app.volume
pos_hint: {"x":1.27, "y":0.6}
text_size: self.size
Label:
text: app.surface_area
pos_hint: {"x":1.355, "y":0.55}
text_size: self.size
"""
screens = {
"Cube": cube,
"Cuboid": cuboid,
"Cone": cone
}
class MyApp(App):
sideText = StringProperty("")
surface_area = StringProperty("0 cm²")
volume = StringProperty("0 cm³")
mode = StringProperty("Cube")
def build(self):
self.screen = Builder.load_string(kv)
return self.screen
def setMode(self, btn):
self.volume = "0 cm³"
self.surface_area = "0 cm³"
self.mode = btn.text
self.screen.cal.clear_widgets()
self.screen.cal.add_widget(Builder.load_string(screens[self.mode]))
def Cube_on_side_change(self, instance):
try:
value = float(instance.text)
num = True
except ValueError:
# failed to convert
num = False
print("Numbers only idiot.")
def cubeCalc(val):
return {
"volume": val * val * val,
"surface_area": (val * val) * 6
}
if num:
result = cubeCalc(value)
self.volume = "{:.2f} cm³".format(result["volume"])
self.surface_area = "{:.2f} cm²".format(result["surface_area"])
def Cuboid_on_side_change(self):
height = self.screen.cal.ids.main.height.text
try:
value = float(height)
num = True
except ValueError:
# failed to convert
num = False
print("Numbers only idiot.")
def cubeCalc(val):
return {
"volume": val * val * val,
"surface_area": (val * val) * 6
}
if num:
result = cubeCalc(value)
self.volume = "{:.2f} cm³".format(result["volume"])
self.surface_area = "{:.2f} cm²".format(result["surface_area"])
if __name__ == "__main__":
MyApp().run()
on_text_validate of the TextInput with id:height in the string cuboid, I want to get the text from the text input with something like: self.screen.main.height.text, however, to do that I would have to add main:main and height:height to Screen. How would I do this?
I suggest refactoring your code. Rather than re-inventing the capabilities of ScreenManager, just use ScreenManager. In your kv string, replace the FloatLayout with:
ScreenManager:
CubeScreen:
CuboidScreen:
ConeScreen:
and use your additional kv strings (like cube) as the basis for additional rules in your kv string. Something like:
<CubeScreen>:
Label:
text: "The Volume and surface area of a Cube:"
pos_hint: {"x":0.1, "y":0.8}
text_size: self.size
FloatLayout:
Label:
text:"Side:"
pos_hint: {"x":0.1, "y":0.7}
text_size: self.size
.
.
.
And define each Screen class in your py, like:
class CubeScreen(Screen):
def get_cube_area(self):
.
.
.
And in each of the new classes (like CubeScreen) you can define methods for calculating area, volume, etc, and you can access the TextInputs easily using their ids. And your on_text_validate in each Screen can just call the appropriate method from that Screen (like on_text_validate: root.get_cube_area()).

Kivy - Get InputText From Other Screen

I am trying to type in text from one screen. Press a button and move to another screen and have that text be shown in a label. I've seen a few questions that are similar to mine, but have not been able to figure out how to use the posted solutions and have been stuck for hours (Link One, Link Two, Link Three). I believe that I need to use the __init__ method somewhere because this is an instance? I tried using the first link, but the label ends up blank (the code does run). Any Advice?
main.py
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.app import App
from kivy.lang.builder import Builder
class SecondWindow(Screen):
def get_unique_text(self):
x = self.manager.get_screen("first")
y = x.ids.unique.text
return str(y)
class FirstWindow(Screen):
pass
class MainWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
kv_main = Builder.load_file('main.kv')
class MyApp(App):
def build(self):
return kv_main
if __name__ == '__main__':
MyApp().run()
main.kv
#:include First.kv
#:include Second.kv
WindowManager:
MainWindow:
FirstWindow:
SecondWindow:
<MainWindow>
name: "main"
BoxLayout:
Button:
text: "Press"
on_release:
app.root.current = "first"
First.kv
<FirstWindow#Screen>:
name: "first"
BoxLayout:
orientation: "vertical"
Label:
text: "Enter Unique Text for Saving"
font_size: 20
text_size: self.width, None
halign: 'center'
TextInput:
id: unique
hint_text: 'example: Stand25'
Button:
text: "Press"
on_release:
app.root.current = "second"
Second.kv
<SecondWindow#Screen>:
name: "second"
BoxLayout:
orientation: "vertical"
Label:
text: "Unique Text"
font_size: 20
text_size: self.width, None
halign: 'center'
Label:
text: root.get_unique_text()
font_size: 16
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
color: 0,0,0,1
Button:
text: "Go Back"
on_release:
app.root.current = "first"
Another approach is to use the on_enter() method of a Screen in order to fetch the text. This also requires an id for the unique Label:
<SecondWindow#Screen>:
name: "second"
BoxLayout:
orientation: "vertical"
Label:
text: "Unique Text"
font_size: 20
text_size: self.width, None
halign: 'center'
Label:
id: unique # added id
# text: root.get_unique_text()
font_size: 16
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
color: 0,0,0,1
Button:
text: "Go Back"
on_release:
app.root.current = "first"
Just add an on_enter() method to the SecondWindow class:
class SecondWindow(Screen):
def on_enter(self, *args):
self.ids.unique.text = self.get_unique_text()
def get_unique_text(self):
x = self.manager.get_screen("first")
y = x.ids.unique.text
return str(y)
In your Second.kv you can reference the text of the TextInput in the First.kv by making a couple changes to the kv files. First, in the main.kv, add an id for the FirstWindow (and eliminate the SecondWindow for now):
WindowManager:
MainWindow:
FirstWindow:
id: first # added id
# SecondWindow: # this gets added later
<MainWindow>
name: "main"
BoxLayout:
Button:
text: "Press"
on_release:
app.root.current = "first"
Then, in the Second.kv, set up the reference to the text of the TextInput:
<SecondWindow#Screen>:
name: "second"
BoxLayout:
orientation: "vertical"
Label:
text: "Unique Text"
font_size: 20
text_size: self.width, None
halign: 'center'
Label:
text: app.root.ids.first.ids.unique.text # reference to unique text
font_size: 16
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
pos: self.pos
size: self.size
color: 0,0,0,1
Button:
text: "Go Back"
on_release:
app.root.current = "first"
Since the kv for SecondWindow uses app.root, it will cause an error if SecondWindow is instantiated before the root widget of the App is assigned. To avoid that, add the SecondWindow after a slight delay:
class MyApp(App):
def build(self):
Clock.schedule_once(self.add_second_screen)
return kv_main
def add_second_screen(self, dt):
self.root.add_widget(SecondWindow())

Kivy: Radio button correct implementation

My App has 3 screens to navigate through with the 'ActionBar'. I have named each screen in kv file, one is 'track' the other 'pess'. These are the two I need help with.
I created a radiobutton widget in my 'pess' screen class (pictures below) So I could add email address.
In my 'track' screen class I have all the data I'd like to send over email but I think I'm over complicating things. I want to link the active radiobutton from my 'pess' screen to my 'track' screen def send function...
my Main.py looks like this
class ScreenGenerator(ScreenManager):
pass
class PessQuestions(Screen):
email = {}
path = ''
def loadEmails(self, *args):
self.path = App.get_running_app().user_data_dir + '/'
try:
with open(self.path + 'email.json', 'r') as data:
self.email = json.load(data)
except FileNotFoundError:
pass
def saveEmails(self, *args):
print(self.ids.name.text, type(self.ids.name.text))
print(self.ids.address.text, type(self.ids.address.text))
self.email[self.ids.name.text] = self.ids.address.text
self.path = App.get_running_app().user_data_dir + '/'
with open(self.path + 'email.json', 'w') as email_address_json:
json.dump(self.email, email_address_json)
print('saveEmails')
self.ids.address.text = ''
self.ids.name.text = ''
self.ids.info.clear_widgets()
self.on_pre_enter() # refresh screen
def delete_email(self, check):
address = check.ids.who_name.text
del self.email[address]
self.ids.info.remove_widget(check)
self.saveEmails()
##########################################################
########### PRE ENTER ####################################
def on_pre_enter(self):
self.ids.info.clear_widgets()
self.loadEmails()
try:
for name, mail in self.email.items():
self.ids.info.add_widget(Checkboxes(name=name, mail=mail, email=self.email))
except ValueError:
print('VALUE ERROR: Label.text accept only "str"')
self.ids.pess_date.text = strftime('[b]%A[/b], %B %Y') # time and date
##########################################################
########### PRE LEAVE ####################################
def on_pre_leave(self):
self.ids.info.clear_widgets()
self.saveEmails()
class Checkboxes(BoxLayout):
def __init__(self, name='', mail='', email='', **kwargs):
super().__init__(**kwargs)
self.ids.who_name.text = name
self.ids.who_email.text = mail
def on_checkbox_Active(self, checkboxInstance, isActive):
address = ''
if isActive:
'''
Email is used for recipient
'''
name = self.ids.who_name.text
email = self.ids.who_email.text
print('Email Set!')
return email
else:
'''
Nothing happens, email is not used
'''
##########################################################
######## TRACKERS WINDOWS ################################
##########################################################
class Trackers(Screen):
email = {'davi': 'example#gmail.com'}
storage = {}
pess_storage = {}
path = ''
def on_pre_enter(self):
self.path = App.get_running_app().user_data_dir + '/'
self.loadData()
for tracker, num in self.storage.items():
self.ids.track.add_widget(Tracker(text=tracker, number=num))
def on_pre_leave(self):
self.ids.track.clear_widgets()
self.saveData()
def saveData(self, *args):
with open(self.path + 'data.json', 'w') as data:
json.dump(self.storage, data)
def loadData(self, *args):
try:
with open(self.path + 'data.json', 'r') as data:
self.storage = json.load(data)
print(self.storage)
except FileNotFoundError:
pass
def savePESS(self, PESS):
self.pess_storage['physical'] = PESS.ids.phy_text.text
self.pess_storage['emotional'] = PESS.ids.emo_text.text
self.pess_storage['spiritual'] = PESS.ids.spi_text.text
self.pess_storage['sexual'] = PESS.ids.sex_text.text
self.pess_storage['comment'] = PESS.ids.comment.text
def save_text(self, tracker, text_input, *args):
tracker.ids.label.text = text_input.text
self.storage[text_input.text] = '0'
self.saveData()
self.loadData()
print('testing')
def send(self, PESS):
self.path = App.get_running_app().user_data_dir + '/'
with open(self.path + 'bat', 'r') as bat:
login = bat.read()
davi = self.email['davi']
mail = PESS.ids.who_email.text
#kevin = self.email['kevin'] # instead of this I'd like to have this variable linked to
gmail = GMail(davi, login)
day = strftime("%a, %b %d, %Y")
tracker_message = 'PESS\n'
subject_message = f"Subject: PESS {day}"
for pess, txt in self.pess_storage.items():
tracker_message += f"\t{pess.title()}: {txt}\n"
for tracker, numbers in self.storage.items():
tracker_message += f"\n{numbers} ---> {tracker}\n"
message = f"{subject_message}\n\n{tracker_message}"
msg = Message(f'PESS | {day}', to=mail, text=message)
gmail.send(msg)
self.sent_confirmation(f'{self.ids.who_name.text}: {mail}')
def sent_confirmation(self, recipient):
box = BoxLayout(orientation='vertical', padding=40, spacing=5)
pop = Popup(title='Email Sent', content=box, size_hint=(None,None), size=(self.width, self.width/2))
info = Label(text=f'Congrats, {recipient} has recieved an email.')
box.add_widget(info)
pop.open()
self.saveData()
class Tracker(BoxLayout):
def __init__(self, text='', number='', **kwargs):
super().__init__(**kwargs)
self.ids.label.text = text
self.ids.count_add.text = number
class Pess(App):
def build(self):
Config.set('graphics', 'width', '600')
Config.set('graphics', 'height', '800')
from kivy.core.window import Window
Window.clearcolor = get_color_from_hex('#262829')
return ScreenGenerator()
if __name__ == '__main__':
Pess().run()
kv file
#: import rgba kivy.utils.get_color_from_hex
#: import CheckBox kivy.uix.checkbox
<Label>:
font_size: '17dp'
<MenuButton#ButtonBehavior+Label>:
canvas.before:
Color:
rgba: 0.1, 0.5, 0.7, 1
Ellipse:
pos: self.pos
size: self.height, self.height
Ellipse:
pos: self.x + self.width - self.height, self.y
size: self.height, self.height
Rectangle:
pos: self.x + self.height / 2, self.y
size: self.width - self.height, self.height
<RoundButton#ButtonBehavior+Label>:
canvas.before:
Color:
rgba: 0.8, 0.3, 0.1, 1
Ellipse:
pos: self.width / 2.265, self.y + 130
size: self.height - self.height / 1.5, self.height / 3
<MenuButton2#ButtonBehavior+Label>:
canvas.before:
Color:
rgba: 0.8, 0.3, 0.1, 1
Ellipse:
pos: self.pos
size: self.height, self.height
Ellipse:
pos: self.x + self.width - self.height, self.y
size: self.height, self.height
Rectangle:
pos: self.x + self.height / 2, self.y
size: self.width - self.height, self.height
<FloatButton#ButtonBehavior+FloatLayout>:
id: float_root
size_hint: (None, None)
text: '[b]+[/b]'
font_size: '48dp'
btn_size: (140,140)
size: (140,140)
bg_color: (0.8, 0.3, 0.1, 1)
pos_hint: {'x': 5.4, 'y': .17}
Button:
text: float_root.text
font_size: '14dp'
#allow_stretch: True
markup: True
size_hint: (None, None)
size: float_root.btn_size
pos_hint: float_root.pos_hint
background_normal: ''
background_color: (0,1,0,0)
canvas.before:
Color:
rgba: float_root.bg_color
Ellipse:
pos: self.pos
size: self.size
<TrackerButton#Button>:
background_color: 0,0,0,0
<ScreenGenerator>:
Menu:
name: 'menu'
Trackers:
name: 'track'
PessQuestions:
name: 'pess'
<Menu>:
BoxLayout:
orientation: 'vertical'
padding: 20
spacing: 30
Image:
source: 'book.png'
allow_strech: True
Label:
canvas.before:
Color:
rgba: (1,1,1,.7)
Rectangle:
size: self.size
pos: self.pos
size_hint_y: None
height: 1
MenuButton:
text: 'Enter Trackers'
height: dp(60)
size_hint_y: None
background_image: ''
background_color: rgba('#637075')
on_release: app.root.current = 'track'
MenuButton:
text: 'Enter PESS'
height: dp(60)
size_hint_y: None
background_image: ''
background_color: rgba('#637075')
on_release: app.root.current = 'pess'
Label:
canvas.before:
Color:
rgba: (1,1,1,.7)
Rectangle:
size: self.size
pos: self.pos
size_hint_y: None
height: 1
Label:
id: time
text: ''
markup: True
<PessQuestions>:
BoxLayout:
orientation: 'vertical'
pos_hint: {'top': 1}
ActionBar:
height: self.minimum_height + dp(50)
size_hint_y: None
background_image: ''
background_color: rgba('#ffffff') # rgba('#0B3242')
ActionView:
ActionPrevious:
title: '[b]PESS[/b]'
font_size: '17dp'
color: rgba('#AFB7BA')
markup: True
on_release: app.root.current = 'track'
ActionButton:
text: 'SEND'
color: rgba('#AFB7BA')
on_release: app.root.get_screen('track').send(root) # if .send() function is on another class outside of 'class PessQuestions' use the .get_screen() to locate the right screen class
#on_release: app.root.send()
ActionButton:
text: 'TRACKER'
color: rgba('#AFB7BA')
on_release: app.root.current = 'track'
ActionButton:
text: 'HOME'
color: rgba('#AFB7BA')
on_release: app.root.current = 'menu'
BoxLayout:
orientation: 'vertical'
padding: dp(10)
spacing: dp(12)
pos_hint: {'top': 1}
TextInput:
id: phy_text
hint_text: 'Physical'
size_hint_y: None
height: self.minimum_height + dp(7)
multiline: False
TextInput:
id: emo_text
hint_text: 'Emotional'
size_hint_y: None
height: self.minimum_height + dp(7)
multiline: False
TextInput:
id: spi_text
hint_text: 'Spiritual'
size_hint_y: None
height: self.minimum_height + dp(7)
multiline: False
TextInput:
id: sex_text
hint_text: 'Sexual'
size_hint_y: None
height: self.minimum_height + dp(7)
multiline: False
TextInput:
id: comment
hint_text: 'Leave a comment'
size_hint_y: None
height: self.minimum_height + dp(100)
MenuButton:
id: save_pess
text: 'Save PESS'
height: dp(60)
size_hint_y: None
background_image: ''
on_press: self.text = 'SAVED!'
on_release: app.root.get_screen('track').savePESS(root)
MenuButton2:
text: 'Clear PESS'
height: dp(60)
size_hint_y: None
background_image: ''
on_press: root.ids.save_pess.text = 'Save PESS'
on_release: root.resetPESS()
MenuButton:
text: 'Send Report'
color: rgba('#AFB7BA')
height: dp(60)
size_hint_y: None
background_image: ''
on_release: app.root.get_screen('track').send(root) # if .send() function is on another class outside of 'class PessQuestions' use the .get_screen() to locate the right screen class
#on_release: app.root.send()
Label:
canvas.before:
Color:
rgba: (1,1,1,.7)
Rectangle:
size: self.size
pos: self.pos
size_hint_y: None
height: 1
Label:
text: '[b]Physical, Emotional, Spiritual, Sexual[/b] | These principles when lived with a full purpose of heart will invite the Holy Ghost into your lives.'
markup: True
text_size: self.width, None
size_hint: 1, None
height: self.texture_size[1]
Label:
id: pess_date
text: ''
markup: True
Label:
canvas.before:
Color:
rgba: (1,1,1,.7)
Rectangle:
size: self.size
pos: self.pos
size_hint_y: None
height: 1
# Label & Checkbox creation | for selecting email address
Label:
text: "EMAIL"
text_size: self.width, None
size_hint: 1, None
height: self.texture_size[1]
ScrollView:
BoxLayout:
id: info
orientation: 'vertical'
padding: dp(5)
spacing: dp(7)
#size_hint_y: None
#height: self.minimum_height + dp(50)
TextInput:
id: name
hint_text: 'Name'
size_hint_y: None
height: self.minimum_height + dp(7)
multiline: False
TextInput:
id: address
hint_text: 'Email Address'
size_hint_y: None
height: self.minimum_height + dp(7)
multiline: False
MenuButton:
id: save_email
text: 'Save Email'
height: dp(60)
size_hint_y: None
background_image: ''
on_release: root.saveEmails()
<Checkboxes>:
CheckBox:
group: 'emails'
color: .294, .761, .623
on_active: root.on_checkbox_Active(self, self.active)
#on_active: app.root.get_screen('pess').on_checkbox_Active(self, self.active)
size_hint_x: .50
Label:
id: who_name
text: ''
text_size: self.width, None
halign: 'left'
Label:
id: who_email
text: ''
text_size: self.width, None
halign: 'left'
Button:
text: '[b]X[/b]'
markup: True
size_hint_x: None
width: dp(20)
on_release: app.root.get_screen('pess').delete_email(root)
<Trackers>:
BoxLayout:
orientation: 'vertical'
ActionBar:
height: self.minimum_height + dp(50)
size_hint_y: None
background_image: ''
background_color: rgba('#ffffff') #rgba('#0B3242')
ActionView:
ActionPrevious:
title: '[b]TRACKERS[/b]'
font_size: '17dp'
color: rgba('#AFB7BA')
markup: True
on_release: app.root.current = 'pess'
ActionButton:
text: 'RESET'
color: rgba('#AFB7BA')
on_release: root.reset_pop()
ActionButton:
text: 'PESS'
color: rgba('#AFB7BA')
on_release: app.root.current = 'pess'
ActionButton:
text: 'HOME'
color: rgba('#AFB7BA')
on_release: app.root.current = 'menu'
ScrollView:
BoxLayout:
id: track
orientation: 'vertical'
padding: dp(10)
spacing: dp(15)
size_hint_y: None
height: self.minimum_height
BoxLayout:
size_hint_y: None
height: dp(100)
Button:
text: '+'
font_size: '56dp'
size_hint_y: None
background_image: ''
background_color: (0,0,0,0)
on_release: root.addWidget()
#BoxLayout:
#FloatButton:
# pos_hint_x: None
# pos_hint_y: None
# on_release: root.addWidget()
<Tracker>:
count_add: count_add
name: name
size_hint_y: None
height: 130
canvas.before:
Color:
rgba: 0.1, 0.5, 0.7, 1
Rectangle:
pos: self.pos[0] + self.height/2, self.pos[1]
size: self.size[0] - self.height, self.height
Ellipse:
pos: self.pos[0], self.pos[1]
size: self.height, self.height
Ellipse:
pos: self.pos[0] + self.width - self.height, self.pos[1]
size: self.height, self.height
TrackerButton:
text: '[b]X[/b]'
markup: True
size_hint_x: None
width: 120
on_release: app.root.get_screen('track').delete_storage(root)
Label:
id: name
canvas.before:
Color:
rgba: (1,1,1,.7)
Rectangle:
size: self.size
pos: self.pos
size_hint_x: None
width: 1
TrackerButton:
id: label
font_size: '16dp'
on_release: app.root.get_screen('track').change_name(root)
TrackerButton:
id: count_add
font_size: '16dp'
text: '0'
size_hint_x: None
width: 120
on_release: app.root.get_screen('track').add_num(root)
Label:
canvas.before:
Color:
rgba: (1,1,1,.7)
Rectangle:
size: self.size
pos: self.pos
size_hint_x: None
width: 1
TrackerButton:
text: '[b]-[/b]'
font_size: '24dp'
markup: True
size_hint_x: None
width: 120
on_release: app.root.get_screen('track').subtract_num(root)
I had my code running really well, but all of the email sending information was hardcoded. I wanted to give the user options to input an email address and send it to that address.
I added the class Checkboxes which get's created every time I add a name and email address and hit the "Save Email" It's grouped so they are radio buttons.
I can't seem to connect the email address to my send function on the class Trackers... I hope all of this makes some sense. I might be overly complicating things for myself. I could use some help please. Thank you.
Just came across this question and since I am short on time I wont be able to help you completely. If you ever want to access widgets inside and outside of your class screens you need to have a shared parent for the both of them. In your main py file you can have something like this defined.
class MainInterface(BoxLayout):
def __init__(self, **kwargs):
super(MainInterface, self).__init__(**kwargs)
class WindowManager(ScreenManager):
pass
class HomeWindow(Screen):
...
class TrackerWindow(Screen):
...
sm = WindowManager()
screens = [HomeWindow(name="Home"), TrackerWindow(name="TrackerWindow")]
for screen in screens:
sm.add_widget(screen)
class MyMainApp(App):
def build(self):
root = MainInterface()
return root
The way you use your kv file from now on will be different from what you are used to. Your kv file can look something like this. I'll be using different example uses for one button to save time for myself.
<MainInterface>:
WindowManager:
id: screenmanager
HomeWindow:
id: homewindow
...
Button:
pos_hint:{"x":0,"top":1}
size_hint: 0.125, 0.1
text: "Some Stuff"
font_size: (root.width**1 + root.height**1) / 10**2
on_release:
root.ids['trackerwindow'].send(YourData)
root.ids['homewindow'].access_user_inputs(self.parent.user_inputs)
root.ids['screenmanager'].transition.direction = "left"
root.ids['screenmanager'].current="TrackerWindow"
TrackerWindow:
id: trackerwindow
...
notice how on the homescreen button to get information from its own screen you can't just use root.access_user_inputs(root.user_inputs) you created a parent tree so you use self.parent if you want to access a function from your windowmanager you would do a similar set up but use self.parent.parent. I hope this helps and if you have questions please ask and I'll be able to further help you later.

Handling keyboard input in regard to its source in Kivy

I am currently experimenting a bit with Kivy and added a TextInput to my GUI.
I now want to keep features such as deleting the last character when pressing backspace and so on but also want Kivy to execute a method when a new letter is entered to my textInput.
How would I do that? I tried the following code which works in terms of calling said function but doesn't keep the backspace and delete functionality.
Code:
KV
#:kivy 1.9.0
<PageLayout>:
GridLayout:
searchterm: searchterm
cols: 2
rows: 2
spacing: 5
padding: 5
Button:
text: "maybe a listview of latest traces here"
size_hint: .2,.5
BoxLayout:
orientation:'vertical'
TextInput:
id: searchterm
multiLine: False
hint_text: "Search for specific processes here"
keyboard_on_key_down: root.on_search
Button:
text: "room for imagination"
size_hint: 0.2, .5
Button:
text: "out of ideas"
BoxLayout:
orientation: "vertical"
Button:
text: "this is a new page for additional information"
Label:
halign: 'center'
valign: 'center'
size_hint: None, .025
width: self.parent.width
text: "About:"
color: 0,0,0,1 #textcolor
canvas.before:
Color:
rgba: 0.5,0.5,0.5,1
Rectangle:
pos: self.pos
size: self.size
Label:
halign: 'center'
valign: 'center'
size_hint: None, .025
width: self.parent.width
text: "This tool was written by me"
color: 0,0,0,1 #textcolor
canvas.before:
Color:
rgba: 0.5,0.5,0.5,1
Rectangle:
pos: self.pos
size: self.size
Label:
halign: 'center'
valign: 'center'
size_hint: None, .025
width: self.parent.width
text: "It is only meant for evaluation and testing purposes."
color: 0,0,0,1 #textcolor
canvas.before:
Color:
rgba: 0.5,0.5,0.5,1
Rectangle:
pos: self.pos
size: self.size
Python
import kivy
from kivy.app import App
from kivy.uix.pagelayout import PageLayout
from kivy.config import Config
from kivy.properties import ObjectProperty
from kivy.uix.widget import Widget
#WINDOW ATTRIBUTES
Config.set('graphics','resizable','1')
Config.set('graphics','width','1000')
Config.set('graphics','height','800')
class Visualizer(PageLayout):
searchterm = ObjectProperty(None)
def on_search(self,*args,**kwargs):
print("Test")
print(*args)
print(**kwargs)
class VisualizerApp(App):
def build(self):
self.title = "MQTT IDS Visualizer. Author: Daniel"
return Visualizer()
GLAPP = VisualizerApp()
GLAPP.run()
You need to call the super for keyboard_on_key_down. I believe the easiest way to do that is to define a subclass of TextInput like so:
class MyTextInput(TextInput):
def keyboard_on_key_down(self, window, keycode, text, modifiers):
App.get_running_app().root.on_search( window, keycode, text, modifiers) # calls your `on_search()` method
return super(MyTextInput, self).keyboard_on_key_down(window, keycode, text, modifiers)
Then replace TextInput with MyTextInput in your kv file.

Can not change the value of widget added by add_widget

-- 2017 12/23 14:00 ** I edited for can understand more clealy ** --
Hello everyone.
I wrote simple code, so please check it.
My purpose is change the value "text" and "cooooop" of Number that added with add_widget().
But can not change.
I think that it is caused by referring to cop of the parent widget.
But I do not know the truth.
How should i do?
Please help me!!
#python
from kivy.app import App
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
class Number(Label):
pass
class Wrapper(GridLayout):
pass
class Adder(ButtonBehavior, Label):
rooting = ObjectProperty(None)
def on_release(self):
self.rooting.add_widget(Number(
rooting = self.rooting,
cooooop = self.rooting.cop,
text = self.rooting.cop,
))
class Changer(ButtonBehavior, Label):
rooting = ObjectProperty(None)
def on_release(self):
self.rooting.cop = 'Thank you'
class TestApp(App):
def build(self):
pass
TestApp().run()
#kvfile
<Number>:
font_size: sp(15)
halign: 'center'
markup: True
valign: 'top'
size_hint_y: None
text_size: self.width, sp(50)
height: sp(30)
multiline: False
<Adder>:
text: "Add"
font_size: sp(15)
halign: 'center'
markup: True
valign: 'middle'
size_hint_y: None
text_size: self.width, sp(50)
height: sp(30)
multiline: False
col: 1.000 ,0.5843 ,0.000, 1
on_release: self.col = 1.000 ,0.5843 ,0.000, 1
on_press: self.col = 1.000 ,0.5843 ,0.000, .5
canvas.before:
Color:
rgba: self.col
RoundedRectangle:
pos: self.pos
size: self.size
radius: [sp(12)]
<Changer>:
text: "Change"
font_size: sp(15)
halign: 'center'
markup: True
valign: 'middle'
size_hint_y: None
text_size: self.width, sp(50)
height: sp(30)
multiline: False
col: 0.000 ,0.4784 ,1.000, 1
on_release: self.col = 0.000 ,0.4784 ,1.000, 1
on_press: self.col = 0.000 ,0.4784 ,1.000, .5
canvas.before:
Color:
rgba: self.col
RoundedRectangle:
pos: self.pos
size: self.size
radius: [sp(12)]
Wrapper:
padding: 0, 300, 0, 0
pos_hint: {'top': 1, 'center_x': .5}
cols: 1
size_hint: .3, None
id: rooting
height: self.minimum_height
cop: 'Change me'
spacing: 0, 50
Adder:
rooting: rooting
Changer:
rooting: rooting
Label:
size_hint_y: None
height: sp(40)

Categories