Python kivy(kivymd)如何从MD存储和加载数据
Python kivy (kivymd) how to store and load data from MD
我想从 TextField(这里是来自 kivymd 的 MDTextField)存储数据和加载数据。我添加了 on_application_close: ... 和 on_application_open: ... 作为原型,因为我不知道该怎么做以及如何将它连接到 main.py 文件其中包含功能。所以我希望用户能够在 TextField 中输入一些值,如果用户关闭并重新打开应用程序,这些值应该仍然存在。我在文档中查找了存储功能,但我不清楚如何正确使用它。
MDTextField:
id: first_text_field
hint_text: "Helper text on focus"
helper_text: "This will disappear when you click off"
helper_text_mode: "on_focus"
input_filter: "int"
on_application_close: store_content(first_text_field, first_text_field.text)
on_application_open: set_text(load_content(first_text_field))
编辑: 这是更新后的代码(main.py、main.kv 和 labels.py)。所有文件都在项目文件夹的文件夹应用程序中。我还在同一个文件夹中添加了一个空 myBackup.json。
这是 labels.py 文件
# labels.py
APPLICATION_NAME = "Application"
CLOSE_APPLICATION = "Close Application"
NAVIGATION = "Navigation"
SYSTEM_PARAMETERS = "System Parameters"
TEXT_ON_CLOSE_APPLICATION = "Are your sure?"
TEXT_ON_CLOSE_APPLICATION_BUTTON_CLOSE = "Close Application"
CONTROLLER_PARAMETER_1_HINT_TEXT = "Controller Parameter 1"
CONTROLLER_PARAMETER_1_HELPER_TEXT = "This parameter controls damping ratio."
CONTROLLER_PARAMETER_2_HINT_TEXT = "Controller Parameter 2"
CONTROLLER_PARAMETER_2_HELPER_TEXT = "This parameter controls P component of the PID controller."
更新代码: 我没有收到错误,但是 myBackup.json 文件在通过关闭按钮(X 按钮)退出应用程序后保持为空window.
# main.py
# -*- coding: utf-8 -*-
import os
# Set virtual keyboard
from kivy.app import App
from kivy.config import Config
from kivy.storage.jsonstore import JsonStore
from kivy.uix.boxlayout import BoxLayout
Config.set('kivy', 'keyboard_mode', 'systemandmulti')
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.core.window import Window
# Window.fullscreen = "auto"
from kivy.lang import Builder
from functools import partial
from kivymd.list import BaseListItem
from kivymd.material_resources import DEVICE_TYPE
from kivymd.navigationdrawer import MDNavigationDrawer, NavigationDrawerHeaderBase
from kivymd.theming import ThemeManager
from application.labels import *
class HackedDemoNavDrawer(MDNavigationDrawer):
# DO NOT USE
def add_widget(self, widget, index=0):
if issubclass(widget.__class__, BaseListItem):
self._list.add_widget(widget, index)
if len(self._list.children) == 1:
widget._active = True
self.active_item = widget
# widget.bind(on_release=lambda x: self.panel.toggle_state())
widget.bind(on_release=lambda x: x._set_active(True, list=self))
elif issubclass(widget.__class__, NavigationDrawerHeaderBase):
self._header_container.add_widget(widget)
else:
super(MDNavigationDrawer, self).add_widget(widget, index)
class MainApp(App):
theme_cls = ThemeManager()
title = APPLICATION_NAME
def build(self):
main_widget = Builder.load_file(
os.path.join(os.path.dirname(__file__), "./main.kv")
)
self.theme_cls.theme_style = 'Dark'
main_widget.ids.text_field_error.bind(
on_text_validate=self.set_error_message,
on_focus=self.set_error_message)
self.bottom_navigation_remove_mobile(main_widget)
return main_widget
def stop(self, *largs):
# Open the popup you want to open and declare callback if user pressed `Yes`
popup = ExitPopup(title=TEXT_ON_CLOSE_APPLICATION,
content=Button(text=TEXT_ON_CLOSE_APPLICATION_BUTTON_CLOSE),
size=(400, 400), size_hint=(None, None)
)
popup.bind(on_confirm=partial(self.close_app, *largs))
popup.open()
def close_app(self, *largs):
super(MainApp, self).stop(*largs)
def bottom_navigation_remove_mobile(self, widget):
# Removes some items from bottom-navigation demo when on mobile
if DEVICE_TYPE == 'mobile':
widget.ids.bottom_navigation_demo.remove_widget(widget.ids.bottom_navigation_desktop_2)
if DEVICE_TYPE == 'mobile' or DEVICE_TYPE == 'tablet':
widget.ids.bottom_navigation_demo.remove_widget(widget.ids.bottom_navigation_desktop_1)
def set_error_message(self, *args):
if len(self.root.ids.text_field_error.text) == 2:
self.root.ids.text_field_error.error = True
else:
self.root.ids.text_field_error.error = False
def on_pause(self):
return True
def on_text_validate_callback(self, instance):
print(instance.text)
def on_start(self):
print("\non_start:")
store = JsonStore('myBackup.json')
if store.count() > 0:
for key in store:
print("\tid={0}, obj={1}".format(key, self.root.ids[key]))
if isinstance(self.root.ids[key], MDTextField):
self.root.ids[key].text = store.get(key)['text']
print("\t\ttext=", self.root.ids[key].text)
def on_stop(self):
print("\non_stop:")
store = JsonStore('myBackup.json')
for key in self.root.ids:
if isinstance(self.root.ids[key], MDTextField):
print("\tid={0}, text={1}".format(key, self.root.ids[key].text))
store.put(key, text=self.root.ids[key].text)
class ExitPopup(Popup):
def __init__(self, **kwargs):
super(ExitPopup, self).__init__(**kwargs)
self.register_event_type('on_confirm')
def on_confirm(self):
pass
def on_button_yes(self):
self.dispatch('on_confirm')
if __name__ == '__main__':
MainApp().run()
这是 main.kv 文件。 labels.py 文件未更改
# main.kv
#:kivy 1.10.1
#:import Toolbar kivymd.toolbar.Toolbar
#:import MDNavigationDrawer application.kivymd.navigationdrawer.MDNavigationDrawer
#:import NavigationLayout application.kivymd.navigationdrawer.NavigationLayout
#:import NavigationDrawerDivider application.kivymd.navigationdrawer.NavigationDrawerDivider
#:import NavigationDrawerToolbar application.kivymd.navigationdrawer.NavigationDrawerToolbar
#:import NavigationDrawerSubheader application.kivymd.navigationdrawer.NavigationDrawerSubheader
#:import MDTextField application.kivymd.textfields.MDTextField
#:import labels application.labels
NavigationLayout:
id: nav_layout
MDNavigationDrawer:
id: nav_drawer
NavigationDrawerToolbar:
title: labels.NAVIGATION
NavigationDrawerIconButton:
icon: 'checkbox-blank-circle'
text: labels.SYSTEM_PARAMETERS
on_release: app.root.ids.scr_mngr.current = 'system_parameters'
NavigationDrawerIconButton:
icon: "checkbox-blank-circle"
text: labels.CLOSE_APPLICATION
on_release: app.stop()
BoxLayout:
orientation: 'vertical'
halign: "center"
Toolbar:
id: toolbar
title: labels.APPLICATION_NAME
md_bg_color: app.theme_cls.primary_color
background_palette: 'Primary'
background_hue: '500'
left_action_items: [['menu', lambda x: app.root.toggle_nav_drawer()]]
#right_action_items: [['dots-vertical', lambda x: app.root.toggle_nav_drawer()]]
ScreenManager:
id: scr_mngr
Screen:
name: 'system_parameters'
BoxLayout:
orientation: "horizontal"
BoxLayout:
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
padding: dp(48)
spacing: 10
MDTextField:
id: controller_parameter_1
hint_text: labels.CONTROLLER_PARAMETER_1_HINT_TEXT
helper_text: labels.CONTROLLER_PARAMETER_1_HELPER_TEXT
helper_text_mode: "on_focus"
input_filter: "int"
on_text_validate: app.on_text_validate_callback(self)
BoxLayout:
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
padding: dp(48)
spacing: 10
MDTextField:
id: controller_parameter_2
hint_text: labels.CONTROLLER_PARAMETER_2_HINT_TEXT
helper_text: labels.CONTROLLER_PARAMETER_2_HELPER_TEXT
helper_text_mode: "on_focus"
input_filter: "float"
on_text_validate: app.on_text_validate_callback(self)
Screen:
name: 'textfields'
ScrollView:
BoxLayout:
orientation: 'vertical'
MDTextField:
id: text_field_error
hint_text: "Helper text on error (Hit Enter with two characters here)"
helper_text: "Two is my least favorite number"
helper_text_mode: "on_error"
Screen:
name: 'nav_drawer'
HackedDemoNavDrawer:
# NavigationDrawerToolbar:
# title: "Navigation Drawer Widgets"
NavigationDrawerIconButton:
icon: 'checkbox-blank-circle'
text: "Badge text ---->"
badge_text: "99+"
NavigationDrawerIconButton:
active_color_type: 'accent'
text: "Accent active color"
NavigationDrawerIconButton:
active_color_type: 'custom'
text: "Custom active color"
active_color: [1, 0, 1, 1]
NavigationDrawerIconButton:
use_active: False
text: "Use active = False"
NavigationDrawerIconButton:
text: "Different icon"
icon: 'alarm'
NavigationDrawerDivider:
NavigationDrawerSubheader:
text: "NavigationDrawerSubheader"
NavigationDrawerIconButton:
text: "NavigationDrawerDivider \/"
NavigationDrawerDivider:
问题 2
I don't get an error, but the myBackup.json file stays empty after
exiting the application via the close button (X Button) of the window.
解决方案 2
使用Window.bind(on_close=self.on_stop)
示例 2
app.py
# app.py
# -*- coding: utf-8 -*-
import os
from functools import partial
from kivy.app import App
from kivy.storage.jsonstore import JsonStore
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.config import Config
# Set virtual keyboard
Config.set('kivy', 'keyboard_mode', 'systemandmulti')
from kivy.core.window import Window
# Window.fullscreen = "auto"
from kivymd.list import BaseListItem
from kivymd.material_resources import DEVICE_TYPE
from kivymd.navigationdrawer import MDNavigationDrawer, NavigationDrawerHeaderBase
from kivymd.textfields import MDTextField
from kivymd.theming import ThemeManager
from labels import *
class HackedDemoNavDrawer(MDNavigationDrawer):
# DO NOT USE
def add_widget(self, widget, index=0):
if issubclass(widget.__class__, BaseListItem):
self._list.add_widget(widget, index)
if len(self._list.children) == 1:
widget._active = True
self.active_item = widget
# widget.bind(on_release=lambda x: self.panel.toggle_state())
widget.bind(on_release=lambda x: x._set_active(True, list=self))
elif issubclass(widget.__class__, NavigationDrawerHeaderBase):
self._header_container.add_widget(widget)
else:
super(MDNavigationDrawer, self).add_widget(widget, index)
class MainApp(App):
theme_cls = ThemeManager()
title = APPLICATION_NAME
def __init__(self, **kwargs):
super(MainApp, self).__init__(**kwargs)
Window.bind(on_close=self.on_stop)
def build(self):
main_widget = Builder.load_file(
os.path.join(os.path.dirname(__file__), "./main.kv")
)
self.theme_cls.theme_style = 'Dark'
main_widget.ids.text_field_error.bind(
on_text_validate=self.set_error_message,
on_focus=self.set_error_message)
self.bottom_navigation_remove_mobile(main_widget)
return main_widget
def stop(self, *largs):
# Open the popup you want to open and declare callback if user pressed `Yes`
popup = ExitPopup(title=TEXT_ON_CLOSE_APPLICATION,
content=Button(text=TEXT_ON_CLOSE_APPLICATION_BUTTON_CLOSE),
size=(400, 400), size_hint=(None, None)
)
popup.bind(on_confirm=partial(self.close_app, *largs))
popup.open()
def close_app(self, *largs):
super(MainApp, self).stop(*largs)
def bottom_navigation_remove_mobile(self, widget):
# Removes some items from bottom-navigation demo when on mobile
if DEVICE_TYPE == 'mobile':
widget.ids.bottom_navigation_demo.remove_widget(widget.ids.bottom_navigation_desktop_2)
if DEVICE_TYPE == 'mobile' or DEVICE_TYPE == 'tablet':
widget.ids.bottom_navigation_demo.remove_widget(widget.ids.bottom_navigation_desktop_1)
def set_error_message(self, *args):
if len(self.root.ids.text_field_error.text) == 2:
self.root.ids.text_field_error.error = True
else:
self.root.ids.text_field_error.error = False
def on_pause(self):
return True
def on_text_validate_callback(self, instance):
print(instance.text)
def on_start(self):
print("\non_start:")
store = JsonStore('myBackup.json')
if store.count() > 0:
for key in store:
print("\tid={0}, obj={1}".format(key, self.root.ids[key]))
if isinstance(self.root.ids[key], MDTextField):
self.root.ids[key].text = store.get(key)['text']
print("\t\ttext=", self.root.ids[key].text)
def on_stop(self, *args):
print("\non_stop:")
store = JsonStore('myBackup.json')
for key in self.root.ids:
if isinstance(self.root.ids[key], MDTextField):
print("\tid={0}, text={1}".format(key, self.root.ids[key].text))
store.put(key, text=self.root.ids[key].text)
class ExitPopup(Popup):
def __init__(self, **kwargs):
super(ExitPopup, self).__init__(**kwargs)
self.register_event_type('on_confirm')
def on_confirm(self):
pass
def on_button_yes(self):
self.dispatch('on_confirm')
if __name__ == '__main__':
MainApp().run()
main.kv
#:kivy 1.11.0
#:import Toolbar kivymd.toolbar.Toolbar
#:import MDNavigationDrawer kivymd.navigationdrawer.MDNavigationDrawer
#:import NavigationLayout kivymd.navigationdrawer.NavigationLayout
#:import NavigationDrawerDivider kivymd.navigationdrawer.NavigationDrawerDivider
#:import NavigationDrawerToolbar kivymd.navigationdrawer.NavigationDrawerToolbar
#:import NavigationDrawerSubheader kivymd.navigationdrawer.NavigationDrawerSubheader
#:import MDTextField kivymd.textfields.MDTextField
#:import labels labels
NavigationLayout:
id: nav_layout
MDNavigationDrawer:
id: nav_drawer
NavigationDrawerToolbar:
title: labels.NAVIGATION
NavigationDrawerIconButton:
icon: 'checkbox-blank-circle'
text: labels.SYSTEM_PARAMETERS
on_release: app.root.ids.scr_mngr.current = 'system_parameters'
NavigationDrawerIconButton:
icon: "checkbox-blank-circle"
text: labels.CLOSE_APPLICATION
on_release: app.stop()
BoxLayout:
orientation: 'vertical'
halign: "center"
Toolbar:
id: toolbar
title: labels.APPLICATION_NAME
md_bg_color: app.theme_cls.primary_color
background_palette: 'Primary'
background_hue: '500'
left_action_items: [['menu', lambda x: app.root.toggle_nav_drawer()]]
#right_action_items: [['dots-vertical', lambda x: app.root.toggle_nav_drawer()]]
ScreenManager:
id: scr_mngr
Screen:
name: 'system_parameters'
BoxLayout:
orientation: "horizontal"
BoxLayout:
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
padding: dp(48)
spacing: 10
MDTextField:
id: controller_parameter_1
hint_text: labels.CONTROLLER_PARAMETER_1_HINT_TEXT
helper_text: labels.CONTROLLER_PARAMETER_1_HELPER_TEXT
helper_text_mode: "on_focus"
input_filter: "int"
on_text_validate: app.on_text_validate_callback(self) # Fix 1
BoxLayout:
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
padding: dp(48)
spacing: 10
MDTextField:
id: controller_parameter_2
hint_text: labels.CONTROLLER_PARAMETER_2_HINT_TEXT
helper_text: labels.CONTROLLER_PARAMETER_2_HELPER_TEXT
helper_text_mode: "on_focus"
input_filter: "float"
on_text_validate: app.on_text_validate_callback(self) # Fix 2
Screen:
name: 'textfields'
ScrollView:
BoxLayout:
orientation: 'vertical'
MDTextField:
id: text_field_error
hint_text: "Helper text on error (Hit Enter with two characters here)"
helper_text: "Two is my least favorite number"
helper_text_mode: "on_error"
Screen:
name: 'nav_drawer'
HackedDemoNavDrawer:
# NavigationDrawerToolbar:
# title: "Navigation Drawer Widgets"
NavigationDrawerIconButton:
icon: 'checkbox-blank-circle'
text: "Badge text ---->"
badge_text: "99+"
NavigationDrawerIconButton:
active_color_type: 'accent'
text: "Accent active color"
NavigationDrawerIconButton:
active_color_type: 'custom'
text: "Custom active color"
active_color: [1, 0, 1, 1]
NavigationDrawerIconButton:
use_active: False
text: "Use active = False"
NavigationDrawerIconButton:
text: "Different icon"
icon: 'alarm'
NavigationDrawerDivider:
NavigationDrawerSubheader:
text: "NavigationDrawerSubheader"
NavigationDrawerIconButton:
text: "NavigationDrawerDivider \/"
NavigationDrawerDivider:
输出
详情请参考示例
- 使用Kivy Storage
- 使用应用程序事件,
on_start
和 on_stop
。
- 添加导入语句,
from kivymd.textfields import MDTextField
on_start:
Fired when the application is being started (before the runTouchApp()
call.
on_stop:
Fired when the application stops.
例子
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivymd.theming import ThemeManager
from kivymd.textfields import MDTextField
from kivy.storage.jsonstore import JsonStore
class RootWidget(BoxLayout):
def on_text_validate_callback(self, instance):
print(instance.text)
class TestApp(App):
theme_cls = ThemeManager()
def build(self):
return RootWidget()
def on_start(self):
print("\non_start:")
store = JsonStore('myBackup.json')
if store.count() > 0:
for key in store:
print("\tid={0}, obj={1}".format(key, self.root.ids[key]))
if isinstance(self.root.ids[key], MDTextField):
self.root.ids[key].text = store.get(key)['text']
print("\t\ttext=", self.root.ids[key].text)
def on_stop(self):
print("\non_stop:")
store = JsonStore('myBackup.json')
for key in self.root.ids:
if isinstance(self.root.ids[key], MDTextField):
print("\tid={0}, text={1}".format(key, self.root.ids[key].text))
store.put(key, text=self.root.ids[key].text)
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.11.0
#:import MDTextField kivymd.textfields.MDTextField
<RootWidget>:
MDTextField:
id: first_text_field
color: 0, 0, 0, 1 # black text color
focus: True
hint_text: "Helper text on focus"
helper_text: "This will disappear when you click off"
helper_text_mode: "on_focus"
input_filter: "int"
on_text_validate: root.on_text_validate_callback(self)
输出
我想从 TextField(这里是来自 kivymd 的 MDTextField)存储数据和加载数据。我添加了 on_application_close: ... 和 on_application_open: ... 作为原型,因为我不知道该怎么做以及如何将它连接到 main.py 文件其中包含功能。所以我希望用户能够在 TextField 中输入一些值,如果用户关闭并重新打开应用程序,这些值应该仍然存在。我在文档中查找了存储功能,但我不清楚如何正确使用它。
MDTextField:
id: first_text_field
hint_text: "Helper text on focus"
helper_text: "This will disappear when you click off"
helper_text_mode: "on_focus"
input_filter: "int"
on_application_close: store_content(first_text_field, first_text_field.text)
on_application_open: set_text(load_content(first_text_field))
编辑: 这是更新后的代码(main.py、main.kv 和 labels.py)。所有文件都在项目文件夹的文件夹应用程序中。我还在同一个文件夹中添加了一个空 myBackup.json。
这是 labels.py 文件
# labels.py
APPLICATION_NAME = "Application"
CLOSE_APPLICATION = "Close Application"
NAVIGATION = "Navigation"
SYSTEM_PARAMETERS = "System Parameters"
TEXT_ON_CLOSE_APPLICATION = "Are your sure?"
TEXT_ON_CLOSE_APPLICATION_BUTTON_CLOSE = "Close Application"
CONTROLLER_PARAMETER_1_HINT_TEXT = "Controller Parameter 1"
CONTROLLER_PARAMETER_1_HELPER_TEXT = "This parameter controls damping ratio."
CONTROLLER_PARAMETER_2_HINT_TEXT = "Controller Parameter 2"
CONTROLLER_PARAMETER_2_HELPER_TEXT = "This parameter controls P component of the PID controller."
更新代码: 我没有收到错误,但是 myBackup.json 文件在通过关闭按钮(X 按钮)退出应用程序后保持为空window.
# main.py
# -*- coding: utf-8 -*-
import os
# Set virtual keyboard
from kivy.app import App
from kivy.config import Config
from kivy.storage.jsonstore import JsonStore
from kivy.uix.boxlayout import BoxLayout
Config.set('kivy', 'keyboard_mode', 'systemandmulti')
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.core.window import Window
# Window.fullscreen = "auto"
from kivy.lang import Builder
from functools import partial
from kivymd.list import BaseListItem
from kivymd.material_resources import DEVICE_TYPE
from kivymd.navigationdrawer import MDNavigationDrawer, NavigationDrawerHeaderBase
from kivymd.theming import ThemeManager
from application.labels import *
class HackedDemoNavDrawer(MDNavigationDrawer):
# DO NOT USE
def add_widget(self, widget, index=0):
if issubclass(widget.__class__, BaseListItem):
self._list.add_widget(widget, index)
if len(self._list.children) == 1:
widget._active = True
self.active_item = widget
# widget.bind(on_release=lambda x: self.panel.toggle_state())
widget.bind(on_release=lambda x: x._set_active(True, list=self))
elif issubclass(widget.__class__, NavigationDrawerHeaderBase):
self._header_container.add_widget(widget)
else:
super(MDNavigationDrawer, self).add_widget(widget, index)
class MainApp(App):
theme_cls = ThemeManager()
title = APPLICATION_NAME
def build(self):
main_widget = Builder.load_file(
os.path.join(os.path.dirname(__file__), "./main.kv")
)
self.theme_cls.theme_style = 'Dark'
main_widget.ids.text_field_error.bind(
on_text_validate=self.set_error_message,
on_focus=self.set_error_message)
self.bottom_navigation_remove_mobile(main_widget)
return main_widget
def stop(self, *largs):
# Open the popup you want to open and declare callback if user pressed `Yes`
popup = ExitPopup(title=TEXT_ON_CLOSE_APPLICATION,
content=Button(text=TEXT_ON_CLOSE_APPLICATION_BUTTON_CLOSE),
size=(400, 400), size_hint=(None, None)
)
popup.bind(on_confirm=partial(self.close_app, *largs))
popup.open()
def close_app(self, *largs):
super(MainApp, self).stop(*largs)
def bottom_navigation_remove_mobile(self, widget):
# Removes some items from bottom-navigation demo when on mobile
if DEVICE_TYPE == 'mobile':
widget.ids.bottom_navigation_demo.remove_widget(widget.ids.bottom_navigation_desktop_2)
if DEVICE_TYPE == 'mobile' or DEVICE_TYPE == 'tablet':
widget.ids.bottom_navigation_demo.remove_widget(widget.ids.bottom_navigation_desktop_1)
def set_error_message(self, *args):
if len(self.root.ids.text_field_error.text) == 2:
self.root.ids.text_field_error.error = True
else:
self.root.ids.text_field_error.error = False
def on_pause(self):
return True
def on_text_validate_callback(self, instance):
print(instance.text)
def on_start(self):
print("\non_start:")
store = JsonStore('myBackup.json')
if store.count() > 0:
for key in store:
print("\tid={0}, obj={1}".format(key, self.root.ids[key]))
if isinstance(self.root.ids[key], MDTextField):
self.root.ids[key].text = store.get(key)['text']
print("\t\ttext=", self.root.ids[key].text)
def on_stop(self):
print("\non_stop:")
store = JsonStore('myBackup.json')
for key in self.root.ids:
if isinstance(self.root.ids[key], MDTextField):
print("\tid={0}, text={1}".format(key, self.root.ids[key].text))
store.put(key, text=self.root.ids[key].text)
class ExitPopup(Popup):
def __init__(self, **kwargs):
super(ExitPopup, self).__init__(**kwargs)
self.register_event_type('on_confirm')
def on_confirm(self):
pass
def on_button_yes(self):
self.dispatch('on_confirm')
if __name__ == '__main__':
MainApp().run()
这是 main.kv 文件。 labels.py 文件未更改
# main.kv
#:kivy 1.10.1
#:import Toolbar kivymd.toolbar.Toolbar
#:import MDNavigationDrawer application.kivymd.navigationdrawer.MDNavigationDrawer
#:import NavigationLayout application.kivymd.navigationdrawer.NavigationLayout
#:import NavigationDrawerDivider application.kivymd.navigationdrawer.NavigationDrawerDivider
#:import NavigationDrawerToolbar application.kivymd.navigationdrawer.NavigationDrawerToolbar
#:import NavigationDrawerSubheader application.kivymd.navigationdrawer.NavigationDrawerSubheader
#:import MDTextField application.kivymd.textfields.MDTextField
#:import labels application.labels
NavigationLayout:
id: nav_layout
MDNavigationDrawer:
id: nav_drawer
NavigationDrawerToolbar:
title: labels.NAVIGATION
NavigationDrawerIconButton:
icon: 'checkbox-blank-circle'
text: labels.SYSTEM_PARAMETERS
on_release: app.root.ids.scr_mngr.current = 'system_parameters'
NavigationDrawerIconButton:
icon: "checkbox-blank-circle"
text: labels.CLOSE_APPLICATION
on_release: app.stop()
BoxLayout:
orientation: 'vertical'
halign: "center"
Toolbar:
id: toolbar
title: labels.APPLICATION_NAME
md_bg_color: app.theme_cls.primary_color
background_palette: 'Primary'
background_hue: '500'
left_action_items: [['menu', lambda x: app.root.toggle_nav_drawer()]]
#right_action_items: [['dots-vertical', lambda x: app.root.toggle_nav_drawer()]]
ScreenManager:
id: scr_mngr
Screen:
name: 'system_parameters'
BoxLayout:
orientation: "horizontal"
BoxLayout:
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
padding: dp(48)
spacing: 10
MDTextField:
id: controller_parameter_1
hint_text: labels.CONTROLLER_PARAMETER_1_HINT_TEXT
helper_text: labels.CONTROLLER_PARAMETER_1_HELPER_TEXT
helper_text_mode: "on_focus"
input_filter: "int"
on_text_validate: app.on_text_validate_callback(self)
BoxLayout:
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
padding: dp(48)
spacing: 10
MDTextField:
id: controller_parameter_2
hint_text: labels.CONTROLLER_PARAMETER_2_HINT_TEXT
helper_text: labels.CONTROLLER_PARAMETER_2_HELPER_TEXT
helper_text_mode: "on_focus"
input_filter: "float"
on_text_validate: app.on_text_validate_callback(self)
Screen:
name: 'textfields'
ScrollView:
BoxLayout:
orientation: 'vertical'
MDTextField:
id: text_field_error
hint_text: "Helper text on error (Hit Enter with two characters here)"
helper_text: "Two is my least favorite number"
helper_text_mode: "on_error"
Screen:
name: 'nav_drawer'
HackedDemoNavDrawer:
# NavigationDrawerToolbar:
# title: "Navigation Drawer Widgets"
NavigationDrawerIconButton:
icon: 'checkbox-blank-circle'
text: "Badge text ---->"
badge_text: "99+"
NavigationDrawerIconButton:
active_color_type: 'accent'
text: "Accent active color"
NavigationDrawerIconButton:
active_color_type: 'custom'
text: "Custom active color"
active_color: [1, 0, 1, 1]
NavigationDrawerIconButton:
use_active: False
text: "Use active = False"
NavigationDrawerIconButton:
text: "Different icon"
icon: 'alarm'
NavigationDrawerDivider:
NavigationDrawerSubheader:
text: "NavigationDrawerSubheader"
NavigationDrawerIconButton:
text: "NavigationDrawerDivider \/"
NavigationDrawerDivider:
问题 2
I don't get an error, but the myBackup.json file stays empty after exiting the application via the close button (X Button) of the window.
解决方案 2
使用Window.bind(on_close=self.on_stop)
示例 2
app.py
# app.py
# -*- coding: utf-8 -*-
import os
from functools import partial
from kivy.app import App
from kivy.storage.jsonstore import JsonStore
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.config import Config
# Set virtual keyboard
Config.set('kivy', 'keyboard_mode', 'systemandmulti')
from kivy.core.window import Window
# Window.fullscreen = "auto"
from kivymd.list import BaseListItem
from kivymd.material_resources import DEVICE_TYPE
from kivymd.navigationdrawer import MDNavigationDrawer, NavigationDrawerHeaderBase
from kivymd.textfields import MDTextField
from kivymd.theming import ThemeManager
from labels import *
class HackedDemoNavDrawer(MDNavigationDrawer):
# DO NOT USE
def add_widget(self, widget, index=0):
if issubclass(widget.__class__, BaseListItem):
self._list.add_widget(widget, index)
if len(self._list.children) == 1:
widget._active = True
self.active_item = widget
# widget.bind(on_release=lambda x: self.panel.toggle_state())
widget.bind(on_release=lambda x: x._set_active(True, list=self))
elif issubclass(widget.__class__, NavigationDrawerHeaderBase):
self._header_container.add_widget(widget)
else:
super(MDNavigationDrawer, self).add_widget(widget, index)
class MainApp(App):
theme_cls = ThemeManager()
title = APPLICATION_NAME
def __init__(self, **kwargs):
super(MainApp, self).__init__(**kwargs)
Window.bind(on_close=self.on_stop)
def build(self):
main_widget = Builder.load_file(
os.path.join(os.path.dirname(__file__), "./main.kv")
)
self.theme_cls.theme_style = 'Dark'
main_widget.ids.text_field_error.bind(
on_text_validate=self.set_error_message,
on_focus=self.set_error_message)
self.bottom_navigation_remove_mobile(main_widget)
return main_widget
def stop(self, *largs):
# Open the popup you want to open and declare callback if user pressed `Yes`
popup = ExitPopup(title=TEXT_ON_CLOSE_APPLICATION,
content=Button(text=TEXT_ON_CLOSE_APPLICATION_BUTTON_CLOSE),
size=(400, 400), size_hint=(None, None)
)
popup.bind(on_confirm=partial(self.close_app, *largs))
popup.open()
def close_app(self, *largs):
super(MainApp, self).stop(*largs)
def bottom_navigation_remove_mobile(self, widget):
# Removes some items from bottom-navigation demo when on mobile
if DEVICE_TYPE == 'mobile':
widget.ids.bottom_navigation_demo.remove_widget(widget.ids.bottom_navigation_desktop_2)
if DEVICE_TYPE == 'mobile' or DEVICE_TYPE == 'tablet':
widget.ids.bottom_navigation_demo.remove_widget(widget.ids.bottom_navigation_desktop_1)
def set_error_message(self, *args):
if len(self.root.ids.text_field_error.text) == 2:
self.root.ids.text_field_error.error = True
else:
self.root.ids.text_field_error.error = False
def on_pause(self):
return True
def on_text_validate_callback(self, instance):
print(instance.text)
def on_start(self):
print("\non_start:")
store = JsonStore('myBackup.json')
if store.count() > 0:
for key in store:
print("\tid={0}, obj={1}".format(key, self.root.ids[key]))
if isinstance(self.root.ids[key], MDTextField):
self.root.ids[key].text = store.get(key)['text']
print("\t\ttext=", self.root.ids[key].text)
def on_stop(self, *args):
print("\non_stop:")
store = JsonStore('myBackup.json')
for key in self.root.ids:
if isinstance(self.root.ids[key], MDTextField):
print("\tid={0}, text={1}".format(key, self.root.ids[key].text))
store.put(key, text=self.root.ids[key].text)
class ExitPopup(Popup):
def __init__(self, **kwargs):
super(ExitPopup, self).__init__(**kwargs)
self.register_event_type('on_confirm')
def on_confirm(self):
pass
def on_button_yes(self):
self.dispatch('on_confirm')
if __name__ == '__main__':
MainApp().run()
main.kv
#:kivy 1.11.0
#:import Toolbar kivymd.toolbar.Toolbar
#:import MDNavigationDrawer kivymd.navigationdrawer.MDNavigationDrawer
#:import NavigationLayout kivymd.navigationdrawer.NavigationLayout
#:import NavigationDrawerDivider kivymd.navigationdrawer.NavigationDrawerDivider
#:import NavigationDrawerToolbar kivymd.navigationdrawer.NavigationDrawerToolbar
#:import NavigationDrawerSubheader kivymd.navigationdrawer.NavigationDrawerSubheader
#:import MDTextField kivymd.textfields.MDTextField
#:import labels labels
NavigationLayout:
id: nav_layout
MDNavigationDrawer:
id: nav_drawer
NavigationDrawerToolbar:
title: labels.NAVIGATION
NavigationDrawerIconButton:
icon: 'checkbox-blank-circle'
text: labels.SYSTEM_PARAMETERS
on_release: app.root.ids.scr_mngr.current = 'system_parameters'
NavigationDrawerIconButton:
icon: "checkbox-blank-circle"
text: labels.CLOSE_APPLICATION
on_release: app.stop()
BoxLayout:
orientation: 'vertical'
halign: "center"
Toolbar:
id: toolbar
title: labels.APPLICATION_NAME
md_bg_color: app.theme_cls.primary_color
background_palette: 'Primary'
background_hue: '500'
left_action_items: [['menu', lambda x: app.root.toggle_nav_drawer()]]
#right_action_items: [['dots-vertical', lambda x: app.root.toggle_nav_drawer()]]
ScreenManager:
id: scr_mngr
Screen:
name: 'system_parameters'
BoxLayout:
orientation: "horizontal"
BoxLayout:
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
padding: dp(48)
spacing: 10
MDTextField:
id: controller_parameter_1
hint_text: labels.CONTROLLER_PARAMETER_1_HINT_TEXT
helper_text: labels.CONTROLLER_PARAMETER_1_HELPER_TEXT
helper_text_mode: "on_focus"
input_filter: "int"
on_text_validate: app.on_text_validate_callback(self) # Fix 1
BoxLayout:
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
padding: dp(48)
spacing: 10
MDTextField:
id: controller_parameter_2
hint_text: labels.CONTROLLER_PARAMETER_2_HINT_TEXT
helper_text: labels.CONTROLLER_PARAMETER_2_HELPER_TEXT
helper_text_mode: "on_focus"
input_filter: "float"
on_text_validate: app.on_text_validate_callback(self) # Fix 2
Screen:
name: 'textfields'
ScrollView:
BoxLayout:
orientation: 'vertical'
MDTextField:
id: text_field_error
hint_text: "Helper text on error (Hit Enter with two characters here)"
helper_text: "Two is my least favorite number"
helper_text_mode: "on_error"
Screen:
name: 'nav_drawer'
HackedDemoNavDrawer:
# NavigationDrawerToolbar:
# title: "Navigation Drawer Widgets"
NavigationDrawerIconButton:
icon: 'checkbox-blank-circle'
text: "Badge text ---->"
badge_text: "99+"
NavigationDrawerIconButton:
active_color_type: 'accent'
text: "Accent active color"
NavigationDrawerIconButton:
active_color_type: 'custom'
text: "Custom active color"
active_color: [1, 0, 1, 1]
NavigationDrawerIconButton:
use_active: False
text: "Use active = False"
NavigationDrawerIconButton:
text: "Different icon"
icon: 'alarm'
NavigationDrawerDivider:
NavigationDrawerSubheader:
text: "NavigationDrawerSubheader"
NavigationDrawerIconButton:
text: "NavigationDrawerDivider \/"
NavigationDrawerDivider:
输出
详情请参考示例
- 使用Kivy Storage
- 使用应用程序事件,
on_start
和on_stop
。 - 添加导入语句,
from kivymd.textfields import MDTextField
on_start:
Fired when the application is being started (before the runTouchApp() call.
on_stop:
Fired when the application stops.
例子
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivymd.theming import ThemeManager
from kivymd.textfields import MDTextField
from kivy.storage.jsonstore import JsonStore
class RootWidget(BoxLayout):
def on_text_validate_callback(self, instance):
print(instance.text)
class TestApp(App):
theme_cls = ThemeManager()
def build(self):
return RootWidget()
def on_start(self):
print("\non_start:")
store = JsonStore('myBackup.json')
if store.count() > 0:
for key in store:
print("\tid={0}, obj={1}".format(key, self.root.ids[key]))
if isinstance(self.root.ids[key], MDTextField):
self.root.ids[key].text = store.get(key)['text']
print("\t\ttext=", self.root.ids[key].text)
def on_stop(self):
print("\non_stop:")
store = JsonStore('myBackup.json')
for key in self.root.ids:
if isinstance(self.root.ids[key], MDTextField):
print("\tid={0}, text={1}".format(key, self.root.ids[key].text))
store.put(key, text=self.root.ids[key].text)
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.11.0
#:import MDTextField kivymd.textfields.MDTextField
<RootWidget>:
MDTextField:
id: first_text_field
color: 0, 0, 0, 1 # black text color
focus: True
hint_text: "Helper text on focus"
helper_text: "This will disappear when you click off"
helper_text_mode: "on_focus"
input_filter: "int"
on_text_validate: root.on_text_validate_callback(self)