I'm trying to update dictionary collected_info
key 'Liners'
keys '4x20'
and '8x20'
with info from a ColorsPopup
popup, but I'm having issues doing it correctly.
The idea is, that when either of the two buttons is pressed, a popup appears, that contains multiple coloured buttons inside. After you toggle buttons inside the popup, it should add those colours as a list to the corresponding key (either '4x20'
or '8x20'
).
The problem is, I cannot implement all the required functionality.
My current issue is that I can't separate list of liners
for each of the collected_info
keys - 4x20 and 8x10.
Here's MRE python code:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.properties import ObjectProperty, BooleanProperty, ListProperty
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.togglebutton import ToggleButton
class ProtocolInfoPage(Screen):
big = ObjectProperty(None)
small = ObjectProperty(None)
collected_info = {'Liners': {'4x20': list(), '8x10': list()}}
def open_popup(self, liner):
popup = ColorsPopup()
if liner == '4x20' and self.big.state == 'down':
popup.popupWindow.open()
self.collected_info['Liners']['4x20'] = popup.liners
elif liner == '4x20' and self.big.state == 'normal':
self.collected_info['Liners']['4x20'] = []
if liner == '8x10' and self.small.state == 'down':
popup.popupWindow.open()
self.collected_info['Liners']['8x10'] = popup.liners
elif liner == '8x10' and self.small.state == 'normal':
self.collected_info['Liners']['8x10'] = []
class ColorsPopup(Screen):
liners = list()
colors = ['GB', 'BL', 'GR', 'RT', 'SW', 'BR', 'TR']
def __init__(self, **kwargs):
super(ColorsPopup, self).__init__(**kwargs)
main_layout = BoxLayout(orientation='vertical')
layout = GridLayout(cols=3, size_hint=(.7, .7), pos_hint={'center_x': .5})
self.popupWindow = Popup(title='Flatliner Colors', content=main_layout, size_hint=(1, .5), auto_dismiss=False)
close_btn = Button(text='Choose Colors', size_hint=(.7, .3), pos_hint={'center_x': .5})
close_btn.bind(on_press=self.popupWindow.dismiss)
for color in self.colors:
color_btn = ToggleButton(text=color)
color_btn.bind(state=self.adding_removing_colors)
if color == 'GB':
color_btn.background_color = (1, 1, 0, 1)
elif color == 'BL':
color_btn.background_color = (0, 0, 1, 1)
elif color == 'GR':
color_btn.background_color = (0, 1, 0, 1)
elif color == 'RT':
color_btn.background_color = (1, 0, 0, 1)
elif color == 'SW':
color_btn.background_color = (0, 0, 0, 1)
elif color == 'BR':
color_btn.background_color = (.5, .5, .3, 1)
layout.add_widget(color_btn)
main_layout.add_widget(layout)
main_layout.add_widget(close_btn)
def adding_removing_colors(self, color, state):
if state == 'down':
self.liners.append(color.text)
elif state == 'normal':
self.liners.remove(color.text)
print(self.liners)
kv = Builder.load_file("kivymd.kv")
class MyApp(App):
def build(self):
return kv
if __name__ == '__main__':
MyApp().run()
print(ProtocolInfoPage.collected_info)
And here's kv:
ProtocolInfoPage:
name: 'second'
big: big_liners
small: small_liners
BoxLayout:
orientation: 'vertical'
GridLayout:
cols:2
Label:
text: 'Liners'
GridLayout:
cols:2
ToggleButton:
id: big_liners
text: '4x20'
on_release:
root.open_popup(self.text)
ToggleButton:
id: small_liners
text: '8x10'
on_release:
root.open_popup(self.text)
I have reworked how ColorsPopup
work, and I feel like this implementation is better than just writing it in kv file, but I still cant figure out a way to do it correctly.
The problem with your logic is you are modifying collected_info
with the value of popup.liners
during instantiation of popup which is empty by default. Hence you never can access it when it changes inside the popup. This can be solved in various ways.
One simple way is to observe the changes of popup.liners
and update your collected_info
accordingly. Now that's exactly what bind
does. For that, create a suitable (kivy) property and bind a callback method/function whenever or wherever you need.
The following is the implementation of this concept. I have added an extra Label
to reflect the changes in real time. Rest have been clarified in comments.
Modified .kv
,
ProtocolInfoPage:
name: 'second'
# big: big_liners
# small: small_liners
BoxLayout:
orientation: 'vertical'
Label: # To visualize the change. Optional.
id: info
size_hint_y: 0.25
GridLayout:
cols:2
Label:
text: 'Liners'
GridLayout:
cols:2
ToggleButton:
# id: big_liners
text: '4x20'
on_state: root.select_option(self) # Pass the instance.
# on_release:
# root.open_popup(self.text)
ToggleButton:
# id: small_liners
text: '8x10'
on_state: root.select_option(self)
# on_release:
# root.open_popup(self.text)
Modified .py
,
class ProtocolInfoPage(Screen):
big = ObjectProperty(None)
small = ObjectProperty(None)
collected_info = {'Liners': {'4x20': list(), '8x10': list()}}
def select_option(self, tbtn):
# Create an instance and save it to self.
self.popup = ColorsPopup()
# Now bind a callback function to it in order to listen to any change in its prop. 'liners'.
# Pass the toggle button also for usage purpose.
self.popup.bind(liners = lambda *args : self.update_collected_info(tbtn, *args))
# Using partial.
# self.popup.bind(liners = partial(self.update_collected_info, tbtn))
# Set logic.
if tbtn.state == "normal":
self.collected_info['Liners'][tbtn.text] = []
else: # i.e. when tbtn.state is "down", open the popup.
self.popup.popupWindow.open()
# Update the info. Optional.
self.ids.info.text = str(self.collected_info)
def update_collected_info(self, tbtn, instance, value):
"""This method will be triggered whenever the prop. 'liners' of ColorsPopup changes."""
self.collected_info['Liners'][tbtn.text] = value
# Update the info. Optional.
self.ids.info.text = str(self.collected_info)
class ColorsPopup(Screen):
liners = ListProperty([ ]) # Make it a kivy property in order to listen to its changes automatically.
.
.
.