如何在Kivy项目中存储Project至列表或字典?项目管理器应用开发求助
Hey Micheno, let's break down how to solve your data storage problem and polish up your code for better maintainability.
1. Data Storage Solution
You need two layers of storage: in-memory storage for active sessions, and persistent storage to save data between app restarts.
In-Memory Storage (Structured with Classes)
Raw lists/dictionaries get messy fast—define simple classes to represent your Project and Task data for cleaner management:
class Task: def __init__(self, name, is_completed=False, notes=""): self.name = name self.is_completed = is_completed self.notes = notes class Project: def __init__(self, name): self.name = name self.tasks = []
Update your screens to use these classes:
- In
PRS, replace the class-levelprojectslist with an instance variable to avoid shared state across screen instances:class PRS(Screen, BoxLayout): def __init__(self, **kwargs): super().__init__(**kwargs) self.projects = [] # Instance-specific project list # ... rest of your __init__ code - When creating a new project, capture the input text and add a
Projectinstance to your list:def new_pr(self, *args): # ... your existing code to create txt1, label1, check1, etc. # Save project when user finishes typing def save_project(instance): project_name = instance.text.strip() if project_name: new_project = Project(project_name) self.projects.append(new_project) print(f"Added project: {project_name}") # Optional: Link this project to its OPEN button button.project = new_project txt1.bind(on_text_validate=save_project) # ... rest of your code for buttons/spinner - Pass the active project to the
TASKSscreen when clicking "OPEN":def open_project(self, instance): self.manager.current = 'task' # Send the project to the tasks screen tasks_screen = self.manager.get_screen('task') tasks_screen.current_project = instance.project
Persistent Storage (Using JsonStore)
You already imported JsonStore—let's use it to save/load data between app sessions:
- Initialize the store in your
MAppclass:class MApp(App): title = "PROJECT MANAGER" def build(self): self.store = JsonStore('project_data.json') sm = ScreenManager() # ... rest of your screen setup return sm - Add methods to save and load projects in
PRS:def save_projects(self): # Convert projects to JSON-serializable data project_data = [] for proj in self.projects: task_data = [ {"name": t.name, "is_completed": t.is_completed, "notes": t.notes} for t in proj.tasks ] project_data.append({"name": proj.name, "tasks": task_data}) App.get_running_app().store.put('saved_projects', data=project_data) def load_projects(self): app = App.get_running_app() if app.store.exists('saved_projects'): saved_data = app.store.get('saved_projects')['data'] for proj_data in saved_data: project = Project(proj_data['name']) # Recreate tasks for task_data in proj_data['tasks']: project.tasks.append(Task(**task_data)) self.projects.append(project) # Optional: Render loaded projects on the screen - Call
save_projects()when the app closes, or when projects are added/deleted. Callload_projects()inPRS's__init__method.
2. Code Improvements
Here are key tweaks to make your code more maintainable:
Move Layouts to KV File
Hardcoding widgets in Python makes the code hard to read. Define static layouts in main.kv instead:
<PRS>: name:"project" BoxLayout: orientation: 'vertical' # Header buttons BoxLayout: size_hint_y: 0.1 Button: text: "NEW PROJECT" on_release: root.new_pr() Button: text: "BACK" on_release: root.changer2() # ScrollView for projects (prevents overflow) ScrollView: BoxLayout: id: project_container orientation: 'vertical' size_hint_y: None height: self.minimum_height
Then add new project widgets to the project_container instead of the main screen.
Fix Class Variable Issues
Variables like widgets1, l1top are class-level—this means all PRS instances share them. Convert them to instance variables in __init__:
class PRS(Screen, BoxLayout): def __init__(self, **kwargs): super().__init__(**kwargs) self.widgets1 = [] self.l1x = .1 self.l1top = .87 self.txt1x = .4 self.txt1top = .88 # ... rest of your variables
Fix Notes Functionality
Your current code adds a new TextInput every time the notes button is clicked. Instead, create a single widget and toggle its visibility:
class TASKS(Screen, BoxLayout): def __init__(self, **kwargs): super().__init__(**kwargs) self.current_project = None # Create a single notes input, hidden by default self.notes_input = TextInput(multiline=True, size_hint=(.4,.5), pos_hint={"x":0.56,"top":.74}, opacity=0) self.add_widget(self.notes_input) # ... rest of your __init__ code def note(self, *args): self.notes_input.opacity = 1 # Load existing notes if available if self.current_project: self.notes_input.text = self.current_project.notes def change(self, *args): self.notes_input.opacity = 0 # Save notes back to the project if self.current_project: self.current_project.notes = self.notes_input.text
Remove Duplicate Imports
Clean up your imports to include each module only once:
from kivy.app import App import sys from kivy.storage.jsonstore import JsonStore from kivy.uix.screenmanager import ScreenManager, Screen from kivy.uix.boxlayout import BoxLayout from kivy.uix.togglebutton import ToggleButton from kivy.uix.checkbox import CheckBox from kivy.uix.label import Label from kivy.uix.textinput import TextInput from kivy.uix.button import Button from kivy.lang import Builder from kivy.core.window import Window from kivy.uix.spinner import Spinner from kivy.uix.scrollview import ScrollView
Implement Theme Toggle
Add logic to your theme buttons in OPS:
class OPS(Screen, BoxLayout): def __init__(self, **kwargs): super().__init__(**kwargs) label = Label(text="COLOR THEME:", size_hint=(.2,.1), pos_hint={"x":.25,"top":.9}) self.add_widget(label) self.dark_btn = ToggleButton(text="DARK", size_hint=(.2, .1), pos_hint={"x":.5,"top":.9}, background_normal="", background_color=(0,0,0,0)) self.light_btn = ToggleButton(text="LIGHT", size_hint=(.2, .1), pos_hint={"x": .7, "top": .9}, background_normal="", background_color=(1,1,1,1)) self.dark_btn.bind(on_press=self.set_dark_theme) self.light_btn.bind(on_press=self.set_light_theme) self.add_widget(self.dark_btn) self.add_widget(self.light_btn) def set_dark_theme(self, instance): Window.clearcolor = (0,0,0,1) # Update widget colors (e.g., set button text to white) for child in self.parent.parent.children: if isinstance(child, Button): child.color = (1,1,1,1) def set_light_theme(self, instance): Window.clearcolor = (1,1,1,1) # Update widget colors (e.g., set button text to black) for child in self.parent.parent.children: if isinstance(child, Button): child.color = (0,0,0,1)
Final Tips
- Add validation to prevent empty project/task names.
- Use
ScrollViewin bothPRSandTASKSto handle large numbers of items. - Call
save_projects()when the app closes (overrideon_stopinMApp).
内容的提问来源于stack exchange,提问作者Micheno

