A frame for MVC

本文通过一个具体实例探讨了MVC(模型-视图-控制器)架构的设计与实现,并对比了MVC与MVP(模型-视图- presenter)的区别。文中详细介绍了如何构建一个简易的MVC框架,包括模型(Model)、视图(View)和控制器(Controller)各部分的具体实现。

    Last time, I tried to make a speech about how to design a MVC frame by oneself in our software club. However, Neo pointed out the
difference between MVC and MVP: in MVC, C will never know about the V and it will never modify the V.
Though I don't think we should care much about whether webwork&struts are MVC or MVP frames,but at east ,MVC isfunny. Is it right?
    And also, I have made my frame be more likely a MVC frame.
    We will need two jsp files, a java class as model, and a java class as control.

    Let's come to model first:
    The model is a simple javabean,in fact the base class Model have a field for view. But customer using it don't have to know that,
    they just need to define the fields which will be the data showing in the view.

     //configurate the interceptors
@InterceptorsRef(interceptors = {ExceptionInterceptor.class ,ModelInterceptor.class})
//point out control for the model;
@ControlRef(clazz =UserControl.class)
public class UserModel extends Model
{
    private User user = new User();//A javaBean User
    private String people = "";//people

    public String getPeople()
    {
        return people;
    }

    public void setPeople(String people)
    {
        this.people = people;
    }

 

    public User getUser()
    {
        return user;
    }

    public void setUser(User user)
    {
        this.user = user;
    }
}


    next is  the control class:


    public class UserControl extends Control
{
    private UserModel model;
    private Logger log = Logger.getLogger("debugLogger");

    public void setModel(Model model)  //you should set the data model into the control;
    {
        this.model = (UserModel) model;
    }


    public void process()
    {
        User user = model.getUser(); //get the User from the model
        Integer age = user.getUserAge();
        if (age != null)
        {
            age += 100;
            user.setUserAge(age);
        }
        user.setUserName(user.getUserName() + "+ processed");
        user.setUserTitle(user.getUserTitle() + "+ processed");//change the value of User
        model.setPeople(model.getPeople() + " processed");//change the value of people
        model.setResult(Result.jsp("result.jsp"));//change the result of the model
    }
}

    and then we will come to the view:
    
     the input view:

 <!--"UserModel" means the class name of the model, and "process" means which method  of the control will be invoked  -->
     <form action="UserModel-process.do" method="post">
    <table width="30%">
        <tr>
            <td>user name:<input type="text" name="user.userName"></td>
        </tr>
        <tr>
            <td>user age:<input type="text" name="user.userAge"></td>
        </tr>
        <tr>
            <td>user title:<input type="text" name="user.userTitle"></td>
        </tr>
        <tr>
            <td>people:<input type="text" name="people"></td>
        </tr>

        <tr>
            <td>
                <input type="submit" value="process">

            </td>
        </tr>
    </table>
</form>

      and then it will come to the result view:

        <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  <%@ taglib uri="/WEB-INF/simple-property.tld" prefix="simple" %>
  <html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
        <title>Simple jsp page</title></head>
      <table>
          <tr>
              <td>user name:<br><font color="green"><simple:property  property="user.userName"/></font></td>
          </tr>
          <tr>
              <td>user age:<br><font color="green"><simple:property  property="user.userAge"/></font></td>
          </tr>
          <tr>
              <td>user title:<br><font color="green"><simple:property  property="user.userTitle"/></font></td>
          </tr>
          <tr>
              <td>people:<br><font color="green"><simple:property  property="people"/></font></td>
          </tr>
      </table>
</html>


    I don't think this kind of frame is a real MVC frame: the control will never modify the data of the view. and the view will never know the
control.
    And at least, we can easily tell that which's the model,which's the view,and which's the control.
    After I developed the frame, and tried to use it for sereval times. I found that, in fact, about struts:the model is the ActionForm,the control
is the Action, and the jsp files is the View. The difference is that, the model was put in the control,and the View directly specifies it's control.
Its design make it more easily to be used,but less clearly to be understand.

 

 


   
   

import tkinter as tk from tkinter import ttk, messagebox import json import os from datetime import datetime class PersonnelManagementSystem: def __init__(self, root): self.root = root self.root.title("人员信息管理系统") self.root.geometry("900x600") self.root.minsize(800, 500) # 设置中文字体 self.style = ttk.Style() self.style.configure(".", font=("SimHei", 10)) # 数据文件和存储 self.data_file = "personnel_data.json" self.personnel_data = self._load_data() # 创建UI self._create_ui() self._refresh_treeview() def _load_data(self): """加载或初始化数据""" try: if os.path.exists(self.data_file): with open(self.data_file, "r", encoding="utf-8") as f: return json.load(f) else: initial_data = self._create_initial_data() self._save_data(initial_data) return initial_data except Exception as e: messagebox.showerror("错误", f"加载数据失败: {e}") return [] def _save_data(self, data=None): """保存数据到文件""" try: with open(self.data_file, "w", encoding="utf-8") as f: json.dump(data or self.personnel_data, f, ensure_ascii=False, indent=2) except Exception as e: messagebox.showerror("错误", f"保存数据失败: {e}") def _create_initial_data(self): """创建初始示例数据""" now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") return [ {"id": 1, "name": "张三", "gender": "男", "age": 30, "phone": "13800138000", "email": "zhangsan@example.com", "address": "北京市朝阳区", "create_time": now, "update_time": now}, {"id": 2, "name": "李四", "gender": "女", "age": 25, "phone": "13900139000", "email": "lisi@example.com", "address": "上海市浦东新区", "create_time": now, "update_time": now}, {"id": 3, "name": "王五", "gender": "男", "age": 35, "phone": "13700137000", "email": "wangwu@example.com", "address": "广州市天河区", "create_time": now, "update_time": now} ] def _create_ui(self): """创建用户界面""" # 主框架 main_frame = ttk.Frame(self.root, padding=10) main_frame.pack(fill=tk.BOTH, expand=True) # 搜索区域 search_frame = ttk.Frame(main_frame) search_frame.pack(fill=tk.X, pady=5) ttk.Label(search_frame, text="搜索:").pack(side=tk.LEFT, padx=5) self.search_var = tk.StringVar() ttk.Entry(search_frame, textvariable=self.search_var, width=30).pack(side=tk.LEFT, padx=5) ttk.Button(search_frame, text="搜索", command=self._search_data).pack(side=tk.LEFT, padx=5) ttk.Button(search_frame, text="重置", command=self._refresh_treeview).pack(side=tk.LEFT) # 表格区域 tree_frame = ttk.Frame(main_frame) tree_frame.pack(fill=tk.BOTH, expand=True, pady=5) columns = ("id", "name", "gender", "age", "phone", "email", "address", "create_time", "update_time") self.tree = ttk.Treeview(tree_frame, columns=columns, show="headings", selectmode="browse") # 设置列标题和宽度 for col in columns: self.tree.heading(col, text=col.capitalize()) width = 100 if col != "id" else 50 self.tree.column(col, width=width, anchor=tk.CENTER) # 添加滚动条 scrollbar = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL, command=self.tree.yview) self.tree.configure(yscroll=scrollbar.set) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.tree.pack(fill=tk.BOTH, expand=True) # 按钮区域 button_frame = ttk.Frame(main_frame) button_frame.pack(fill=tk.X, pady=5) for text, command in [("添加", self._add_person), ("修改", self._edit_person), ("删除", self._delete_person), ("刷新", self._refresh_treeview)]: ttk.Button(button_frame, text=text, command=command).pack(side=tk.LEFT, padx=5) def _refresh_treeview(self, data=None): """刷新表格数据""" for item in self.tree.get_children(): self.tree.delete(item) for person in (data or self.personnel_data): self.tree.insert("", tk.END, values=[ person["id"], person["name"], person["gender"], person["age"], person["phone"], person["email"], person["address"], person["create_time"], person["update_time"] ]) def _search_data(self): """搜索数据""" keyword = self.search_var.get().strip().lower() if not keyword: self._refresh_treeview() return results = [p for p in self.personnel_data if any(keyword in str(val).lower() for val in p.values())] self._refresh_treeview(results) d的知识点
06-25
<think>我们正在开发一个基于Pythontkinter和ttk模块的人员信息管理系统。这涉及到GUI编程、数据管理和事件处理等知识点。下面将分步骤讲解关键知识和技术实现要点。###1.tkinter和ttk基础tkinter是Python的标准GUI库,提供基础控件(如按钮、标签、输入框等)。ttk是tkinter的扩展模块,引入主题化控件(具有现代外观和风格)。**基本窗口创建:**```pythonimporttkinterastkfromtkinterimportttk#创建主窗口root=tk.Tk()root.title("人员信息管理系统")root.geometry("800x600")#设置窗口大小#添加ttk标签label=ttk.Label(root,text="欢迎使用人员信息管理系统")label.pack(pady=20)#添加垂直间距root.mainloop()#进入主事件循环```###2.布局管理tkinter提供三种布局管理器:`pack()`、`grid()`和`place()`。推荐使用`grid()`进行表格状布局。**示例表格布局:**```python#使用grid布局frame=ttk.Frame(root,padding="10")frame.pack(fill=tk.BOTH,expand=True)#第一行:标签和输入框ttk.Label(frame,text="姓名:").grid(column=0,row=0,sticky=tk.W,padx=5,pady=5)name_entry=ttk.Entry(frame,width=30)name_entry.grid(column=1,row=0,padx=5,pady=5)#第二行:按钮add_button=ttk.Button(frame,text="添加记录")add_button.grid(column=0,row=1,columnspan=2,pady=10)```###3.数据存储与处理人员信息通常需要持久化存储。可使用SQLite数据库(轻量级,无需单独服务器)或文件(如CSV、JSON)。**SQLite数据库集成示例:**```pythonimportsqlite3#连接数据库(如果不存在则创建)conn=sqlite3.connect('personnel.db')cursor=conn.cursor()#创建人员信息表cursor.execute('''CREATETABLEIFNOTEXISTSemployees(idINTEGERPRIMARYKEYAUTOINCREMENT,nameTEXTNOTNULL,ageINTEGER,departmentTEXT,positionTEXT)''')conn.commit()```###4.实现CRUD功能通过GUI界面实现增删改查操作:-**添加记录**:从输入框获取数据,插入数据库-**查询记录**:显示在Treeview(表格控件)中-**更新/删除**:选择记录后操作**Treeview控件示例:**```python#创建Treeview(表格)columns=("id","name","age","department","position")tree=ttk.Treeview(frame,columns=columns,show="headings")#设置列标题forcolincolumns:tree.heading(col,text=col.title())tree.grid(column=0,row=2,columnspan=2,padx=5,pady=5)#从数据库加载数据defload_data():tree.delete(*tree.get_children())#清空现有数据cursor.execute("SELECT*FROMemployees")forrowincursor.fetchall():tree.insert("",tk.END,values=row)#调用加载函数load_data()```###5.事件绑定为按钮等控件绑定事件处理函数:```pythondefadd_employee():#获取输入框内容name=name_entry.get()age=age_entry.get()department=dept_entry.get()position=pos_entry.get()#插入数据库cursor.execute("INSERTINTOemployees(name,age,department,position)VALUES(?,?,?,?)",(name,age,department,position))conn.commit()load_data()#刷新表格显示clear_entries()#清空输入框#绑定按钮事件add_button.config(command=add_employee)```###6.界面美化与主题ttk支持主题切换(如'clam','alt','default'等):```python#查看可用主题print(ttk.Style().theme_names())#设置主题ttk.Style().theme_use('clam')```###7.完整系统架构建议1.**模型(Model)**:数据库操作类(如`PersonnelDB`)2.**视图(View)**:GUI界面类(如`AppGUI`)3.**控制器(Controller)**:事件处理函数(连接模型和视图)---**总结**:开发人员信息管理系统需掌握:-tkinter/ttk控件使用与布局-数据持久化(SQLite/文件)-MVC架构思想-事件驱动编程§§相关问题§§1.如何优化tkinter应用的性能?2.如何处理tkinter中的多线程问题?3.如何将tkinter应用打包成可执行文件?4.ttk控件与传统tkinter控件的主要区别是什么?</think>### Python tkinter ttk 人员信息管理系统实现及相关知识点 #### 一、核心知识点概述 1. **GUI框架基础** tkinter是Python标准GUI库,提供基础控件(按钮、输入框等) ttk(Themed Tk)是tkinter的增强模块,提供现代风格控件和主题支持 2. **系统架构组成** ```mermaid graph LR A[数据层] --> B[业务逻辑层] B --> C[表示层] C --> D[用户界面] ``` 3. **关键技术点** - 数据存储:SQLite/CSV/JSON - 界面布局:`grid`/`pack`管理器 - 事件处理:命令绑定与回调函数 - 主题控制:ttk.Style对象 #### 二、核心模块实现 ##### 1. 主界面框架 ```python import tkinter as tk from tkinter import ttk, messagebox class PersonnelSystem: def __init__(self, root): self.root = root self.root.title("人员信息管理系统 v1.0") self.root.geometry("900x600") # 创建主题样式 self.style = ttk.Style() self.style.theme_use("clam") # 可选: alt, default, classic self.create_widgets() self.load_data() def create_widgets(self): # 顶部工具栏 tool_frame = ttk.Frame(self.root) tool_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Button(tool_frame, text="添加", command=self.add_record).pack(side=tk.LEFT, padx=2) ttk.Button(tool_frame, text="编辑", command=self.edit_record).pack(side=tk.LEFT, padx=2) ttk.Button(tool_frame, text="删除", command=self.delete_record).pack(side=tk.LEFT, padx=2) ttk.Button(tool_frame, text="导出", command=self.export_data).pack(side=tk.RIGHT, padx=2) # 搜索区域 search_frame = ttk.LabelFrame(self.root, text="搜索条件") search_frame.pack(fill=tk.X, padx=5, pady=5) ttk.Label(search_frame, text="姓名:").grid(row=0, column=0, padx=5) self.search_entry = ttk.Entry(search_frame, width=20) self.search_entry.grid(row=0, column=1, padx=5) ttk.Button(search_frame, text="搜索", command=self.search_data).grid(row=0, column=2, padx=5) # 数据表格 self.tree = ttk.Treeview(self.root, columns=("ID", "姓名", "部门", "职位", "电话"), show="headings") self.tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 配置列 columns = [("ID", 80), ("姓名", 120), ("部门", 150), ("职位", 150), ("电话", 120)] for col, width in columns: self.tree.heading(col, text=col) self.tree.column(col, width=width, anchor=tk.CENTER) ``` ##### 2. 数据管理(SQLite示例) ```python def load_data(self): # 连接数据库 import sqlite3 conn = sqlite3.connect('personnel.db') cursor = conn.cursor() # 创建表(如果不存在) cursor.execute('''CREATE TABLE IF NOT EXISTS employees ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, department TEXT, position TEXT, phone TEXT)''') # 加载数据到表格 cursor.execute("SELECT * FROM employees") for row in cursor.fetchall(): self.tree.insert("", tk.END, values=row) conn.close() ``` ##### 3. 增删改查功能 ```python def add_record(self): # 创建弹出窗口 self.add_window = tk.Toplevel(self.root) self.add_window.title("添加新记录") # 创建输入表单 fields = ["姓名*", "部门", "职位", "电话"] self.entries = {} for i, field in enumerate(fields): ttk.Label(self.add_window, text=field).grid(row=i, column=0, padx=5, pady=5, sticky=tk.W) entry = ttk.Entry(self.add_window, width=25) entry.grid(row=i, column=1, padx=5, pady=5) self.entries[field] = entry # 确认按钮 ttk.Button(self.add_window, text="保存", command=self.save_record).grid(row=len(fields), columnspan=2, pady=10) def save_record(self): # 获取输入值并验证 name = self.entries["姓名*"].get().strip() if not name: messagebox.showerror("错误", "姓名不能为空") return # 保存到数据库 try: conn = sqlite3.connect('personnel.db') cursor = conn.cursor() cursor.execute('''INSERT INTO employees (name, department, position, phone) VALUES (?, ?, ?, ?)''', (name, self.entries["部门"].get(), self.entries["职位"].get(), self.entries["电话"].get())) conn.commit() conn.close() # 刷新表格 self.tree.insert("", tk.END, values=( cursor.lastrowid, name, self.entries["部门"].get(), self.entries["职位"].get(), self.entries["电话"].get() )) self.add_window.destroy() except Exception as e: messagebox.showerror("数据库错误", str(e)) ``` #### 三、进阶技术要点 1. **主题定制化** ```python # 自定义按钮样式 self.style.configure('Custom.TButton', foreground='blue', font=('Arial', 10, 'bold'), padding=6) # 应用样式 ttk.Button(frame, text="搜索", style='Custom.TButton') ``` 2. **数据验证技巧** ```python # 电话格式验证 def validate_phone(input): if input == "": return True return re.match(r"^1[3-9]\d{9}$", input) is not None vcmd = (self.add_window.register(validate_phone), '%P') phone_entry = ttk.Entry(self.add_window, validate="key", validatecommand=vcmd) ``` 3. **多线程处理** 使用`threading`模块防止数据库操作阻塞GUI: ```python import threading def save_record_threaded(): threading.Thread(target=self.save_record, daemon=True).start() ``` #### 四、最佳实践建议 1. **代码组织** - 采用MVC模式分离数据/视图/控制逻辑 - 使用面向对象封装组件 2. **错误处理** - 数据库操作使用try-except块 - 添加输入数据验证 - 使用`messagebox`显示友好错误信息 3. **用户体验优化** - 添加状态栏显示操作反馈 - 实现数据分页加载 - 支持快捷键操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值