如何使用 Kivy 中一个 Popup window 的信息更新字典中的两个键?
How to update two keys in a dictionary with info from one Popup window in Kivy?
我正在尝试使用 ColorsPopup
弹出窗口中的信息更新字典 collected_info
键 'Liners'
键 '4x20'
和 '8x20'
,但我正确执行时遇到问题。
想法是,当按下两个按钮中的任何一个时,会出现一个弹出窗口,其中包含多个彩色按钮。在弹出窗口中切换按钮后,它应该将这些颜色作为列表添加到相应的键('4x20'
或 '8x20'
)。
问题是,我无法实现所有必需的功能。
我目前的问题是,我无法为每个 collected_info
键(4x20 和 8x10)分离 liners
列表。
这是 MRE python 代码:
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)
这是 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)
我重新设计了 ColorsPopup
的工作方式,我觉得这个实现比仅仅将它写在 kv 文件中要好,但我仍然想不出正确的方法。
您的逻辑问题在于您在实例化默认为空的弹出窗口期间将 collected_info
修改为 popup.liners
的值。因此,当它在弹出窗口中发生变化时,您永远无法访问它。这可以通过多种方式解决。
一个简单的方法是观察 popup.liners
的变化并相应地更新您的 collected_info
。现在这正是 bind
所做的。为此,创建一个合适的 (kivy) 属性 并随时随地绑定回调 method/function。
下面是这个概念的实现。我添加了一个额外的 Label
来实时反映变化。其余已在评论中阐明。
已修改 .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)
已修改 .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'.
self.popup.bind(liners = partial(self.update_collected_info, tbtn)) # Pass the toggle button also for usage purpose.
# 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.
.
.
.
我正在尝试使用 ColorsPopup
弹出窗口中的信息更新字典 collected_info
键 'Liners'
键 '4x20'
和 '8x20'
,但我正确执行时遇到问题。
想法是,当按下两个按钮中的任何一个时,会出现一个弹出窗口,其中包含多个彩色按钮。在弹出窗口中切换按钮后,它应该将这些颜色作为列表添加到相应的键('4x20'
或 '8x20'
)。
问题是,我无法实现所有必需的功能。
我目前的问题是,我无法为每个 collected_info
键(4x20 和 8x10)分离 liners
列表。
这是 MRE python 代码:
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)
这是 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)
我重新设计了 ColorsPopup
的工作方式,我觉得这个实现比仅仅将它写在 kv 文件中要好,但我仍然想不出正确的方法。
您的逻辑问题在于您在实例化默认为空的弹出窗口期间将 collected_info
修改为 popup.liners
的值。因此,当它在弹出窗口中发生变化时,您永远无法访问它。这可以通过多种方式解决。
一个简单的方法是观察 popup.liners
的变化并相应地更新您的 collected_info
。现在这正是 bind
所做的。为此,创建一个合适的 (kivy) 属性 并随时随地绑定回调 method/function。
下面是这个概念的实现。我添加了一个额外的 Label
来实时反映变化。其余已在评论中阐明。
已修改 .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)
已修改 .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'.
self.popup.bind(liners = partial(self.update_collected_info, tbtn)) # Pass the toggle button also for usage purpose.
# 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.
.
.
.