This is my first question, so sorry if butcher it.
I'm making an application, that will allow for a more efficient protocol creation for other people at my job.
The problem is, that I'm trying to update created RecycleView, but for some reason it doesn't work. Some solutions on the web advise using .refresh_from_data() method, but it did not work, and all the other solutions are too complicated (or I'm too dumb).
I have this function - add_entry that adds information from two TextInputs as a dictionary inside protocol list.
At the current moment my RecycleView just shows numbers, because no solution have worked, and I actually really struggled to even make a Recycle View.
Here is relevant part of Python code:
class DrillingInfoPage(Screen):
rod = 1
dist = 3
protocol = ListProperty() # {Rod:_,Distance:_,Proc:_,Depth:_}
def add_entry(self, proc, depth):
self.protocol.append({'Rod': 0, 'Distance': 0, 'Proc': 0, 'Depth': 0})
self.protocol[self.rod-1]['Proc'] = proc
self.protocol[self.rod-1]['Depth'] = depth
self.protocol[self.rod-1]['Rod'] = self.rod
self.protocol[self.rod-1]['Distance'] = self.dist
self.rod += 1
self.dist += 3
print(self.protocol)
return self.protocol
class Profile(Screen):
pass
class WindowManager(ScreenManager):
pass
class ColorsPopup(Screen):
popupWindow = None
class Recycle(RecycleView):
def __init__(self, **kwargs):
super(Recycle, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in range(50)]
kv = Builder.load_file("my.kv")
class MyApp(App):
def build(self):
return kv
if __name__ == '__main__':
MyApp().run()
And here is a relevant part of KV file:
<DrillingInfoPage>:
name: 'third'
BoxLayout:
orientation: 'vertical'
Label:
size_hint: 1, .4
text: 'Drilling Info Page'
GridLayout:
size_hint: 1, .1
cols:3
GridLayout:
cols:2
Label:
text: 'BG'
TextInput:
id: start
multiline: False
GridLayout:
cols:2
Label:
text: 'BG'
TextInput:
id: end
multiline: False
Button:
text: 'Confirm'
on_release: drilling_holes.text = 'BG' + start.text + ' -----> ' + 'BG' + end.text
GridLayout:
size_hint: 1, .1
cols:3
GridLayout:
cols:2
Label:
text: '%:'
TextInput:
id: proc
multiline: False
GridLayout:
cols:2
Label:
text: 'Depth:'
TextInput:
id: depth
multiline: False
Button:
text: 'Add'
on_release: root.add_entry(proc.text, depth.text)
Label:
id: drilling_holes
size_hint: 1, .2
text: ''
Recycle:
id: drilling_data
data: self.data
viewclass: 'Label'
RecycleBoxLayout:
default_size: None, '25dp'
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Label:
size_hint: 1, .2
text: ''
GridLayout:
size_hint: 1, .17
cols:2
Button:
text: 'Go Back'
on_release:
app.root.current = 'second'
root.manager.transition.direction = 'down'
Button:
text: 'Confirm'
on_release:
app.root.current = 'last'
root.manager.transition.direction = 'up'
I've tried creating some functions inside RecycleView class that would refresh the data, as well as inside the DrillingInfoPage class, but nothing seems to work.
I'm new to Python and especially Kivy, therefore hoping someone wise can guide me in the right direction :)
Here how the screen itself looks right now, ideally it should be empty at first and pressing 'Add' button should add a new line
You just have to add the new information to the data list of the RecycleView. It's not clear exactly what you want to add to the RecycleView, but you can just add a line to your add_entry() method like this:
self.ids.drilling_data.data.append({'text': proc}) # add new entry to data list
And that method does not need a return statement
Related
I have been writing a script to develop an app that calculates the results of Mahjong game.
I have created multiple screens to enter the players name and enter the score etc. in one of the screen I am entering the score and it is not getting saved. As soon as I move to other screen, the score data is lost. How can i save the score in the screen?
.py file looks like below
Window.size = (360, 600)
class MainScreen(Screen):
pass
class Player_Screen(Screen):
#pass
#def callback(self, text):
#self.ids.textbox.text = text
class ScreenManagement(ScreenManager):
shared_data = StringProperty("")
Mahjong = Builder.load_file('trial_app.kv') # Specifying location of kv file
class MainApp(App):
def build(self):
return Mahjong
if __name__ == "__main__":
MainApp().run()
my .kv file looks like below
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
#: import App kivy.app.App
ScreenManagement:
#title: title # so I can use it in the .py file
#score:score # same here
transition: FadeTransition()
MainScreen:
id: title
Player_Screen:
id: Players
name: 'Players'
<MainScreen>:
name: "main"
canvas.before:
Color:
rgba: 0.2, 0.5, 1, 0.5
Rectangle:
pos: self.pos
size: self.size
GridLayout:
cols : 1
Button:
text: "Mahjong"
background_normal:'Mahjong.JPG'
Button:
text: "Let's Play"
on_release: app.root.current = "Contents"
<Player_Screen>:
f_username: Name
BoxLayout:
orientation: 'vertical'
id:f_username
TextInput:
id: Name
hint_text:'Enter Player1'
on_text: root.manager.shared_data = self.text #root.player_name()
TextInput:
id: Name
hint_text:'Enter Player2'
on_text: root.manager.shared_data = self.text
TextInput:
id: Name
hint_text:'Enter Player3'
on_text: root.manager.shared_data = self.text
TextInput:
id: Name
hint_text:'Enter Player4'
on_text: root.manager.shared_data = self.text
Button:
text: 'Back'
on_press: self.background_color = 0,0,0,1
on_release: root.manager.current = 'Contents'
Button:
text: 'Next'
#on_press: root.callback(self.text)
on_press: root.save_data()
on_release: root.manager.current = 'Score'
First of all kindly note that your last edited code is not a reproducible one. It's sometimes quite essential to be such one.
However if I got you right, you want to share or use the entered data in the input fields somewhere in your code as long as the application is running. If that's the requirement, you can do it in multiple ways. Here's a such one:
To store all data centrally in shared_data make it a DictProperty.
class ScreenManagement(ScreenManager):
shared_data = DictProperty(rebind = True)
# rebind is necessary for nested access in kvlang.
Feed the data in the input fields using different keys as follows,
<Player_Screen>:
f_username: Name
BoxLayout:
orientation: 'vertical'
id:f_username
TextInput:
id: Name
hint_text:'Enter Player1'
on_text: root.manager.shared_data["player1"] = self.text
TextInput:
id: Name
hint_text:'Enter Player2'
on_text: root.manager.shared_data["player2"] = self.text
...
Once they are stored, you can access them anywhere from the ScreenManagement instance of your GUI (which also happens to be the root). I've added another screen to demonstrate that.
class PlayerNameScreen(Screen):
pass
Now in kvlang for that screen,
...
ScreenManagement:
#title: title # so I can use it in the .py file
#score:score # same here
transition: FadeTransition()
MainScreen:
id: title
Player_Screen:
id: Players
name: 'Players'
PlayerNameScreen:
name: "player_names"
<PlayerNameScreen>:
Label:
text: "\\n".join(f"{player} : {name}" for player, name in root.manager.shared_data.items())
Button:
size_hint: None, None
size: "100dp", "50dp"
pos_hint: {"center_x" : 0.5, "center_y" : 0.2}
text: "Back"
on_release: root.manager.current = "Players"
I am new to kivy ...I wanted make a to do list app....and wanted to add a name of the task from user and a button which can get a tick mark on press....
some how i got the thing to get a label on screen but i couldn't get it.
This main.py
class ListWidget(RecycleView):
def update(self):
self.data = [{'text': str(item)}for item in self.item]
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.item = []
class RootWidget(BoxLayout):
inputbutton = ObjectProperty(None).
inputcontent = ObjectProperty(None).
outputcontent = ObjectProperty(None).
def add_item(self):
if self.inputcontent.text != " ":
formatted = f'\n*{self.inputcontent.text}'
self.outputcontent.item.append(formatted)
self.outputcontent.update()
self.inputcontent.text = ""
class MyApp(App):
def build(self):
return RootWidget()
MyApp().run()
this is my.kv file
<RootWidget>
inputbutton: inputbutton
inputcontent: inputcontent
outputcontent: outputcontent
orientation: 'vertical'
BoxLayout:
orientation: 'vertical'
size_hint: 1, 0.25
Label:
text: 'TO-DO'
font_size: 32
size_hint: 1,0.3
BoxLayout:
orientation: 'horizontal'
Button:
id: inputbutton
size_hint: 0.25, 1
text: 'add'
on_press:root.add_item()
TextInput:
id: inputcontent
multiline: False
ListWidget:
id: outputcontent
viewclass: 'Label'
orientation: 'vertical'
RecycleBoxLayout:
default_size: None,dp(56)
default_size_hint: 0.4,None
size_hint_y: None
height:self.minimum_height
orientation: 'vertical'
this is the output
This is the output
You can use a custom viewclass that does what you want. Something like this:
class LabelAndButton(GridLayout):
text = StringProperty() # must have a text property (used in ListWidget.data)
Then in the kv you can use this class as the viewclass and define how it looks:
ListWidget:
id: outputcontent
viewclass: 'LabelAndButton' # use the new class
orientation: 'vertical'
RecycleBoxLayout:
default_size: None,dp(56)
default_size_hint: 0.4,None
size_hint_y: None
height:self.minimum_height
orientation: 'vertical'
<LabelAndButton>:
cols:2
Label:
text: root.text
Button:
text: root.text
I'm trying to make a simple Python Kivy app (using a kv file for the layout) that I will use to keep inventory of my parts for work. I am very new to python and kivy, so I need a little bit of direction on this.
One of the basic functions will be to allow the user to input a new part on the "Add Part to Inventory" screen (one of four screens). The user input consists of four items (part name, serial number, on-hand quantity, and the minimum amount needed).
Once the user enters this info and presses the 'Submit' button, I would like that data to be appended to a new row in a google sheet that I'm using as a backend database using the Google Drive/Sheets API (which I was able to get working).
Most of the example apps that I've found online only have one screen, and are only dealing with one type of user input (usually text, whereas my input will be text and integers).
I'm confused as to where I need to put my ids in the kv file (I'm assuming under the AddPartWindow layout), how to call a function when a button is pressed that is not in the root class, how to store the user input into a list since I'm not sure what the ObjectProperty function is really doing, and finally, how to append that list to a new row in my google sheet.
I'm not looking for a cut and paste answer, just some direction would be nice as I have not been able to find a good reference for exactly what I'm trying to do. My main confusion lies in having multiple screens, and how to transfer data between the different screens and their respective classes. Should data only be funneled through a single root class? Or can each class be used to handle data coming from their respective screens (windows) in the app? What exactly is the ObjectProperty function doing? I find the kivy website to be a bit convoluted when describing the properties class.
Any help/direction woud be greatly appreciated.
Here is the main.py code:
import kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
import gspread
from oauth2client.service_account import ServiceAccountCredentials
scope = ['https://www.googleapis.com/auth/drive']
creds = ServiceAccountCredentials.from_json_keyfile_name('My_First_Project-3d753d98320e.json', scope)
client = gspread.authorize(creds)
iQue_sheet = client.open("InventoryBackend").sheet1
#root layout
class InventoryWindow(Screen):
pass
#Layout in question
class AddPartWindow(Screen):
#Is there anything else I need to do with these before saving into a list or dictionary?
part_name = ObjectProperty(None)
serial_number = ObjectProperty(None)
on_hand_cnt = ObjectProperty(None)
min_needed = ObjectProperty(None)
#This function should save the user input into a list, np, and then append to the google sheet iQue_sheet
#Wasn't sure if it should be a list or a dictionary.
#I'm assuming .text is type-casting each object to a string. Can this be used for numerical inputs?
def new_part(self):
np = [self.part_name.text, self.serial_number.text, self.on_hand_cnt.text, self.min_needed.text]
iQue_sheet.append(np)
class OnOrderWindow(Screen):
pass
class OrderFormWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
class InventoryApp(App):
def build(self):
#These are used to enable going back and forth between screens using buttons
sm = ScreenManager()
sm.add_widget(InventoryWindow(name='inv_window'))
sm.add_widget(OnOrderWindow(name='on_order_window'))
sm.add_widget(AddPartWindow(name='add_part_window'))
sm.add_widget(OrderFormWindow(name='order_form_window'))
return sm
if __name__ == "__main__":
InventoryApp().run()
Here is the my .kv file for the layout, the Add Part window is the last layout towards the end:
WindowManager:
InventoryWindow:
OnOrderWindow:
OrderFormWindow:
AddPartWindow:
<ItemLabel#Label>
font_size: '15sp'
halign: 'left'
valign: 'middle'
text_size: self.size
<ItemButton#Button>
pos_hint: {'center_x':0.5, 'center_y':0.5}
size_hint: 0.65, 0.2
<ItemButton2#Button>
pos_hint: {'center_x':0.5, 'center_y':0.5}
size_hint: 0.65, 0.2
on_release:
app.root.current = "order_form_window"
<InventoryWindow>:
name: "inv_window"
add_probe: add_probe
FloatLayout:
Label:
text: 'Inventory'
font_size: '25sp'
size_hint: (1, 0.17)
pos_hint: {'x': 0, 'y': 0.87}
GridLayout:
cols: 4
padding:[10, 65, 10, 10]
spacing: 5
BoxLayout:
orientation: 'vertical'
ItemLabel:
text:'iQue3: Probe/Tubing'
BoxLayout:
orientation: 'vertical'
ItemButton:
text:'Add'
on_release:
root.new_part()
BoxLayout:
orientation: 'vertical'
ItemButton:
text:'Sub'
BoxLayout:
orientation: 'vertical'
ItemButton2:
text:'Order'
BoxLayout:
orientation: 'vertical'
ItemLabel:
text:'Gen2: Probe/Tubing'
BoxLayout:
orientation: 'vertical'
ItemButton:
text:'Add'
BoxLayout:
orientation: 'vertical'
ItemButton:
text:'Sub'
BoxLayout:
orientation: 'vertical'
ItemButton2:
text:'Order'
BoxLayout:
orientation: 'vertical'
ItemLabel:
text:'Beads'
BoxLayout:
orientation: 'vertical'
ItemButton:
text:'Add'
BoxLayout:
orientation: 'vertical'
ItemButton:
text:'Sub'
BoxLayout:
orientation: 'vertical'
ItemButton2:
text:'Order'
BoxLayout:
orientation: 'vertical'
ItemLabel:
text:'iQue3 Fluid Maint. Kit'
BoxLayout:
orientation: 'vertical'
ItemButton:
text:'Add'
BoxLayout:
orientation: 'vertical'
ItemButton:
text:'Sub'
BoxLayout:
orientation: 'vertical'
ItemButton2:
text:'Order'
BoxLayout:
orientation: 'vertical'
ItemLabel:
text:'Screener Fluid Maint. Kit'
BoxLayout:
orientation: 'vertical'
ItemButton:
text:'Add'
BoxLayout:
orientation: 'vertical'
ItemButton:
text:'Sub'
BoxLayout:
orientation: 'vertical'
ItemButton2:
text:'Order'
BoxLayout:
orientation: 'vertical'
GridLayout:
cols: 2
size_hint: 1, 0.15
padding: [10, 0, 10, 10]
BoxLayout:
Button:
text: 'Add Part to Inventory'
font_size: '18sp'
size_hint: (1, 0.75)
on_release:
app.root.current = "add_part_window"
root.manager.transition.direction = "right"
Button:
text: 'On Order'
font_size: '18sp'
size_hint: (1, 0.75)
on_release:
app.root.current = "on_order_window"
root.manager.transition.direction = "left"
<OnOrderWindow>:
name: "on_order_window"
FloatLayout:
Label:
text: 'On Order'
font_size: '25sp'
size_hint: (1, 0.17)
pos_hint: {'x': 0, 'y': 0.87}
GridLayout:
cols: 2
size_int: 1, .15
padding:[10, 510, 10, 10]
Button:
text: "Inventory"
font_size: '18sp'
size_hint: (1, 0.6)
on_release:
app.root.current = "inv_window"
root.manager.transition.direction = "right"
Button:
text:"Add Part to Inventory"
size_hint: (1, 0.6)
font_size: '18sp'
on_release:
app.root.current = "add_part_window"
root.manager.transition.direction = "left"
<OrderFormWindow>
name: "order_form_window"
FloatLayout:
Label:
text: 'Order Form'
font_size: '25sp'
size_hint: (1, 0.17)
pos_hint: {'x': 0, 'y': 0.87}
GridLayout:
cols:2
padding: [50, 50, 50, 120]
ItemLabel:
text: "Part: "
ItemLabel:
text: "Populate Part Here"
ItemLabel:
text: "Serial Number: "
ItemLabel:
text: "Populate SN Here"
ItemLabel:
text: "On Hand: "
ItemLabel:
text: "Populate On Hand Count Here"
ItemLabel:
text: "Minimum Needed: "
ItemLabel:
text: "Populate Min Needed Here"
ItemLabel:
text: "Order Quantity"
TextInput:
halign: 'left'
valign: 'middle'
input_type: 'number'
input_filter: 'int'
multiline:False
GridLayout:
cols:2
size_hint: 1, 0.15
padding: [10, 0, 10, 10]
Button:
text:"Cancel"
on_release: app.root.current = "inv_window"
Button:
text:"Submit Order"
on_release: app.root.current = "on_order_window"
#This is the add part screen layout I'm referring to
<AddPartWindow>
name: "add_part_window"
#These are the id's I was referring to:
part_name: part_name
serial_number: serial_number
on_hand_cnt: on_hand_cnt
min_needed: min_needed
FloatLayout:
Label:
text: 'Add Part to Inventory'
font_size: '25sp'
size_hint: (1, 0.17)
pos_hint: {'x': 0, 'y': 0.86}
GridLayout:
cols:2
padding: [50, 100, 50, 120]
spacing: 5
ItemLabel:
text: "Name of Part: "
TextInput:
id: part_name
halign: 'left'
valign: 'middle'
multinline:False
ItemLabel:
text: "Serial Number: "
TextInput:
id: serial_number
halign: 'left'
valign: 'middle'
multiline:False
ItemLabel:
text: "How Many On Hand?: "
TextInput:
id: on_hand_cnt
halign: 'left'
valign: 'middle'
multinline:False
ItemLabel:
text: "Minimum Needed?: "
TextInput:
id: min_needed
halign: 'left'
valign: 'middle'
multiline:False
GridLayout:
cols:2
size_hint: 1, 0.15
padding: [10, 0, 10, 10]
Button:
text:"Cancel"
on_release:
app.root.current = "inv_window"
root.manager.transition.direction = "left"
#Here is the button I'm referring to, I realize that putting "root." in front of new_part() is not
#the correct thing to put, so it's a placeholder for now:
Button:
text:"Submit"
on_release:
root.new_part()
app.root.current = "inv_window"
root.manager.transition.direction = "left"
Here is a screen shot of the Add Part Screen
The current error I'm getting when I enter input into the four fields and click on the Submit button is this:
File "C:\Users\edr27\kivy_venv\lib\site-packages\kivy\uix\behaviors\button.py", line 179, in on_touch_up
self.dispatch('on_release')
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\edr27\kivy_venv\lib\site-packages\kivy\lang\builder.py", line 57, in custom_callback
exec(__kvlang__.co_value, idmap)
File "C:\Users\edr27\PycharmProjects\pythonProject\inventory.kv", line 345, in <module>
root.new_part()
File "C:\Users\edr27\PycharmProjects\pythonProject\main.py", line 35, in new_part
np = [self.part_name.text, self.serial_number.text, self.on_hand_cnt.text, self.min_needed.text]
AttributeError: 'NoneType' object has no attribute 'text'
Edit:
Here is how I implemented Oussama's answer in order to get this to work, all I had to do was update the AddPartWindow class:
class AddPartWindow(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.np = []
def submit(self):
self.part_name = self.ids.part_name.text
self.serial_number = self.ids.serial_number.text
self.on_hand_cnt = self.ids.on_hand_cnt.text
self.min_needed = self.ids.min_needed.text
self.np = [self.part_name, self.serial_number, self.on_hand_cnt, self.min_needed]
iQue_sheet.append_row(self.np)
I also made this change in order to append to sheet 3 of the workbook instead of sheet 1:
iQue_sheet = client.open("InventoryBackend").get_worksheet(2)
this main.py feel free to import what u need. this a simple example of what u want to do
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.app import App
Builder.load_file('the.kv')
class fscreen(widget):
def __init__(self, **kwargs):
supper().__init__(**kwargs)
self.np = []
def submit(self):
self.part_name = self.ids.Pname.text
self.serial_number = self.ids.Snumber.text
self.on_hand_cnt = self.ids.onhabdcnt.text
self.min_needed = self.ids.minneeded.text
self.np = [self.part_name, self.serial_number, self.on_hand_cnt, self.min_needed]
iQue_sheet.append(self.np)
class secscreen(widget):
def __init__(self, **kwargs):
supper().__init__(**kwargs)
pass
class thscreen(widget):
def __init__(self, **kwargs):
supper().__init__(**kwargs)
pass
class theapp(App):
def build(self):
self.screenm = ScreenManager()
self.screen = Screen(name = "first screen")
screen.add_widget(self.fscreen)
self.screenm.add_widget(screen)
if __name__ = "__main__":
theapp.run()
this is the.kv file
<fscreen>
TextInput:
id: Pname
hint_text: 'partname'
TextInput:
id: Snumber
hint_text: 'serialnumber'
TextInput:
id: onhandcnt
hint_text: 'on hand cnt'
TextInput:
id: minneeded
hint_text: 'min nedded'
Button:
text: 'Submit'
on_press: root.submit()
I added a checkbox on my sign-in screen for saving the username and password information so the user will not have to enter them every time. I thought about creating a text file that will store this information, but maybe there is a better approach. I'm not able to save it properly; this is what I have so far. In the init method of my class, I'm checking to see if there is a text file with the information. If so, I want to extract the username and password to fill in the TextInputs on my screen. If not, I'll leave them empty and let the user fill in the two TextInputs. The Textinputs are taking care of in my next method add_user(). I get this error: AttributeError: 'super' object has no attribute '__getattr__'. I haven't figured out the checkbox behavior since I already an error. Does anyone have an idea?
try.py
class SigninWindows(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
if os.path.isfile('prev_details.txt'):
with open('prev_details.txt', 'r') as f:
d = f.read().split(',')
self.ids.username_field.text = d[0]
self.ids.pwd_field.text = d[1]
else:
self.ids.username_field.text = ''
self.ids.pwd_field.text = ''
def add_user(self):
uname = self.ids.username_field.text
passw = self.ids.pwd_field.text
info = self.ids.info
table_name = uname.replace('#', '_').replace('.', '_')
try.kv
<SigninWindows>:
id: signin_page
name: "signin_page"
orientation: "vertical"
spacing: 10
space_x: self.size[0]/5.5
canvas.before:
Color:
rgba: (0,0,0,1)
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
id: data_signin
orientation: 'vertical'
size_hint_x: 1
BoxLayout:
Image:
id: ds_im
orientation: 'vertical'
source: 'ds.png'
allow_stretch: True
BoxLayout:
id: validate_info
orientation: "vertical"
size_hint: 1,0.8
padding: 80, 10
Label:
id: info
text: ''
markup: True
TextInput:
id: username_field
text: ''
hint_text: "Username"
hint_text_color: 0.5,0.5,0.5,1
multiline: False
focus: True
on_text_validate: pwd_field.focus = True
size_hint: 1, .8
foreground_color: 0.5,0.5,0.5,1
background_color: .1,.1,.1,1
write_tab: False
TextInput:
id: pwd_field
text: ''
hint_text: "Password"
hint_text_color: 0.5,0.5,0.5,1
multiline: False
password: True
on_text_validate: root.validate_user()
size_hint: 1,0.8
foreground_color: 0.5,0.5,0.5,1
background_color: .1,.1,.1,1
BoxLayout:
id: remember_section
orientation : 'horizontal'
size_hint_x: None
width: 80
size_hint_y: None
height: 50
padding: 80, 10
CheckBox:
text: ''
size_hint_x: None
width: 25
size_hint_y: None
height: 25
canvas.before:
Color:
rgba: (1, 1, 1, 1)
Rectangle:
size: self.size
pos: self.pos
Label:
text: 'Remember User id?'
font_size: 20
size_hint_x: None
width: 220
The ids dictionary of SigninWindows has not yet been created when the __init__() method is running. You can move that code into another method and call it using Clock.schedule_once(). That will delay its execution very slightly until the ids are available.
There is not a much better way to do this rather than a text file. Import the OS module and do os.readfile(filepath and or if it is in the same folder as your .py file just the name) and read from there the rest is straight forward.
I am trying to build an App using Python and kivy.
In my kv file I wanted to create a custom widget (MenuFloatLayout)
which can be referenced by the other screens. It is basically a Menu bar that is on every screen. This bar consists of several toggle buttons that are in their down state and disabled if you are currently on the screen this button is linked to.
This is referenced by:
state: "down" if root.manager.current == 'Screenname' else "normal"
The Problem is:
root.manager.current is not linked to the usual screen manager anymore,
because my custom widget is the root now.
Is there a work around?
Or is there a easier way to link the toggle buttons state to the screen the user is on?
I am new to programming and Python, I'm glad for any help or tips you can give me! Thanks!
The Python file:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
class StartWindow(Screen):
pass
class PortfolioOverview(Screen):
pass
class Portfolio(Screen):
pass
class Market(Screen):
pass
class Economics(Screen):
pass
class PortfolioTools(Screen):
pass
class WindowManager(ScreenManager):
pass
kv = Builder.load_file("vigiles.kv")
class VigilesApp(App):
def build(self):
return kv
if __name__ == "__main__":
VigilesApp().run()
And the kv file:
WindowManager:
StartWindow:
PortfolioOverview:
Portfolio:
Market:
Economics:
PortfolioTools:
<MenuFloatLayout#FloatLayout>:
Label:
text: "Portfolio"
markup: True
size_hint: 0.5, None
height: 30
pos_hint:{"top":1, "left":1}
TextInput:
text: "Search"
multiline: False
size_hint: 0.5, None
height: 30
pos_hint:{"top":1, "right":1}
ScrollView:
size_hint: None, None
do_scroll_y: False
do_scroll_x: True
size: 500, 150
GridLayout:
rows: 1
size_hint_y: None
ToggleButton:
group: "pmenu"
text: 'Overview'
state: "down" if root.manager.current == 'poverview' else "normal"
disabled: True if root.manager.current == 'poverview' else False
background_disabled_down: "atlas://data/images/defaulttheme/button_pressed"
disabled_color: 1, 1, 1, 1
on_release: app.root.current = "poverview"
ToggleButton:
group: "pmenu"
text: 'Portfolio'
state: "down" if root.manager.current == 'portfolio' else "normal"
disabled: True if root.manager.current == 'portfolio' else False
background_disabled_down: "atlas://data/images/defaulttheme/button_pressed"
disabled_color: 1, 1, 1, 1
on_release: app.root.current = "portfolio"
ToggleButton:
group: "pmenu"
text: 'Market'
state: "down" if root.manager.current == 'market' else "normal"
disabled: True if root.manager.current == 'market' else False
background_disabled_down: "atlas://data/images/defaulttheme/button_pressed"
disabled_color: 1, 1, 1, 1
on_release: app.root.current = "market"
ToggleButton:
group: "pmenu"
text: 'Economics'
state: "down" if root.manager.current == 'economics' else "normal"
disabled: True if root.manager.current == 'economics' else False
background_disabled_down: "atlas://data/images/defaulttheme/button_pressed"
disabled_color: 1, 1, 1, 1
on_release: app.root.current = "economics"
ToggleButton:
group: "pmenu"
text: 'Tools'
state: "down" if root.manager.current == 'ptools' else "normal"
disabled: True if root.manager.current == 'ptools' else False
background_disabled_down: "atlas://data/images/defaulttheme/button_pressed"
disabled_color: 1, 1, 1, 1
on_release: app.root.current = "ptools"
<StartWindow>:
name: "start"
BoxLayout:
canvas:
Rectangle:
size: self.size
color: 1, 1, 1, 0
id: login_layout
orientation: 'vertical'
padding: [10,10,10,10]
spacing: 30
Label:
text: 'some text'
font_size: 32
color: 0, 0, 0, 1
BoxLayout:
orientation: 'vertical'
Label:
text: 'Login'
font_size: 18
halign: 'left'
text_size: root.width-20, 20
color: 0, 0, 0, 1
TextInput:
id: login
multiline:False
font_size: 28
BoxLayout:
orientation: 'vertical'
Label:
text: 'Password'
halign: 'left'
font_size: 18
text_size: root.width-20, 20
color: 0, 0, 0, 1
TextInput:
id: password
multiline:False
password:True
font_size: 28
Button:
text: 'Connect'
font_size: 24
on_release: app.root.current = "poverview"
<PortfolioOverview>:
name: "poverview"
MenuFloatLayout:
<Portfolio>:
name: "portfolio"
MenuFloatLayout:
<Market>:
name: "market"
MenuFloatLayout:
<Economics>:
name: "economics"
MenuFloatLayout:
<PortfolioTools>:
name: "ptools"
MenuFloatLayout:
Goal is to either link my custom widget back to my screen manager or find an easier solution to link the toggle buttons state to the current screen.
AttributeError: 'MenuFloatLayout' object has no attribute 'manager'
I would make your root widget a Layout widget (GridLayout, BoxLayout, or FloatLayout), and have your screen manager take up only part of the actual screen. Try changing
WindowManager:
StartWindow:
PortfolioOverview:
Portfolio:
to:
GridLayout:
# Play with using a FloatLayout or BoxLayout instead of GridLayout if you want
cols: 1
MenuFloatLayout:
id: the_menu_id
WindowManager:
StartWindow:
PortfolioOverview:
Portfolio:
That way your menu will persist and the screen manager is only changing part of the actual screen.
By giving the MenuFloatLayout an id you are able to reference it. To reference it from the Python side, use self.root.ids.the_menu_id if you're referencing it from your App object. If you're in some other kind of object, you can do the same by first importing App (import App) and then using App.get_running_app().root.ids.the_menu_id
To reference it from your screens, you can use the app keyword. For example: app.root.ids.the_menu_id
All three ways of referencing the menu's id are essentially identical.
Problems
Too many instances of MenuFloatLayout
Use lots of resources e.g. memory, this makes the app big and poor performance
Solution
Create one instance of MenuFloatLayout
Each screen reference the one and only one instance of MenuFloatLayout
Use nested ScreenManager. This first ScreenManager for controlling the authentication / login screen. The second ScreenManager for navigation between different screens.
Create a dynamic class for ToggleButton
Example
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
class StartWindow(Screen):
pass
class PortfolioOverview(Screen):
pass
class Portfolio(Screen):
pass
class Market(Screen):
pass
class Economics(Screen):
pass
class PortfolioTools(Screen):
pass
class WindowManager(ScreenManager):
pass
Builder.load_file("main.kv")
class TestApp(App):
def build(self):
return WindowManager()
if __name__ == "__main__":
TestApp().run()
main.kv - kv file
<WindowManager>:
sm2: sm2
StartWindow:
Screen:
name: 'connect'
ScreenManager:
id: sm2
PortfolioOverview:
Portfolio:
Market:
Economics:
PortfolioTools:
MenuFloatLayout:
<CustomToggleButton#ToggleButton>: # dynamic class
group: "pmenu"
state: "normal" if app.root is None else "down" if app.root.sm2.current == self.text.lower() else "normal"
background_disabled_down: "atlas://data/images/defaulttheme/button_pressed"
disabled_color: 1, 1, 1, 1
on_state:
if self.state == "down": self.disabled = True
else: self.disabled = False
on_release:
app.root.sm2.current = self.text.lower()
<MenuFloatLayout#FloatLayout>: # dynamic class
Label:
text: "Portfolio"
markup: True
size_hint: 0.5, None
height: 30
pos_hint:{"top":1, "left":1}
TextInput:
hint_text: "Search"
multiline: False
size_hint: 0.5, None
height: 30
pos_hint:{"top":1, "right":1}
ScrollView:
size_hint: None, None
do_scroll_y: False
do_scroll_x: True
size: 500, 150
GridLayout:
rows: 1
size_hint_y: None
CustomToggleButton:
text: 'Overview'
state: 'down' # default
CustomToggleButton:
text: 'Portfolio'
CustomToggleButton:
text: 'Market'
CustomToggleButton:
text: 'Economics'
CustomToggleButton:
text: 'Tools'
<StartWindow>:
name: "start"
BoxLayout:
canvas:
Rectangle:
size: self.size
color: 1, 1, 1, 0
id: login_layout
orientation: 'vertical'
padding: [10,10,10,10]
spacing: 30
Label:
text: 'some text'
font_size: 32
color: 0, 0, 0, 1
BoxLayout:
orientation: 'vertical'
Label:
text: 'Login'
font_size: 18
halign: 'left'
text_size: root.width-20, 20
color: 0, 0, 0, 1
TextInput:
id: login
multiline:False
font_size: 28
BoxLayout:
orientation: 'vertical'
Label:
text: 'Password'
halign: 'left'
font_size: 18
text_size: root.width-20, 20
color: 0, 0, 0, 1
TextInput:
id: password
multiline:False
password:True
font_size: 28
Button:
text: 'Connect'
font_size: 24
on_release:
root.manager.current = 'connect'
<PortfolioOverview>:
name: "overview"
Label:
text: 'Screen - Overview'
<Portfolio>:
name: "portfolio"
Label:
text: 'Screen - Portfolio'
<Market>:
name: "market"
Label:
text: 'Screen - Market'
<Economics>:
name: "economics"
Label:
text: 'Screen - Economics'
<PortfolioTools>:
name: "tools"
Label:
text: 'Screen - Portfolio Tools'
Output